@productmaker/mcp 12.2.6 → 12.2.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +75 -14
- package/dist/http.js +1426 -1107
- package/dist/stdio.js +1429 -1112
- package/dist/ui/meta-upload-app.html +221 -80
- package/package.json +1 -1
package/dist/http.js
CHANGED
|
@@ -3021,7 +3021,7 @@ var LlmPricingSnapshotSchema = z3.discriminatedUnion("source", [
|
|
|
3021
3021
|
});
|
|
3022
3022
|
function nativeOutputTokenCeiling(target) {
|
|
3023
3023
|
if (target.nativeOwnedOutputPolicy) return target.nativeOwnedOutputPolicy.verifiedMaxOutputTokens;
|
|
3024
|
-
return target.transport === "google" || target.id === "gemini-3.1-pro-openrouter" ? 65536 : 1e5;
|
|
3024
|
+
return target.transport === "google" || target.id === "gemini-flash-runware" || target.id === "gemini-3.1-pro-openrouter" ? 65536 : 1e5;
|
|
3025
3025
|
}
|
|
3026
3026
|
__name(nativeOutputTokenCeiling, "nativeOutputTokenCeiling");
|
|
3027
3027
|
var LlmRouteTargetSchema = z3.object({
|
|
@@ -3837,7 +3837,19 @@ var identity = {
|
|
|
3837
3837
|
requestDigest: digest,
|
|
3838
3838
|
nativeMessagesBudget: NativeMessagesBudgetSchema.optional()
|
|
3839
3839
|
};
|
|
3840
|
-
var ModelRouteAttemptRequestSchema = z4.object(
|
|
3840
|
+
var ModelRouteAttemptRequestSchema = z4.object({
|
|
3841
|
+
...identity,
|
|
3842
|
+
/** Trusted proxy evidence of a pure reasoning cap. Recovery only: this never
|
|
3843
|
+
* grants a changed body permission to reuse an older paid execution. Absent
|
|
3844
|
+
* on historical entries; retained on new entries to identify normalized work.
|
|
3845
|
+
*/
|
|
3846
|
+
reasoningNormalization: z4.object({
|
|
3847
|
+
version: z4.literal(1),
|
|
3848
|
+
originalCallId: id,
|
|
3849
|
+
originalRequestDigest: digest,
|
|
3850
|
+
originalNativeMessagesBudget: NativeMessagesBudgetSchema.optional()
|
|
3851
|
+
}).strict().optional()
|
|
3852
|
+
}).strict();
|
|
3841
3853
|
var ModelRouteCostSchema = z4.discriminatedUnion("source", [
|
|
3842
3854
|
z4.object({
|
|
3843
3855
|
source: z4.literal("unknown")
|
|
@@ -8276,6 +8288,12 @@ var CodeLandingAuthorEnvelopeSchema = z23.object({
|
|
|
8276
8288
|
*/
|
|
8277
8289
|
variantFacts: z23.array(LandingVariantFactSchema).max(16).optional()
|
|
8278
8290
|
});
|
|
8291
|
+
var CODE_LANDING_DEFERRED_AUTHOR_PROVIDER_RESULT_KIND = "code_landing_deferred_author_provider_v1";
|
|
8292
|
+
var CodeLandingDeferredAuthorProviderResultSchema = z23.object({
|
|
8293
|
+
kind: z23.literal(CODE_LANDING_DEFERRED_AUTHOR_PROVIDER_RESULT_KIND),
|
|
8294
|
+
stepKey: z23.string().min(1),
|
|
8295
|
+
providerResult: z23.record(z23.string(), z23.unknown())
|
|
8296
|
+
});
|
|
8279
8297
|
var CodeLandingCommerceChangeSchema = z23.object({
|
|
8280
8298
|
price: z23.string().trim().min(1).max(40).optional(),
|
|
8281
8299
|
compareAtPrice: z23.string().trim().min(1).max(40).nullable().optional(),
|
|
@@ -10806,6 +10824,8 @@ var role = z38.object({
|
|
|
10806
10824
|
provider: id2,
|
|
10807
10825
|
model: id2,
|
|
10808
10826
|
bindingId: id2,
|
|
10827
|
+
/** Immutable shared-router chain for fresh recipes; absent preserves legacy custody. */
|
|
10828
|
+
routeSnapshot: LlmRouteSnapshotSchema.optional(),
|
|
10809
10829
|
/** null explicitly omits effort on OpenRouter; undefined is a historical recipe. */
|
|
10810
10830
|
reasoning: CreativeEngineRoleReasoningSchema.nullable().optional(),
|
|
10811
10831
|
/** Server-owned output ceiling; omitted historical recipes keep their old stage cap. */
|
|
@@ -10825,7 +10845,13 @@ var CreativeEngineRecipeSchema = z38.object({
|
|
|
10825
10845
|
roles: z38.object({
|
|
10826
10846
|
director: role,
|
|
10827
10847
|
reviewer: role,
|
|
10828
|
-
executor: role.nullable()
|
|
10848
|
+
executor: role.nullable(),
|
|
10849
|
+
/**
|
|
10850
|
+
* Independent reviewer of the assembled final video only (owner
|
|
10851
|
+
* decision 2026-10-26). Absent on historical recipes: their final
|
|
10852
|
+
* review keeps the frozen `reviewer`, unchanged hash and identity.
|
|
10853
|
+
*/
|
|
10854
|
+
finalReviewer: role.optional()
|
|
10829
10855
|
}).strict(),
|
|
10830
10856
|
capabilities: z38.object({
|
|
10831
10857
|
videoModels: z38.array(id2).min(1),
|
|
@@ -10844,9 +10870,14 @@ var CreativeEngineRecipeSchema = z38.object({
|
|
|
10844
10870
|
/** New recipes reserve an explicit native failover; historical calls stay single-route. */
|
|
10845
10871
|
modelFallbackPolicy: z38.literal("anthropic_conclusive_v1").optional(),
|
|
10846
10872
|
/** New reviewer/executor calls use the shared same-model native provider router. */
|
|
10847
|
-
centralModelRouting: z38.
|
|
10873
|
+
centralModelRouting: z38.enum([
|
|
10874
|
+
"native_owned_same_model_v1",
|
|
10875
|
+
"shared_role_chain_v1"
|
|
10876
|
+
]).optional(),
|
|
10848
10877
|
/** Skip only a redundant global review of a deterministically frozen scene repair. */
|
|
10849
10878
|
repairPlanReviewPolicy: z38.literal("frozen_contract_v1").optional(),
|
|
10879
|
+
/** Root-only visual patch wire contract; absent preserves historic full-plan recovery. */
|
|
10880
|
+
repairAuthoring: z38.literal("scene_patch_v1").optional(),
|
|
10850
10881
|
/** New requests maximize output inside model and measured input headroom. */
|
|
10851
10882
|
outputTokenPolicy: z38.literal("model_max_v1").optional(),
|
|
10852
10883
|
/**
|
|
@@ -11307,6 +11338,7 @@ var CreativeEngineCandidateSchema = z38.object({
|
|
|
11307
11338
|
qualityScore: z38.number().min(0).max(100)
|
|
11308
11339
|
}).strict();
|
|
11309
11340
|
var CreativeEngineCostBasisSchema = z38.enum([
|
|
11341
|
+
"reused",
|
|
11310
11342
|
"provider_reported",
|
|
11311
11343
|
"provider_credits_conversion",
|
|
11312
11344
|
"catalogue_estimate"
|
|
@@ -11607,6 +11639,15 @@ var CreativeEngineEventSchema = z38.discriminatedUnion("type", [
|
|
|
11607
11639
|
}).strict(),
|
|
11608
11640
|
plan: CreativeEnginePlanSchema
|
|
11609
11641
|
}).strict(),
|
|
11642
|
+
z38.object({
|
|
11643
|
+
type: z38.literal("accepted_author_output_revalidated"),
|
|
11644
|
+
operationId: id2,
|
|
11645
|
+
acceptedId: id2,
|
|
11646
|
+
context: CreativeEngineContextBindingSchema,
|
|
11647
|
+
outputSha256: hash,
|
|
11648
|
+
validationRevision: z38.string().regex(/^[a-z][a-z0-9_]{0,63}$/),
|
|
11649
|
+
plan: CreativeEnginePlanSchema
|
|
11650
|
+
}).strict(),
|
|
11610
11651
|
z38.object({
|
|
11611
11652
|
type: z38.literal("plan_recorded"),
|
|
11612
11653
|
plan: CreativeEnginePlanSchema
|
|
@@ -13098,7 +13139,9 @@ var SERVICE_MODES = [
|
|
|
13098
13139
|
];
|
|
13099
13140
|
var ServiceModeSchema = z46.enum(SERVICE_MODES);
|
|
13100
13141
|
var AppFeaturesSchema = z46.object({
|
|
13101
|
-
serviceMotionGraphics: z46.boolean()
|
|
13142
|
+
serviceMotionGraphics: z46.boolean(),
|
|
13143
|
+
/** GIF authoring controls; absent on older APIs means hidden. */
|
|
13144
|
+
landingGif: z46.boolean().default(false)
|
|
13102
13145
|
});
|
|
13103
13146
|
var MOTION_GRAPHICS_FORMATS = [
|
|
13104
13147
|
"vertical",
|
|
@@ -20905,7 +20948,7 @@ var CAPABILITY_MODEL_GUIDANCE = {
|
|
|
20905
20948
|
]
|
|
20906
20949
|
},
|
|
20907
20950
|
generate_free_video: {
|
|
20908
|
-
summary: "
|
|
20951
|
+
summary: "Creates custom-script video using paid credits; legacy 'free' means free-form.",
|
|
20909
20952
|
useWhen: [
|
|
20910
20953
|
"genera un video libre creativo",
|
|
20911
20954
|
"la persona pide un video libre con un prompt o guion espec\xEDfico"
|
|
@@ -30375,11 +30418,11 @@ var OAuthOboCredential = class {
|
|
|
30375
30418
|
// src/guidance/productmaker-guidance.ts
|
|
30376
30419
|
var START_DROPSHIPPING_PROMPT_NAME = "start_dropshipping";
|
|
30377
30420
|
var PRODUCTMAKER_SERVER_INSTRUCTIONS = [
|
|
30378
|
-
"
|
|
30421
|
+
"ProductMaker accepts new image or video inputs through request_media_upload, which opens an inline file picker or accepts supported host file references. It creates a private staging session and does not generate media, publish, or consume credits. Existing ProductMaker assetId values can be reused without another upload. A user's explicit refusal to upload, share, or store media takes precedence.",
|
|
30379
30422
|
"For requests that do not need a new media upload, understand the user's intent before choosing a ProductMaker tool.",
|
|
30380
30423
|
"For every concrete ProductMaker request to calculate how much to charge, profitability, or margin for a dropshipping product, you must call cost_product after collecting its missing country and real supplier cost. Never calculate or reproduce ProductMaker's costing formula yourself; answer from the tool result.",
|
|
30381
|
-
"
|
|
30382
|
-
"
|
|
30424
|
+
"Remote file inputs use supported host file references or the inline picker. Local filesystem paths are available only to local stdio clients. The secure upload page is a fallback when the inline picker is unavailable.",
|
|
30425
|
+
"check_media_upload reads an existing session after the user reports completion. An uploaded result supplies owner-scoped assetId and mediaKind values for compatible ProductMaker operations; a pending result indicates that upload is unfinished. The upload App does not send chat messages. Session tokens and private handoff details are not user-facing content.",
|
|
30383
30426
|
"Keep focused requests direct: creatives, landing pages, Shopify publication, Meta publication, and status checks do not require a DropshippingProject. Never search for, reuse, infer, or mention a DropshippingProject unless the user explicitly asks to continue the guided journey.",
|
|
30384
30427
|
"Use the guided journey only for an explicit end-to-end launch.",
|
|
30385
30428
|
"In guided mode, create a project only after a product is chosen, then call guide_dropshipping_launch after every mutation and follow its nextAction.",
|
|
@@ -30861,197 +30904,271 @@ var ApiClient = class {
|
|
|
30861
30904
|
};
|
|
30862
30905
|
|
|
30863
30906
|
// src/index.ts
|
|
30864
|
-
var MCP_VERSION = "12.2.
|
|
30907
|
+
var MCP_VERSION = "12.2.8";
|
|
30865
30908
|
|
|
30866
30909
|
// src/meta-upload-app.ts
|
|
30867
30910
|
import { readFileSync } from "fs";
|
|
30911
|
+
import { z as z132 } from "zod";
|
|
30868
30912
|
import { registerAppResource, RESOURCE_MIME_TYPE } from "@modelcontextprotocol/ext-apps/server";
|
|
30869
|
-
|
|
30870
|
-
|
|
30871
|
-
|
|
30872
|
-
|
|
30873
|
-
|
|
30874
|
-
|
|
30875
|
-
|
|
30876
|
-
|
|
30877
|
-
|
|
30878
|
-
|
|
30879
|
-
|
|
30880
|
-
|
|
30881
|
-
|
|
30882
|
-
|
|
30883
|
-
|
|
30884
|
-
|
|
30885
|
-
|
|
30886
|
-
|
|
30887
|
-
|
|
30888
|
-
|
|
30889
|
-
|
|
30890
|
-
|
|
30891
|
-
|
|
30892
|
-
|
|
30893
|
-
|
|
30913
|
+
|
|
30914
|
+
// src/public-https.ts
|
|
30915
|
+
import { lookup } from "dns/promises";
|
|
30916
|
+
import https from "https";
|
|
30917
|
+
import { isIP } from "net";
|
|
30918
|
+
import { createGunzip, createInflate, createBrotliDecompress } from "zlib";
|
|
30919
|
+
|
|
30920
|
+
// ../../packages/utils/src/ip-classification.ts
|
|
30921
|
+
import net from "net";
|
|
30922
|
+
function parseIPv4(ip) {
|
|
30923
|
+
const parts = ip.split(".");
|
|
30924
|
+
if (parts.length !== 4) return null;
|
|
30925
|
+
const nums = [
|
|
30926
|
+
Number(parts[0]),
|
|
30927
|
+
Number(parts[1]),
|
|
30928
|
+
Number(parts[2]),
|
|
30929
|
+
Number(parts[3])
|
|
30930
|
+
];
|
|
30931
|
+
if (nums.some((n) => !Number.isInteger(n) || n < 0 || n > 255)) return null;
|
|
30932
|
+
return nums;
|
|
30933
|
+
}
|
|
30934
|
+
__name(parseIPv4, "parseIPv4");
|
|
30935
|
+
function classifyPrivateIPv4(ip, includeAdditionalRanges) {
|
|
30936
|
+
const p = parseIPv4(ip);
|
|
30937
|
+
if (!p) return false;
|
|
30938
|
+
const [a, b] = p;
|
|
30939
|
+
return a === 0 || a === 10 || a === 127 || a === 169 && b === 254 || a === 172 && b >= 16 && b <= 31 || a === 192 && b === 168 || a >= 240 || includeAdditionalRanges && (a === 100 && b >= 64 && b <= 127 || a === 198 && (b === 18 || b === 19) || a >= 224);
|
|
30940
|
+
}
|
|
30941
|
+
__name(classifyPrivateIPv4, "classifyPrivateIPv4");
|
|
30942
|
+
function isPrivateIPv4(ip) {
|
|
30943
|
+
return classifyPrivateIPv4(ip, true);
|
|
30944
|
+
}
|
|
30945
|
+
__name(isPrivateIPv4, "isPrivateIPv4");
|
|
30946
|
+
function is6to4PrivateIPv4(ipv6) {
|
|
30947
|
+
const groups = ipv6.split(":");
|
|
30948
|
+
if (groups.length < 3) return false;
|
|
30949
|
+
return isPrivateIPv4Words(groups[1] || "0", groups[2] || "0");
|
|
30950
|
+
}
|
|
30951
|
+
__name(is6to4PrivateIPv4, "is6to4PrivateIPv4");
|
|
30952
|
+
function isPrivateIPv4Words(high, low) {
|
|
30953
|
+
const hi = parseInt(high, 16);
|
|
30954
|
+
const lo = parseInt(low, 16);
|
|
30955
|
+
if (Number.isNaN(hi) || Number.isNaN(lo)) return false;
|
|
30956
|
+
const a = hi >> 8 & 255;
|
|
30957
|
+
const b = hi & 255;
|
|
30958
|
+
const c = lo >> 8 & 255;
|
|
30959
|
+
const d = lo & 255;
|
|
30960
|
+
return isPrivateIPv4(`${a}.${b}.${c}.${d}`);
|
|
30961
|
+
}
|
|
30962
|
+
__name(isPrivateIPv4Words, "isPrivateIPv4Words");
|
|
30963
|
+
function isPrivateIPv6(ip) {
|
|
30964
|
+
let normalized;
|
|
30894
30965
|
try {
|
|
30895
|
-
|
|
30896
|
-
} catch
|
|
30897
|
-
|
|
30898
|
-
throw new Error(`Missing packaged media upload app HTML at ${htmlUrl.pathname}. Rebuild @productmaker/mcp so ${MEDIA_UPLOAD_APP_RESOURCE_URI} is bundled. Original error: ${message}`);
|
|
30966
|
+
normalized = new URL(`http://[${ip.split("%", 1)[0]}]/`).hostname.slice(1, -1).toLowerCase();
|
|
30967
|
+
} catch {
|
|
30968
|
+
return false;
|
|
30899
30969
|
}
|
|
30970
|
+
if (normalized === "::" || /^::[0-9a-f]+$/.test(normalized)) return true;
|
|
30971
|
+
if (normalized.startsWith("64:ff9b:")) return true;
|
|
30972
|
+
if (/^fe[89a-f][0-9a-f]:/.test(normalized)) return true;
|
|
30973
|
+
if (/^f[cd][0-9a-f]{2}:/.test(normalized)) return true;
|
|
30974
|
+
if (normalized.startsWith("ff")) return true;
|
|
30975
|
+
if (/^2001:0*:/.test(normalized)) return true;
|
|
30976
|
+
if (normalized.startsWith("2001:db8:")) return true;
|
|
30977
|
+
if (normalized.startsWith("2002:") && is6to4PrivateIPv4(normalized)) return true;
|
|
30978
|
+
const embedded = /^::(?:ffff:(?:0:)?)?([0-9a-f]+):([0-9a-f]+)$/.exec(normalized);
|
|
30979
|
+
if (embedded?.[1] && embedded[2]) return isPrivateIPv4Words(embedded[1], embedded[2]);
|
|
30980
|
+
return false;
|
|
30900
30981
|
}
|
|
30901
|
-
__name(
|
|
30902
|
-
function
|
|
30903
|
-
const
|
|
30904
|
-
|
|
30905
|
-
|
|
30906
|
-
|
|
30907
|
-
function registerMediaUploadAppResource(server, resolveEndpoint) {
|
|
30908
|
-
const html = renderMediaUploadAppHtml(resolveEndpoint);
|
|
30909
|
-
const csp = buildMediaUploadAppCsp(resolveEndpoint);
|
|
30910
|
-
registerAppResource(server, MEDIA_UPLOAD_APP_NAME, MEDIA_UPLOAD_APP_RESOURCE_URI, {
|
|
30911
|
-
title: "Upload media",
|
|
30912
|
-
mimeType: RESOURCE_MIME_TYPE,
|
|
30913
|
-
_meta: {
|
|
30914
|
-
ui: {
|
|
30915
|
-
csp
|
|
30916
|
-
}
|
|
30917
|
-
}
|
|
30918
|
-
}, async () => ({
|
|
30919
|
-
contents: [
|
|
30920
|
-
{
|
|
30921
|
-
uri: MEDIA_UPLOAD_APP_RESOURCE_URI,
|
|
30922
|
-
mimeType: RESOURCE_MIME_TYPE,
|
|
30923
|
-
text: html,
|
|
30924
|
-
_meta: {
|
|
30925
|
-
ui: {
|
|
30926
|
-
csp
|
|
30927
|
-
}
|
|
30928
|
-
}
|
|
30929
|
-
}
|
|
30930
|
-
]
|
|
30931
|
-
}));
|
|
30982
|
+
__name(isPrivateIPv6, "isPrivateIPv6");
|
|
30983
|
+
function isPrivateIp(ip) {
|
|
30984
|
+
const kind2 = net.isIP(ip);
|
|
30985
|
+
if (kind2 === 4) return isPrivateIPv4(ip);
|
|
30986
|
+
if (kind2 === 6) return isPrivateIPv6(ip);
|
|
30987
|
+
return false;
|
|
30932
30988
|
}
|
|
30933
|
-
__name(
|
|
30934
|
-
|
|
30935
|
-
|
|
30936
|
-
|
|
30937
|
-
|
|
30938
|
-
|
|
30939
|
-
|
|
30940
|
-
|
|
30941
|
-
|
|
30942
|
-
|
|
30943
|
-
|
|
30944
|
-
|
|
30945
|
-
|
|
30946
|
-
return null;
|
|
30989
|
+
__name(isPrivateIp, "isPrivateIp");
|
|
30990
|
+
function looksLikeNonCanonicalIp(host) {
|
|
30991
|
+
if (net.isIP(host) !== 0) return false;
|
|
30992
|
+
if (/^\d+$/.test(host)) return true;
|
|
30993
|
+
if (/^0x[0-9a-f]+$/i.test(host)) return true;
|
|
30994
|
+
if (host.includes(".")) {
|
|
30995
|
+
const labels = host.split(".");
|
|
30996
|
+
const allNumeric = labels.every((label) => {
|
|
30997
|
+
if (label.length === 0) return false;
|
|
30998
|
+
return /^\d+$/.test(label) || // decimal / octal-with-leading-zero
|
|
30999
|
+
/^0x[0-9a-f]+$/i.test(label);
|
|
31000
|
+
});
|
|
31001
|
+
if (allNumeric) return true;
|
|
30947
31002
|
}
|
|
31003
|
+
return false;
|
|
30948
31004
|
}
|
|
30949
|
-
__name(
|
|
30950
|
-
|
|
30951
|
-
|
|
30952
|
-
|
|
30953
|
-
|
|
31005
|
+
__name(looksLikeNonCanonicalIp, "looksLikeNonCanonicalIp");
|
|
31006
|
+
|
|
31007
|
+
// src/public-host.ts
|
|
31008
|
+
function isPrivateHost(value) {
|
|
31009
|
+
let host = value.toLowerCase().replace(/^\[|\]$/g, "").replace(/\.$/, "");
|
|
31010
|
+
if (host === "localhost" || host === "metadata.google.internal") return true;
|
|
31011
|
+
if (host.includes(":")) {
|
|
30954
31012
|
try {
|
|
30955
|
-
|
|
30956
|
-
return url2.protocol === "https:" && url2.origin === origin && url2.pathname.startsWith("/assets/") && url2.pathname.length > "/assets/".length && !url2.username && !url2.password ? url2.href : null;
|
|
31013
|
+
host = new URL(`https://[${host}]/`).hostname.slice(1, -1);
|
|
30957
31014
|
} catch {
|
|
30958
|
-
return
|
|
31015
|
+
return true;
|
|
30959
31016
|
}
|
|
30960
|
-
}
|
|
30961
|
-
|
|
30962
|
-
|
|
30963
|
-
|
|
30964
|
-
|
|
30965
|
-
|
|
30966
|
-
|
|
30967
|
-
|
|
30968
|
-
|
|
30969
|
-
}
|
|
30970
|
-
|
|
30971
|
-
|
|
30972
|
-
|
|
30973
|
-
|
|
30974
|
-
|
|
30975
|
-
|
|
30976
|
-
|
|
30977
|
-
|
|
30978
|
-
|
|
30979
|
-
|
|
30980
|
-
|
|
30981
|
-
|
|
30982
|
-
] : [];
|
|
30983
|
-
});
|
|
30984
|
-
const links = media.links.flatMap((link) => {
|
|
30985
|
-
const url2 = safeUrl(link.url);
|
|
30986
|
-
return url2 ? [
|
|
30987
|
-
{
|
|
30988
|
-
url: url2,
|
|
30989
|
-
label: link.label?.slice(0, 120) ?? "Open result"
|
|
30990
|
-
}
|
|
30991
|
-
] : [];
|
|
30992
|
-
});
|
|
30993
|
-
return {
|
|
30994
|
-
state: images.length || videos.length || links.length ? "ready" : "empty",
|
|
30995
|
-
images,
|
|
30996
|
-
videos,
|
|
30997
|
-
links
|
|
31017
|
+
}
|
|
31018
|
+
return looksLikeNonCanonicalIp(host) || isPrivateIp(host);
|
|
31019
|
+
}
|
|
31020
|
+
__name(isPrivateHost, "isPrivateHost");
|
|
31021
|
+
|
|
31022
|
+
// src/public-https.ts
|
|
31023
|
+
var PublicHttpsError = class extends Error {
|
|
31024
|
+
static {
|
|
31025
|
+
__name(this, "PublicHttpsError");
|
|
31026
|
+
}
|
|
31027
|
+
code;
|
|
31028
|
+
constructor(code, message) {
|
|
31029
|
+
super(message), this.code = code;
|
|
31030
|
+
this.name = "PublicHttpsError";
|
|
31031
|
+
}
|
|
31032
|
+
};
|
|
31033
|
+
function pinnedLookup(pin) {
|
|
31034
|
+
return (_hostname, options, callback) => {
|
|
31035
|
+
if (options.all) callback(null, [
|
|
31036
|
+
pin
|
|
31037
|
+
]);
|
|
31038
|
+
else callback(null, pin.address, pin.family);
|
|
30998
31039
|
};
|
|
30999
31040
|
}
|
|
31000
|
-
__name(
|
|
31001
|
-
function
|
|
31041
|
+
__name(pinnedLookup, "pinnedLookup");
|
|
31042
|
+
async function resolvePublicAddress(url2, signal) {
|
|
31043
|
+
const hostname = url2.hostname.replace(/^\[|\]$/g, "");
|
|
31044
|
+
if (isPrivateHost(hostname)) throw new PublicHttpsError("INVALID_INPUT", "media URL host is private");
|
|
31045
|
+
const literalFamily = isIP(hostname);
|
|
31046
|
+
const records = literalFamily ? [
|
|
31047
|
+
{
|
|
31048
|
+
address: hostname,
|
|
31049
|
+
family: literalFamily
|
|
31050
|
+
}
|
|
31051
|
+
] : await new Promise((resolve4, reject) => {
|
|
31052
|
+
const abort = /* @__PURE__ */ __name(() => reject(signal.reason), "abort");
|
|
31053
|
+
if (signal.aborted) return abort();
|
|
31054
|
+
signal.addEventListener("abort", abort, {
|
|
31055
|
+
once: true
|
|
31056
|
+
});
|
|
31057
|
+
lookup(hostname, {
|
|
31058
|
+
all: true
|
|
31059
|
+
}).then(resolve4, reject).finally(() => {
|
|
31060
|
+
signal.removeEventListener("abort", abort);
|
|
31061
|
+
});
|
|
31062
|
+
});
|
|
31063
|
+
if (!Array.isArray(records) || !records.length || records.some((entry) => !isIP(entry.address) || isIP(entry.address) !== entry.family || isPrivateHost(entry.address))) {
|
|
31064
|
+
throw new PublicHttpsError("INVALID_INPUT", "media URL DNS answers are not exclusively public IP addresses");
|
|
31065
|
+
}
|
|
31066
|
+
const first = records[0];
|
|
31067
|
+
if (!first || first.family !== 4 && first.family !== 6) throw new PublicHttpsError("INVALID_INPUT", "media URL has no usable DNS address");
|
|
31002
31068
|
return {
|
|
31003
|
-
|
|
31004
|
-
|
|
31005
|
-
},
|
|
31006
|
-
"openai/outputTemplate": TASK_RESULTS_APP_RESOURCE_URI
|
|
31069
|
+
address: first.address,
|
|
31070
|
+
family: first.family
|
|
31007
31071
|
};
|
|
31008
31072
|
}
|
|
31009
|
-
__name(
|
|
31010
|
-
function
|
|
31011
|
-
|
|
31073
|
+
__name(resolvePublicAddress, "resolvePublicAddress");
|
|
31074
|
+
async function withPinnedHttpsResponse(url2, pin, signal, consume, tls = {}) {
|
|
31075
|
+
const agent = new https.Agent({
|
|
31076
|
+
keepAlive: false,
|
|
31077
|
+
lookup: pinnedLookup(pin),
|
|
31078
|
+
...tls
|
|
31079
|
+
});
|
|
31080
|
+
let body;
|
|
31081
|
+
let incoming;
|
|
31082
|
+
let req;
|
|
31083
|
+
try {
|
|
31084
|
+
const response = await new Promise((resolve4, reject) => {
|
|
31085
|
+
req = https.request(url2, {
|
|
31086
|
+
agent,
|
|
31087
|
+
signal,
|
|
31088
|
+
headers: {
|
|
31089
|
+
"accept-encoding": "gzip, deflate, br"
|
|
31090
|
+
}
|
|
31091
|
+
}, (res) => {
|
|
31092
|
+
try {
|
|
31093
|
+
incoming = res;
|
|
31094
|
+
const encoding = res.headers["content-encoding"]?.toLowerCase();
|
|
31095
|
+
const decoder = encoding === "gzip" ? createGunzip() : encoding === "deflate" ? createInflate() : encoding === "br" ? createBrotliDecompress() : void 0;
|
|
31096
|
+
body = decoder ? res.pipe(decoder) : res;
|
|
31097
|
+
if (decoder) res.on("error", (error) => decoder.destroy(error));
|
|
31098
|
+
const iterator = body[Symbol.asyncIterator]();
|
|
31099
|
+
const stream = new ReadableStream({
|
|
31100
|
+
async pull(controller) {
|
|
31101
|
+
try {
|
|
31102
|
+
const next = await iterator.next();
|
|
31103
|
+
if (next.done) controller.close();
|
|
31104
|
+
else controller.enqueue(new Uint8Array(next.value));
|
|
31105
|
+
} catch (error) {
|
|
31106
|
+
controller.error(error);
|
|
31107
|
+
}
|
|
31108
|
+
},
|
|
31109
|
+
cancel() {
|
|
31110
|
+
body?.destroy();
|
|
31111
|
+
res.destroy();
|
|
31112
|
+
}
|
|
31113
|
+
});
|
|
31114
|
+
const headers = new Headers();
|
|
31115
|
+
for (const [key2, value] of Object.entries(res.headers)) {
|
|
31116
|
+
if (value !== void 0) headers.set(key2, Array.isArray(value) ? value.join(", ") : value);
|
|
31117
|
+
}
|
|
31118
|
+
const status = res.statusCode ?? 502;
|
|
31119
|
+
resolve4(new Response([
|
|
31120
|
+
204,
|
|
31121
|
+
205,
|
|
31122
|
+
304
|
|
31123
|
+
].includes(status) ? null : stream, {
|
|
31124
|
+
status,
|
|
31125
|
+
headers
|
|
31126
|
+
}));
|
|
31127
|
+
} catch (error) {
|
|
31128
|
+
res.destroy();
|
|
31129
|
+
reject(error);
|
|
31130
|
+
}
|
|
31131
|
+
});
|
|
31132
|
+
req.on("error", reject);
|
|
31133
|
+
req.end();
|
|
31134
|
+
});
|
|
31135
|
+
if (response.status >= 300 && response.status < 400) throw new PublicHttpsError("DOWNLOAD_FAILED", "media URL redirects are not allowed");
|
|
31136
|
+
return await consume(response, url2);
|
|
31137
|
+
} finally {
|
|
31138
|
+
body?.destroy();
|
|
31139
|
+
incoming?.destroy();
|
|
31140
|
+
req?.destroy();
|
|
31141
|
+
agent.destroy();
|
|
31142
|
+
}
|
|
31012
31143
|
}
|
|
31013
|
-
__name(
|
|
31014
|
-
function
|
|
31015
|
-
|
|
31016
|
-
|
|
31017
|
-
|
|
31018
|
-
|
|
31019
|
-
|
|
31020
|
-
|
|
31021
|
-
|
|
31022
|
-
|
|
31023
|
-
|
|
31024
|
-
|
|
31025
|
-
|
|
31026
|
-
|
|
31027
|
-
|
|
31028
|
-
|
|
31029
|
-
|
|
31030
|
-
|
|
31031
|
-
|
|
31032
|
-
|
|
31033
|
-
|
|
31034
|
-
|
|
31035
|
-
|
|
31036
|
-
|
|
31037
|
-
}, async () => ({
|
|
31038
|
-
contents: [
|
|
31039
|
-
{
|
|
31040
|
-
uri: TASK_RESULTS_APP_RESOURCE_URI,
|
|
31041
|
-
mimeType: RESOURCE_MIME_TYPE2,
|
|
31042
|
-
text: html,
|
|
31043
|
-
_meta: meta
|
|
31044
|
-
}
|
|
31045
|
-
]
|
|
31046
|
-
}));
|
|
31144
|
+
__name(withPinnedHttpsResponse, "withPinnedHttpsResponse");
|
|
31145
|
+
async function withPublicHttpsResponse(rawUrl, timeoutMs, consume) {
|
|
31146
|
+
let url2;
|
|
31147
|
+
try {
|
|
31148
|
+
url2 = new URL(rawUrl);
|
|
31149
|
+
} catch {
|
|
31150
|
+
throw new PublicHttpsError("INVALID_INPUT", "media URL is not valid");
|
|
31151
|
+
}
|
|
31152
|
+
if (url2.protocol !== "https:" || url2.username || url2.password) throw new PublicHttpsError("INVALID_INPUT", "media URL must be HTTPS without embedded credentials");
|
|
31153
|
+
const controller = new AbortController();
|
|
31154
|
+
const timer = setTimeout(() => controller.abort(new Error("media download timed out")), timeoutMs);
|
|
31155
|
+
timer.unref();
|
|
31156
|
+
try {
|
|
31157
|
+
let pin;
|
|
31158
|
+
try {
|
|
31159
|
+
pin = await resolvePublicAddress(url2, controller.signal);
|
|
31160
|
+
} catch (error) {
|
|
31161
|
+
if (error instanceof PublicHttpsError || controller.signal.aborted) throw error;
|
|
31162
|
+
throw new PublicHttpsError("INVALID_INPUT", "media URL DNS lookup failed");
|
|
31163
|
+
}
|
|
31164
|
+
return await withPinnedHttpsResponse(url2, pin, controller.signal, consume);
|
|
31165
|
+
} finally {
|
|
31166
|
+
clearTimeout(timer);
|
|
31167
|
+
}
|
|
31047
31168
|
}
|
|
31048
|
-
__name(
|
|
31049
|
-
|
|
31050
|
-
// src/tools/tasks.ts
|
|
31051
|
-
import { z as z137 } from "zod";
|
|
31169
|
+
__name(withPublicHttpsResponse, "withPublicHttpsResponse");
|
|
31052
31170
|
|
|
31053
31171
|
// src/resolve-image.ts
|
|
31054
|
-
import { lookup as dnsLookup } from "dns/promises";
|
|
31055
31172
|
import { promises as fs, createReadStream } from "fs";
|
|
31056
31173
|
import * as path from "path";
|
|
31057
31174
|
import * as os from "os";
|
|
@@ -31098,53 +31215,6 @@ function sniffMime(buf) {
|
|
|
31098
31215
|
return null;
|
|
31099
31216
|
}
|
|
31100
31217
|
__name(sniffMime, "sniffMime");
|
|
31101
|
-
function isPrivateHost(host) {
|
|
31102
|
-
if (host === "localhost" || host === "0.0.0.0") return true;
|
|
31103
|
-
if (host === "metadata.google.internal" || host === "169.254.169.254") return true;
|
|
31104
|
-
if (/^127\./.test(host)) return true;
|
|
31105
|
-
if (/^10\./.test(host)) return true;
|
|
31106
|
-
if (/^192\.168\./.test(host)) return true;
|
|
31107
|
-
if (/^172\.(1[6-9]|2\d|3[0-1])\./.test(host)) return true;
|
|
31108
|
-
if (host.startsWith("169.254.")) return true;
|
|
31109
|
-
if (host === "[::1]" || host === "::1") return true;
|
|
31110
|
-
if (/^\[?0*:(?:0*:){6}0*1\]?$/i.test(host)) return true;
|
|
31111
|
-
const ipv4Mapped = host.match(/^\[?::ffff:([0-9a-f.:]+)\]?$/i);
|
|
31112
|
-
if (ipv4Mapped && ipv4Mapped[1]) {
|
|
31113
|
-
const inner = ipv4Mapped[1];
|
|
31114
|
-
if (inner.includes(".")) return isPrivateHost(inner);
|
|
31115
|
-
const parts = inner.split(":").map((h) => parseInt(h, 16));
|
|
31116
|
-
if (parts.length === 2 && parts.every((n) => !Number.isNaN(n))) {
|
|
31117
|
-
const bytes = [
|
|
31118
|
-
parts[0] >> 8,
|
|
31119
|
-
parts[0] & 255,
|
|
31120
|
-
parts[1] >> 8,
|
|
31121
|
-
parts[1] & 255
|
|
31122
|
-
];
|
|
31123
|
-
return isPrivateHost(bytes.join("."));
|
|
31124
|
-
}
|
|
31125
|
-
}
|
|
31126
|
-
if (/^\[?f[cd][0-9a-f]{2}:/i.test(host)) return true;
|
|
31127
|
-
if (/^\[?fe[89ab][0-9a-f]:/i.test(host)) return true;
|
|
31128
|
-
return false;
|
|
31129
|
-
}
|
|
31130
|
-
__name(isPrivateHost, "isPrivateHost");
|
|
31131
|
-
async function assertHostResolvesPublic(hostname) {
|
|
31132
|
-
let records;
|
|
31133
|
-
try {
|
|
31134
|
-
records = await dnsLookup(hostname, {
|
|
31135
|
-
all: true
|
|
31136
|
-
});
|
|
31137
|
-
} catch {
|
|
31138
|
-
if (process.env.NODE_ENV === "test" || process.env.VITEST) return;
|
|
31139
|
-
throw new ResolveImageError("INVALID_INPUT", `imageUrl DNS lookup failed for ${hostname}`);
|
|
31140
|
-
}
|
|
31141
|
-
for (const { address } of records) {
|
|
31142
|
-
if (isPrivateHost(address)) {
|
|
31143
|
-
throw new ResolveImageError("INVALID_INPUT", `imageUrl host resolves to a private address (${address})`);
|
|
31144
|
-
}
|
|
31145
|
-
}
|
|
31146
|
-
}
|
|
31147
|
-
__name(assertHostResolvesPublic, "assertHostResolvesPublic");
|
|
31148
31218
|
function expandUserPath(raw) {
|
|
31149
31219
|
let s = raw.trim();
|
|
31150
31220
|
if (s === "~") return os.homedir();
|
|
@@ -31218,48 +31288,37 @@ async function resolvePath(rawPath) {
|
|
|
31218
31288
|
}
|
|
31219
31289
|
__name(resolvePath, "resolvePath");
|
|
31220
31290
|
async function resolveImageUrl(rawUrl, maxBytes) {
|
|
31221
|
-
let u;
|
|
31222
|
-
try {
|
|
31223
|
-
u = new URL(rawUrl);
|
|
31224
|
-
} catch {
|
|
31225
|
-
throw new ResolveImageError("INVALID_INPUT", "imageUrl is not a valid URL");
|
|
31226
|
-
}
|
|
31227
|
-
if (u.protocol !== "https:") throw new ResolveImageError("INVALID_INPUT", "imageUrl must be HTTPS");
|
|
31228
|
-
if (isPrivateHost(u.hostname)) throw new ResolveImageError("INVALID_INPUT", "imageUrl host not allowed");
|
|
31229
|
-
await assertHostResolvesPublic(u.hostname);
|
|
31230
|
-
let res;
|
|
31231
31291
|
try {
|
|
31232
|
-
|
|
31233
|
-
|
|
31234
|
-
|
|
31292
|
+
return await withPublicHttpsResponse(rawUrl, 3e4, async (res) => {
|
|
31293
|
+
if (!res.ok) throw new ResolveImageError("IMAGE_DOWNLOAD_FAILED", `HTTP ${res.status}`);
|
|
31294
|
+
const cl = res.headers.get("content-length");
|
|
31295
|
+
if (cl && Number(cl) > maxBytes) {
|
|
31296
|
+
throw new ResolveImageError("IMAGE_TOO_LARGE", `content-length ${cl}`);
|
|
31297
|
+
}
|
|
31298
|
+
const ct = (res.headers.get("content-type") ?? "").split(";")[0]?.trim() ?? "";
|
|
31299
|
+
if (!ALLOWED_MIMES.has(ct)) throw new ResolveImageError("IMAGE_TYPE_UNSUPPORTED", `content-type ${ct}`);
|
|
31300
|
+
let buf;
|
|
31301
|
+
try {
|
|
31302
|
+
buf = Buffer.from(await readResponseBytes(res, maxBytes));
|
|
31303
|
+
} catch (error) {
|
|
31304
|
+
if (error instanceof ResponseBodyTooLargeError) {
|
|
31305
|
+
throw new ResolveImageError("IMAGE_TOO_LARGE", `image URL exceeds ${maxBytes} bytes`);
|
|
31306
|
+
}
|
|
31307
|
+
throw new ResolveImageError("IMAGE_DOWNLOAD_FAILED", "image response could not be read");
|
|
31308
|
+
}
|
|
31309
|
+
const sniffed = sniffMime(buf);
|
|
31310
|
+
if (!sniffed || sniffed !== ct) {
|
|
31311
|
+
throw new ResolveImageError("IMAGE_TYPE_UNSUPPORTED", `magic bytes (${sniffed ?? "unknown"}) do not match content-type (${ct})`);
|
|
31312
|
+
}
|
|
31313
|
+
return {
|
|
31314
|
+
buffer: buf,
|
|
31315
|
+
mimetype: sniffed
|
|
31316
|
+
};
|
|
31235
31317
|
});
|
|
31236
|
-
} catch (e) {
|
|
31237
|
-
throw new ResolveImageError("IMAGE_DOWNLOAD_FAILED", e.message);
|
|
31238
|
-
}
|
|
31239
|
-
if (!res.ok) throw new ResolveImageError("IMAGE_DOWNLOAD_FAILED", `HTTP ${res.status}`);
|
|
31240
|
-
const cl = res.headers.get("content-length");
|
|
31241
|
-
if (cl && Number(cl) > maxBytes) {
|
|
31242
|
-
throw new ResolveImageError("IMAGE_TOO_LARGE", `content-length ${cl}`);
|
|
31243
|
-
}
|
|
31244
|
-
const ct = (res.headers.get("content-type") ?? "").split(";")[0]?.trim() ?? "";
|
|
31245
|
-
if (!ALLOWED_MIMES.has(ct)) throw new ResolveImageError("IMAGE_TYPE_UNSUPPORTED", `content-type ${ct}`);
|
|
31246
|
-
let buf;
|
|
31247
|
-
try {
|
|
31248
|
-
buf = Buffer.from(await readResponseBytes(res, maxBytes));
|
|
31249
31318
|
} catch (error) {
|
|
31250
|
-
if (error instanceof
|
|
31251
|
-
|
|
31252
|
-
}
|
|
31253
|
-
throw new ResolveImageError("IMAGE_DOWNLOAD_FAILED", "image response could not be read");
|
|
31254
|
-
}
|
|
31255
|
-
const sniffed = sniffMime(buf);
|
|
31256
|
-
if (!sniffed || sniffed !== ct) {
|
|
31257
|
-
throw new ResolveImageError("IMAGE_TYPE_UNSUPPORTED", `magic bytes (${sniffed ?? "unknown"}) do not match content-type (${ct})`);
|
|
31319
|
+
if (error instanceof ResolveImageError) throw error;
|
|
31320
|
+
throw new ResolveImageError(error instanceof PublicHttpsError && error.code === "INVALID_INPUT" ? "INVALID_INPUT" : "IMAGE_DOWNLOAD_FAILED", error instanceof Error ? error.message : "image download failed");
|
|
31258
31321
|
}
|
|
31259
|
-
return {
|
|
31260
|
-
buffer: buf,
|
|
31261
|
-
mimetype: sniffed
|
|
31262
|
-
};
|
|
31263
31322
|
}
|
|
31264
31323
|
__name(resolveImageUrl, "resolveImageUrl");
|
|
31265
31324
|
async function resolveImages(images, opts = {
|
|
@@ -31290,6 +31349,258 @@ async function resolveOne(ref, transport, maxBytes) {
|
|
|
31290
31349
|
}
|
|
31291
31350
|
__name(resolveOne, "resolveOne");
|
|
31292
31351
|
|
|
31352
|
+
// src/validate-api-url.ts
|
|
31353
|
+
function assertSafePublicBaseUrl(envName, url2) {
|
|
31354
|
+
let u;
|
|
31355
|
+
try {
|
|
31356
|
+
u = new URL(url2);
|
|
31357
|
+
} catch {
|
|
31358
|
+
throw new Error(`${envName} is not a valid URL: ${url2}`);
|
|
31359
|
+
}
|
|
31360
|
+
if (u.protocol !== "https:") {
|
|
31361
|
+
throw new Error(`${envName} must use https (got ${u.protocol})`);
|
|
31362
|
+
}
|
|
31363
|
+
if (u.username || u.password || u.search || u.hash) {
|
|
31364
|
+
throw new Error(`${envName} must not contain credentials, query parameters, or fragments`);
|
|
31365
|
+
}
|
|
31366
|
+
if (isPrivateHost(u.hostname)) {
|
|
31367
|
+
throw new Error(`${envName} must not point to a private/loopback host: ${u.hostname}`);
|
|
31368
|
+
}
|
|
31369
|
+
}
|
|
31370
|
+
__name(assertSafePublicBaseUrl, "assertSafePublicBaseUrl");
|
|
31371
|
+
|
|
31372
|
+
// src/app-origin.ts
|
|
31373
|
+
var PRODUCTMAKER_APP_ORIGIN = "https://productmaker.app";
|
|
31374
|
+
|
|
31375
|
+
// src/meta-upload-app.ts
|
|
31376
|
+
var MEDIA_UPLOAD_APP_RESOURCE_URI = "ui://media-upload/v1.html";
|
|
31377
|
+
var MEDIA_UPLOAD_APP_NAME = "ProductMaker media upload";
|
|
31378
|
+
var MEDIA_UPLOAD_APP_HTML_URL = new URL("./ui/meta-upload-app.html", import.meta.url);
|
|
31379
|
+
var PRODUCTMAKER_UPLOAD_STORAGE_ORIGIN = "https://825805a8113ddb98b68b9b0ddbb5d72a.r2.cloudflarestorage.com";
|
|
31380
|
+
var MEDIA_UPLOAD_CONNECT_DOMAINS = [
|
|
31381
|
+
"https://api.productmaker.app",
|
|
31382
|
+
PRODUCTMAKER_APP_ORIGIN,
|
|
31383
|
+
"https://www.productmaker.app",
|
|
31384
|
+
// The file bridge supplies temporary URLs. Keep this host-owned scope until
|
|
31385
|
+
// a real ChatGPT/library upload establishes every required exact origin.
|
|
31386
|
+
"https://*.oaiusercontent.com"
|
|
31387
|
+
];
|
|
31388
|
+
var STORAGE_ORIGINS_ERROR = "PM_MEDIA_UPLOAD_STORAGE_ORIGINS must be a JSON array of exact public HTTPS origins";
|
|
31389
|
+
function normalizeStorageOrigins(value) {
|
|
31390
|
+
try {
|
|
31391
|
+
const origins = z132.array(z132.string().trim().min(1)).max(16).parse(value);
|
|
31392
|
+
return [
|
|
31393
|
+
...new Set(origins.map((origin) => {
|
|
31394
|
+
assertSafePublicBaseUrl("PM_MEDIA_UPLOAD_STORAGE_ORIGINS", origin);
|
|
31395
|
+
const parsed = new URL(origin);
|
|
31396
|
+
if (parsed.pathname !== "/" || parsed.hostname.includes("*")) throw new Error(STORAGE_ORIGINS_ERROR);
|
|
31397
|
+
return parsed.origin;
|
|
31398
|
+
}))
|
|
31399
|
+
];
|
|
31400
|
+
} catch {
|
|
31401
|
+
throw new Error(STORAGE_ORIGINS_ERROR);
|
|
31402
|
+
}
|
|
31403
|
+
}
|
|
31404
|
+
__name(normalizeStorageOrigins, "normalizeStorageOrigins");
|
|
31405
|
+
function parseMediaUploadStorageOrigins(value) {
|
|
31406
|
+
if (value === void 0) return void 0;
|
|
31407
|
+
try {
|
|
31408
|
+
return normalizeStorageOrigins(JSON.parse(value));
|
|
31409
|
+
} catch {
|
|
31410
|
+
throw new Error(STORAGE_ORIGINS_ERROR);
|
|
31411
|
+
}
|
|
31412
|
+
}
|
|
31413
|
+
__name(parseMediaUploadStorageOrigins, "parseMediaUploadStorageOrigins");
|
|
31414
|
+
function buildMediaUploadAppCsp(resolveEndpoint, storageOrigins) {
|
|
31415
|
+
const resolveOrigin = new URL(resolveEndpoint).origin;
|
|
31416
|
+
const approvedStorage = storageOrigins === void 0 ? resolveOrigin === "https://api.productmaker.app" ? [
|
|
31417
|
+
PRODUCTMAKER_UPLOAD_STORAGE_ORIGIN
|
|
31418
|
+
] : [] : normalizeStorageOrigins(storageOrigins);
|
|
31419
|
+
return {
|
|
31420
|
+
connectDomains: [
|
|
31421
|
+
.../* @__PURE__ */ new Set([
|
|
31422
|
+
...MEDIA_UPLOAD_CONNECT_DOMAINS,
|
|
31423
|
+
...approvedStorage,
|
|
31424
|
+
resolveOrigin
|
|
31425
|
+
])
|
|
31426
|
+
],
|
|
31427
|
+
// This standalone HTML has no remote static assets. Local image previews
|
|
31428
|
+
// use File/createImageBitmap and data: URLs; host-library bytes use fetch.
|
|
31429
|
+
resourceDomains: []
|
|
31430
|
+
};
|
|
31431
|
+
}
|
|
31432
|
+
__name(buildMediaUploadAppCsp, "buildMediaUploadAppCsp");
|
|
31433
|
+
var MEDIA_UPLOAD_APP_CSP = buildMediaUploadAppCsp("https://api.productmaker.app/v1/uploaded-assets/sessions/resolve");
|
|
31434
|
+
function readMediaUploadAppHtml(htmlUrl = MEDIA_UPLOAD_APP_HTML_URL) {
|
|
31435
|
+
try {
|
|
31436
|
+
return readFileSync(htmlUrl, "utf8");
|
|
31437
|
+
} catch (error) {
|
|
31438
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
31439
|
+
throw new Error(`Missing packaged media upload app HTML at ${htmlUrl.pathname}. Rebuild @productmaker/mcp so ${MEDIA_UPLOAD_APP_RESOURCE_URI} is bundled. Original error: ${message}`);
|
|
31440
|
+
}
|
|
31441
|
+
}
|
|
31442
|
+
__name(readMediaUploadAppHtml, "readMediaUploadAppHtml");
|
|
31443
|
+
function renderMediaUploadAppHtml(resolveEndpoint) {
|
|
31444
|
+
const encodedEndpoint = JSON.stringify(resolveEndpoint).slice(1, -1).replaceAll("<", "\\u003c");
|
|
31445
|
+
return readMediaUploadAppHtml().replace("__PRODUCTMAKER_RESOLVE_ENDPOINT__", encodedEndpoint);
|
|
31446
|
+
}
|
|
31447
|
+
__name(renderMediaUploadAppHtml, "renderMediaUploadAppHtml");
|
|
31448
|
+
function registerMediaUploadAppResource(server, resolveEndpoint, storageOrigins) {
|
|
31449
|
+
const html = renderMediaUploadAppHtml(resolveEndpoint);
|
|
31450
|
+
const csp = buildMediaUploadAppCsp(resolveEndpoint, storageOrigins);
|
|
31451
|
+
const meta = {
|
|
31452
|
+
ui: {
|
|
31453
|
+
csp
|
|
31454
|
+
},
|
|
31455
|
+
// OpenAI requires a dedicated per-plugin UI origin. Keep its compatibility
|
|
31456
|
+
// alias separate from the host-derived standard domain used by Claude.
|
|
31457
|
+
"openai/widgetDomain": PRODUCTMAKER_APP_ORIGIN,
|
|
31458
|
+
"openai/widgetCSP": {
|
|
31459
|
+
connect_domains: csp.connectDomains,
|
|
31460
|
+
resource_domains: csp.resourceDomains,
|
|
31461
|
+
// Trust only the existing secure-upload fallback for OpenAI external opens.
|
|
31462
|
+
// The current HTML uses an ordinary anchor; no navigation behavior changes.
|
|
31463
|
+
redirect_domains: [
|
|
31464
|
+
PRODUCTMAKER_APP_ORIGIN
|
|
31465
|
+
]
|
|
31466
|
+
}
|
|
31467
|
+
};
|
|
31468
|
+
registerAppResource(server, MEDIA_UPLOAD_APP_NAME, MEDIA_UPLOAD_APP_RESOURCE_URI, {
|
|
31469
|
+
title: "Upload media",
|
|
31470
|
+
mimeType: RESOURCE_MIME_TYPE,
|
|
31471
|
+
_meta: meta
|
|
31472
|
+
}, async () => ({
|
|
31473
|
+
contents: [
|
|
31474
|
+
{
|
|
31475
|
+
uri: MEDIA_UPLOAD_APP_RESOURCE_URI,
|
|
31476
|
+
mimeType: RESOURCE_MIME_TYPE,
|
|
31477
|
+
text: html,
|
|
31478
|
+
_meta: meta
|
|
31479
|
+
}
|
|
31480
|
+
]
|
|
31481
|
+
}));
|
|
31482
|
+
}
|
|
31483
|
+
__name(registerMediaUploadAppResource, "registerMediaUploadAppResource");
|
|
31484
|
+
|
|
31485
|
+
// src/task-results-app.ts
|
|
31486
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
31487
|
+
import { registerAppResource as registerAppResource2, RESOURCE_MIME_TYPE as RESOURCE_MIME_TYPE2 } from "@modelcontextprotocol/ext-apps/server";
|
|
31488
|
+
var TASK_RESULTS_APP_RESOURCE_URI = "ui://task-results/v1.html";
|
|
31489
|
+
var HTML_URL = new URL("./ui/task-results-app.html", import.meta.url);
|
|
31490
|
+
function assetOrigin(value) {
|
|
31491
|
+
if (!value) return null;
|
|
31492
|
+
try {
|
|
31493
|
+
const url2 = new URL(value);
|
|
31494
|
+
return url2.protocol === "https:" && !url2.username && !url2.password ? url2.origin : null;
|
|
31495
|
+
} catch {
|
|
31496
|
+
return null;
|
|
31497
|
+
}
|
|
31498
|
+
}
|
|
31499
|
+
__name(assetOrigin, "assetOrigin");
|
|
31500
|
+
function buildTaskResultsView(media, proxyBaseUrl) {
|
|
31501
|
+
const origin = assetOrigin(proxyBaseUrl);
|
|
31502
|
+
const safeUrl = /* @__PURE__ */ __name((value) => {
|
|
31503
|
+
if (!origin) return null;
|
|
31504
|
+
try {
|
|
31505
|
+
const url2 = new URL(value);
|
|
31506
|
+
return url2.protocol === "https:" && url2.origin === origin && url2.pathname.startsWith("/assets/") && url2.pathname.length > "/assets/".length && !url2.username && !url2.password ? url2.href : null;
|
|
31507
|
+
} catch {
|
|
31508
|
+
return null;
|
|
31509
|
+
}
|
|
31510
|
+
}, "safeUrl");
|
|
31511
|
+
const images = media.images.flatMap((image2, index) => {
|
|
31512
|
+
const url2 = safeUrl(image2.url);
|
|
31513
|
+
return url2 ? [
|
|
31514
|
+
{
|
|
31515
|
+
url: url2,
|
|
31516
|
+
alt: image2.alt?.slice(0, 160) ?? `ProductMaker image ${index + 1}`
|
|
31517
|
+
}
|
|
31518
|
+
] : [];
|
|
31519
|
+
});
|
|
31520
|
+
const videos = media.videos.flatMap((video) => {
|
|
31521
|
+
const url2 = safeUrl(video.url);
|
|
31522
|
+
return url2 ? [
|
|
31523
|
+
{
|
|
31524
|
+
url: url2,
|
|
31525
|
+
...video.label ? {
|
|
31526
|
+
label: video.label.slice(0, 120)
|
|
31527
|
+
} : {},
|
|
31528
|
+
...video.scriptRewritten === true ? {
|
|
31529
|
+
scriptRewritten: true
|
|
31530
|
+
} : {}
|
|
31531
|
+
}
|
|
31532
|
+
] : [];
|
|
31533
|
+
});
|
|
31534
|
+
const links = media.links.flatMap((link) => {
|
|
31535
|
+
const url2 = safeUrl(link.url);
|
|
31536
|
+
return url2 ? [
|
|
31537
|
+
{
|
|
31538
|
+
url: url2,
|
|
31539
|
+
label: link.label?.slice(0, 120) ?? "Open result"
|
|
31540
|
+
}
|
|
31541
|
+
] : [];
|
|
31542
|
+
});
|
|
31543
|
+
return {
|
|
31544
|
+
state: images.length || videos.length || links.length ? "ready" : "empty",
|
|
31545
|
+
images,
|
|
31546
|
+
videos,
|
|
31547
|
+
links
|
|
31548
|
+
};
|
|
31549
|
+
}
|
|
31550
|
+
__name(buildTaskResultsView, "buildTaskResultsView");
|
|
31551
|
+
function taskResultsToolMeta() {
|
|
31552
|
+
return {
|
|
31553
|
+
ui: {
|
|
31554
|
+
resourceUri: TASK_RESULTS_APP_RESOURCE_URI
|
|
31555
|
+
},
|
|
31556
|
+
"openai/outputTemplate": TASK_RESULTS_APP_RESOURCE_URI
|
|
31557
|
+
};
|
|
31558
|
+
}
|
|
31559
|
+
__name(taskResultsToolMeta, "taskResultsToolMeta");
|
|
31560
|
+
function renderTaskResultsAppHtml(proxyBaseUrl) {
|
|
31561
|
+
return readFileSync2(HTML_URL, "utf8").replace("__PRODUCTMAKER_ASSET_ORIGIN__", JSON.stringify(assetOrigin(proxyBaseUrl)).replaceAll("<", "\\u003c"));
|
|
31562
|
+
}
|
|
31563
|
+
__name(renderTaskResultsAppHtml, "renderTaskResultsAppHtml");
|
|
31564
|
+
function registerTaskResultsAppResource(server, proxyBaseUrl) {
|
|
31565
|
+
const origin = assetOrigin(proxyBaseUrl);
|
|
31566
|
+
const resourceDomains = origin ? [
|
|
31567
|
+
origin
|
|
31568
|
+
] : [];
|
|
31569
|
+
const html = renderTaskResultsAppHtml(proxyBaseUrl);
|
|
31570
|
+
const meta = {
|
|
31571
|
+
ui: {
|
|
31572
|
+
csp: {
|
|
31573
|
+
resourceDomains,
|
|
31574
|
+
connectDomains: []
|
|
31575
|
+
}
|
|
31576
|
+
},
|
|
31577
|
+
"openai/widgetDomain": PRODUCTMAKER_APP_ORIGIN,
|
|
31578
|
+
"openai/widgetCSP": {
|
|
31579
|
+
resource_domains: resourceDomains,
|
|
31580
|
+
connect_domains: []
|
|
31581
|
+
},
|
|
31582
|
+
"openai/widgetDescription": "Displays the actual ProductMaker images and videos with playback controls and download links."
|
|
31583
|
+
};
|
|
31584
|
+
registerAppResource2(server, "ProductMaker task results", TASK_RESULTS_APP_RESOURCE_URI, {
|
|
31585
|
+
title: "Your creative",
|
|
31586
|
+
mimeType: RESOURCE_MIME_TYPE2,
|
|
31587
|
+
_meta: meta
|
|
31588
|
+
}, async () => ({
|
|
31589
|
+
contents: [
|
|
31590
|
+
{
|
|
31591
|
+
uri: TASK_RESULTS_APP_RESOURCE_URI,
|
|
31592
|
+
mimeType: RESOURCE_MIME_TYPE2,
|
|
31593
|
+
text: html,
|
|
31594
|
+
_meta: meta
|
|
31595
|
+
}
|
|
31596
|
+
]
|
|
31597
|
+
}));
|
|
31598
|
+
}
|
|
31599
|
+
__name(registerTaskResultsAppResource, "registerTaskResultsAppResource");
|
|
31600
|
+
|
|
31601
|
+
// src/tools/tasks.ts
|
|
31602
|
+
import { z as z138 } from "zod";
|
|
31603
|
+
|
|
31293
31604
|
// src/status-poller.ts
|
|
31294
31605
|
var TERMINAL = /* @__PURE__ */ new Set([
|
|
31295
31606
|
"done",
|
|
@@ -31366,7 +31677,6 @@ __name(buildPollingContinuation, "buildPollingContinuation");
|
|
|
31366
31677
|
import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js";
|
|
31367
31678
|
|
|
31368
31679
|
// src/resolve-media.ts
|
|
31369
|
-
import { lookup as dnsLookup2 } from "dns/promises";
|
|
31370
31680
|
import { promises as fs2, createReadStream as createReadStream2 } from "fs";
|
|
31371
31681
|
import * as os2 from "os";
|
|
31372
31682
|
import * as path2 from "path";
|
|
@@ -31503,32 +31813,6 @@ async function readFileWithCap2(absPath) {
|
|
|
31503
31813
|
return Buffer.concat(chunks, total);
|
|
31504
31814
|
}
|
|
31505
31815
|
__name(readFileWithCap2, "readFileWithCap");
|
|
31506
|
-
async function assertPublicUrl(rawUrl) {
|
|
31507
|
-
let url2;
|
|
31508
|
-
try {
|
|
31509
|
-
url2 = new URL(rawUrl);
|
|
31510
|
-
} catch {
|
|
31511
|
-
throw new ResolveMediaError("INVALID_INPUT", "media URL is not valid");
|
|
31512
|
-
}
|
|
31513
|
-
if (url2.protocol !== "https:") throw new ResolveMediaError("INVALID_INPUT", "media URL must be HTTPS");
|
|
31514
|
-
if (url2.username || url2.password) throw new ResolveMediaError("INVALID_INPUT", "media URL must not contain embedded credentials");
|
|
31515
|
-
if (isPrivateHost(url2.hostname)) throw new ResolveMediaError("INVALID_INPUT", "media URL host is private");
|
|
31516
|
-
try {
|
|
31517
|
-
const records = await dnsLookup2(url2.hostname, {
|
|
31518
|
-
all: true
|
|
31519
|
-
});
|
|
31520
|
-
if (records.some((record) => isPrivateHost(record.address))) {
|
|
31521
|
-
throw new ResolveMediaError("INVALID_INPUT", "media URL resolves to a private address");
|
|
31522
|
-
}
|
|
31523
|
-
} catch (error) {
|
|
31524
|
-
if (error instanceof ResolveMediaError) throw error;
|
|
31525
|
-
if (process.env.NODE_ENV !== "test" && !process.env.VITEST) {
|
|
31526
|
-
throw new ResolveMediaError("INVALID_INPUT", "media URL DNS lookup failed");
|
|
31527
|
-
}
|
|
31528
|
-
}
|
|
31529
|
-
return url2;
|
|
31530
|
-
}
|
|
31531
|
-
__name(assertPublicUrl, "assertPublicUrl");
|
|
31532
31816
|
async function resolvePath2(rawPath, expectedKind) {
|
|
31533
31817
|
const absolutePath = path2.resolve(expandUserPath2(rawPath));
|
|
31534
31818
|
const extension = path2.extname(absolutePath).toLowerCase();
|
|
@@ -31572,46 +31856,48 @@ async function resolveMediaRef(ref, opts) {
|
|
|
31572
31856
|
return resolvePath2(ref.path, opts.expectedKind);
|
|
31573
31857
|
}
|
|
31574
31858
|
if (!ref.url) throw new ResolveMediaError("INVALID_INPUT", "media URL is empty");
|
|
31575
|
-
|
|
31576
|
-
|
|
31577
|
-
|
|
31578
|
-
|
|
31579
|
-
|
|
31580
|
-
|
|
31581
|
-
|
|
31582
|
-
|
|
31583
|
-
|
|
31584
|
-
|
|
31585
|
-
|
|
31586
|
-
|
|
31587
|
-
|
|
31588
|
-
|
|
31589
|
-
|
|
31590
|
-
|
|
31591
|
-
|
|
31592
|
-
|
|
31593
|
-
|
|
31594
|
-
|
|
31595
|
-
|
|
31596
|
-
|
|
31597
|
-
|
|
31598
|
-
|
|
31599
|
-
|
|
31859
|
+
try {
|
|
31860
|
+
return await withPublicHttpsResponse(ref.url, 12e4, async (response, url2) => {
|
|
31861
|
+
const extension = path2.extname(url2.pathname).toLowerCase();
|
|
31862
|
+
const format = MEDIA_FORMATS.find((entry) => entry.exts.some((candidate) => candidate === extension));
|
|
31863
|
+
if (format && opts.expectedKind && format.kind !== opts.expectedKind) {
|
|
31864
|
+
throw new ResolveMediaError("MEDIA_TYPE_UNSUPPORTED", "media URL extension does not match the requested type");
|
|
31865
|
+
}
|
|
31866
|
+
if (!response.ok) throw new ResolveMediaError("MEDIA_URL_UNAVAILABLE", `media URL returned HTTP ${response.status}`);
|
|
31867
|
+
const contentLength = Number(response.headers.get("content-length") ?? 0);
|
|
31868
|
+
if (contentLength > MAX_MEDIA_BYTES) {
|
|
31869
|
+
void response.body?.cancel().catch(() => void 0);
|
|
31870
|
+
throw new ResolveMediaError("MEDIA_TOO_LARGE", `media URL exceeds ${MAX_MEDIA_BYTES} bytes`);
|
|
31871
|
+
}
|
|
31872
|
+
const buffer = await readResponseWithCap(response);
|
|
31873
|
+
const sniffed = sniffMime2(buffer, extension);
|
|
31874
|
+
if (!sniffed) throw new ResolveMediaError("MEDIA_TYPE_UNSUPPORTED", "media URL magic bytes are unsupported");
|
|
31875
|
+
const kind2 = sniffed.startsWith("video/") ? "video" : "image";
|
|
31876
|
+
if (opts.expectedKind && kind2 !== opts.expectedKind) {
|
|
31877
|
+
throw new ResolveMediaError("MEDIA_TYPE_UNSUPPORTED", `expected ${opts.expectedKind} but received ${kind2}`);
|
|
31878
|
+
}
|
|
31879
|
+
if (format && sniffed !== format.mime) {
|
|
31880
|
+
throw new ResolveMediaError("MEDIA_TYPE_UNSUPPORTED", `media URL magic bytes do not match ${format.mime}`);
|
|
31881
|
+
}
|
|
31882
|
+
const filename = path2.basename(url2.pathname) || `media.${kind2 === "video" ? "mp4" : "jpg"}`;
|
|
31883
|
+
return {
|
|
31884
|
+
kind: kind2,
|
|
31885
|
+
mimetype: sniffed,
|
|
31886
|
+
filename,
|
|
31887
|
+
buffer
|
|
31888
|
+
};
|
|
31889
|
+
});
|
|
31890
|
+
} catch (error) {
|
|
31891
|
+
if (error instanceof ResolveMediaError) throw error;
|
|
31892
|
+
throw new ResolveMediaError(error instanceof PublicHttpsError && error.code === "INVALID_INPUT" ? "INVALID_INPUT" : "MEDIA_URL_UNAVAILABLE", error instanceof Error ? error.message : "media download failed");
|
|
31600
31893
|
}
|
|
31601
|
-
const filename = path2.basename(url2.pathname) || `media.${kind2 === "video" ? "mp4" : "jpg"}`;
|
|
31602
|
-
return {
|
|
31603
|
-
kind: kind2,
|
|
31604
|
-
mimetype: sniffed,
|
|
31605
|
-
filename,
|
|
31606
|
-
buffer
|
|
31607
|
-
};
|
|
31608
31894
|
}
|
|
31609
31895
|
__name(resolveMediaRef, "resolveMediaRef");
|
|
31610
31896
|
|
|
31611
31897
|
// src/tools/shared.ts
|
|
31612
|
-
import { z as
|
|
31613
|
-
var Lang =
|
|
31614
|
-
var Country =
|
|
31898
|
+
import { z as z133 } from "zod";
|
|
31899
|
+
var Lang = z133.string().regex(/^[a-z]{2}(?:[-_][a-zA-Z]{2})?$/, "Usa un c\xF3digo de idioma BCP-47, por ejemplo es, en-US o pt-BR").max(10);
|
|
31900
|
+
var Country = z133.string().min(2).max(3);
|
|
31615
31901
|
var LandingStrategyEnum = LandingStrategySchema;
|
|
31616
31902
|
var LandingImageFormatEnum = LandingImageFormatSchema;
|
|
31617
31903
|
var VideoPresetEnum = VideoPreset.extract([
|
|
@@ -31715,7 +32001,7 @@ function animationStyleApplies(videoPreset, angleNarratives) {
|
|
|
31715
32001
|
return ANIMATED_VIDEO_PRESETS.has(videoPreset ?? "") || (angleNarratives ?? []).some((n) => ANIMATED_NARRATIVE_STYLES.has(n));
|
|
31716
32002
|
}
|
|
31717
32003
|
__name(animationStyleApplies, "animationStyleApplies");
|
|
31718
|
-
var VideoTierEnum =
|
|
32004
|
+
var VideoTierEnum = z133.enum([
|
|
31719
32005
|
"basic"
|
|
31720
32006
|
]);
|
|
31721
32007
|
function resolveVideoTier(_tier) {
|
|
@@ -31773,10 +32059,10 @@ var ImageCreativeStyleEnum = ImageCreativeStyle;
|
|
|
31773
32059
|
var ImageCreativeArchetypeEnum = ImageCreativeArchetype;
|
|
31774
32060
|
var ImageCreativeVisualTreatmentEnum = ImageCreativeVisualTreatment;
|
|
31775
32061
|
var ImageCreativeAspectRatioEnum = ImageCreativeAspectRatio;
|
|
31776
|
-
var ImageRefSchema =
|
|
31777
|
-
url:
|
|
31778
|
-
path:
|
|
31779
|
-
assetId:
|
|
32062
|
+
var ImageRefSchema = z133.object({
|
|
32063
|
+
url: z133.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.'),
|
|
32064
|
+
path: z133.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`.'),
|
|
32065
|
+
assetId: z133.string().uuid().optional().describe("ID opaco devuelto por check_media_upload. \xDAsalo para una imagen subida desde el MCP App; ProductMaker adopta el archivo directamente y el MCP nunca descarga sus bytes.")
|
|
31780
32066
|
}).refine((v) => [
|
|
31781
32067
|
v.url,
|
|
31782
32068
|
v.path,
|
|
@@ -31784,11 +32070,11 @@ var ImageRefSchema = z132.object({
|
|
|
31784
32070
|
].filter(Boolean).length === 1, {
|
|
31785
32071
|
message: "each image entry must have exactly one of `url`, `path`, or `assetId`"
|
|
31786
32072
|
});
|
|
31787
|
-
var ImagesInput =
|
|
32073
|
+
var ImagesInput = z133.array(ImageRefSchema).min(1).max(5).superRefine((images, ctx) => {
|
|
31788
32074
|
const assetCount = images.filter((image2) => Boolean(image2.assetId)).length;
|
|
31789
32075
|
if (assetCount > 0 && assetCount !== images.length) {
|
|
31790
32076
|
ctx.addIssue({
|
|
31791
|
-
code:
|
|
32077
|
+
code: z133.ZodIssueCode.custom,
|
|
31792
32078
|
message: "all image entries must use assetId, or all must use URL/path sources"
|
|
31793
32079
|
});
|
|
31794
32080
|
}
|
|
@@ -31869,14 +32155,14 @@ async function submitCreative(api, opts) {
|
|
|
31869
32155
|
});
|
|
31870
32156
|
}
|
|
31871
32157
|
__name(submitCreative, "submitCreative");
|
|
31872
|
-
var ResearchTargetCountryEnum =
|
|
32158
|
+
var ResearchTargetCountryEnum = z133.enum([
|
|
31873
32159
|
"CO",
|
|
31874
32160
|
"MX",
|
|
31875
32161
|
"PE",
|
|
31876
32162
|
"EC",
|
|
31877
32163
|
"CL"
|
|
31878
32164
|
]);
|
|
31879
|
-
var ResearchCategoryEnum =
|
|
32165
|
+
var ResearchCategoryEnum = z133.enum([
|
|
31880
32166
|
"auto",
|
|
31881
32167
|
"automotive",
|
|
31882
32168
|
"kitchen",
|
|
@@ -32671,7 +32957,7 @@ function approveRefusalMessage(reason) {
|
|
|
32671
32957
|
__name(approveRefusalMessage, "approveRefusalMessage");
|
|
32672
32958
|
|
|
32673
32959
|
// src/tools/task-outputs.ts
|
|
32674
|
-
import { z as
|
|
32960
|
+
import { z as z134 } from "zod";
|
|
32675
32961
|
|
|
32676
32962
|
// src/tools/conversational-review-format.ts
|
|
32677
32963
|
function escapeMarkdownText(value) {
|
|
@@ -32802,12 +33088,12 @@ function serviceVideoResultMedia(result) {
|
|
|
32802
33088
|
};
|
|
32803
33089
|
}
|
|
32804
33090
|
__name(serviceVideoResultMedia, "serviceVideoResultMedia");
|
|
32805
|
-
var CreativeVariantIdentity =
|
|
32806
|
-
variantTaskId:
|
|
32807
|
-
sourceTaskId:
|
|
33091
|
+
var CreativeVariantIdentity = z134.object({
|
|
33092
|
+
variantTaskId: z134.string().uuid(),
|
|
33093
|
+
sourceTaskId: z134.string().uuid(),
|
|
32808
33094
|
kind: TaskCreativeVariantKind,
|
|
32809
33095
|
status: TaskStatusSchema,
|
|
32810
|
-
error:
|
|
33096
|
+
error: z134.string().optional()
|
|
32811
33097
|
});
|
|
32812
33098
|
function proxifyOutputs(outputs, proxyBaseUrl) {
|
|
32813
33099
|
if (!proxyBaseUrl || !isProxyEnabled()) return outputs;
|
|
@@ -33411,29 +33697,29 @@ ${parsed.data.error ? `${escapeMarkdownText(parsed.data.error)}
|
|
|
33411
33697
|
__name(renderVideoScriptMarkdown, "renderVideoScriptMarkdown");
|
|
33412
33698
|
|
|
33413
33699
|
// src/tools/media-presentation.ts
|
|
33414
|
-
import { z as
|
|
33415
|
-
var HttpsPresentationUrlSchema =
|
|
33700
|
+
import { z as z135 } from "zod";
|
|
33701
|
+
var HttpsPresentationUrlSchema = z135.string().url().max(2048).refine((value) => new URL(value).protocol === "https:");
|
|
33416
33702
|
var ProductMakerPreviewUrlSchema = HttpsPresentationUrlSchema.refine((value) => {
|
|
33417
33703
|
const hostname = new URL(value).hostname.toLowerCase();
|
|
33418
33704
|
return hostname === "productmaker.app" || hostname.endsWith(".productmaker.app");
|
|
33419
33705
|
}, "preview URL must be owned by ProductMaker");
|
|
33420
|
-
var positiveIntAtMost = /* @__PURE__ */ __name((maximum) =>
|
|
33421
|
-
var PresentationImageSchema =
|
|
33422
|
-
kind:
|
|
33706
|
+
var positiveIntAtMost = /* @__PURE__ */ __name((maximum) => z135.number().int().positive().max(maximum), "positiveIntAtMost");
|
|
33707
|
+
var PresentationImageSchema = z135.object({
|
|
33708
|
+
kind: z135.literal("image"),
|
|
33423
33709
|
url: HttpsPresentationUrlSchema,
|
|
33424
|
-
mimeType:
|
|
33710
|
+
mimeType: z135.enum([
|
|
33425
33711
|
"image/jpeg",
|
|
33426
33712
|
"image/png"
|
|
33427
33713
|
]),
|
|
33428
33714
|
byteSize: positiveIntAtMost(5 * 1024 * 1024),
|
|
33429
33715
|
width: positiveIntAtMost(16384),
|
|
33430
33716
|
height: positiveIntAtMost(16384),
|
|
33431
|
-
alt:
|
|
33717
|
+
alt: z135.string().trim().min(1).max(240)
|
|
33432
33718
|
}).strict();
|
|
33433
33719
|
var McpMediaPresentationSchema = PresentationImageSchema;
|
|
33434
|
-
var GalleryImageSchema =
|
|
33720
|
+
var GalleryImageSchema = z135.object({
|
|
33435
33721
|
url: ProductMakerPreviewUrlSchema,
|
|
33436
|
-
alt:
|
|
33722
|
+
alt: z135.string().trim().min(1).max(240)
|
|
33437
33723
|
});
|
|
33438
33724
|
var MCP_GALLERY_IMAGE_MAX_BYTES = 10 * 1024 * 1024;
|
|
33439
33725
|
async function imageBlocks(image2, transport) {
|
|
@@ -33490,12 +33776,12 @@ async function buildMcpMediaPresentationContent(rawPresentation, transport = "ht
|
|
|
33490
33776
|
];
|
|
33491
33777
|
}
|
|
33492
33778
|
__name(buildMcpMediaPresentationContent, "buildMcpMediaPresentationContent");
|
|
33493
|
-
var OwnedImagePreviewSchema =
|
|
33779
|
+
var OwnedImagePreviewSchema = z135.object({
|
|
33494
33780
|
url: HttpsPresentationUrlSchema.refine((value) => {
|
|
33495
33781
|
const url2 = new URL(value);
|
|
33496
33782
|
return !url2.username && !url2.password && !isPrivateHost(url2.hostname);
|
|
33497
33783
|
}, "Preview URL must be public HTTPS without credentials"),
|
|
33498
|
-
alt:
|
|
33784
|
+
alt: z135.string().trim().min(1).max(240)
|
|
33499
33785
|
});
|
|
33500
33786
|
async function buildMcpImagePreviewContent(rawPreview, transport = "http") {
|
|
33501
33787
|
const preview2 = OwnedImagePreviewSchema.safeParse(rawPreview);
|
|
@@ -33690,7 +33976,7 @@ async function observeMcpMediaDelivery(fingerprint, work) {
|
|
|
33690
33976
|
__name(observeMcpMediaDelivery, "observeMcpMediaDelivery");
|
|
33691
33977
|
|
|
33692
33978
|
// src/tools/animated-video.ts
|
|
33693
|
-
import { z as
|
|
33979
|
+
import { z as z136 } from "zod";
|
|
33694
33980
|
|
|
33695
33981
|
// src/tools/paid-confirmation.ts
|
|
33696
33982
|
function describeMarketStudyChoice(enabled) {
|
|
@@ -33939,30 +34225,30 @@ function hookVariantsLabel(count) {
|
|
|
33939
34225
|
return count === 1 ? "1 versi\xF3n del gancho" : `${count} versiones del gancho`;
|
|
33940
34226
|
}
|
|
33941
34227
|
__name(hookVariantsLabel, "hookVariantsLabel");
|
|
33942
|
-
var DurationInput =
|
|
33943
|
-
var CreateAnimatedVideoInput =
|
|
33944
|
-
action:
|
|
34228
|
+
var DurationInput = z136.union(MOTION_GRAPHICS_DURATIONS.map((d) => z136.literal(d)));
|
|
34229
|
+
var CreateAnimatedVideoInput = z136.object({
|
|
34230
|
+
action: z136.enum([
|
|
33945
34231
|
"prepare",
|
|
33946
34232
|
"status",
|
|
33947
34233
|
"cancel",
|
|
33948
34234
|
"generate"
|
|
33949
34235
|
]).default("prepare").describe("prepare inicia una captura gratuita; status consulta websiteCaptureId; cancel la cancela; generate requiere captura ready y confirmaci\xF3n expl\xEDcita del usuario despu\xE9s de mostrar vista previa y precio."),
|
|
33950
|
-
brandId:
|
|
33951
|
-
websiteCaptureId:
|
|
33952
|
-
generationRequestId:
|
|
33953
|
-
confirmed:
|
|
34236
|
+
brandId: z136.string().uuid().optional().describe("ID de un servicio propio y listo (de create_brand). Obligatorio al preparar y crear videos nuevos. Conserva el mismo brandId en reintentos; si recuperas una generaci\xF3n hist\xF3rica aceptada sin este campo, om\xEDtelo como en la petici\xF3n original. status/cancel no lo necesitan."),
|
|
34237
|
+
websiteCaptureId: z136.string().uuid().optional().describe("ID de la captura preparada. Obligatorio para status/cancel/generate; no inventarlo."),
|
|
34238
|
+
generationRequestId: z136.string().uuid().optional().describe("Identidad opcional de una generaci\xF3n confirmada. Repite el mismo UUID al recuperar una respuesta perdida; usa otro \xFAnicamente cuando el usuario pida y confirme otro video, nunca por un error de transporte."),
|
|
34239
|
+
confirmed: z136.boolean().optional().describe("Solo true despu\xE9s de que el usuario confirme esta captura, formato, duraci\xF3n, idioma y precio. Obligatorio para generate; preparar no genera ni cobra."),
|
|
33954
34240
|
// Plain text, not `.url()`: users type «cal.com», and the web accepts it.
|
|
33955
34241
|
// normalizeWebAddress (shared with the web) adds https:// and refuses
|
|
33956
34242
|
// anything that is not a public http(s) address.
|
|
33957
|
-
landingUrl:
|
|
34243
|
+
landingUrl: z136.string().trim().min(1).optional().describe("Enlace p\xFAblico de la p\xE1gina del servicio, como lo dio el usuario (\xABcal.com\xBB o \xABhttps://cal.com\xBB). El video sale de lo que muestra esa p\xE1gina: su marca, colores y mensajes."),
|
|
33958
34244
|
format: MotionGraphicsFormatSchema.optional().describe('Formato: "vertical" (9:16, Reels/TikTok), "square" (1:1, feed) u "horizontal" (16:9, YouTube/web). Si el usuario no lo dijo, PREG\xDANTALE.'),
|
|
33959
34245
|
durationSec: DurationInput.optional().describe(`Duraci\xF3n en segundos: ${MOTION_GRAPHICS_DURATIONS.join(", ")}. Fija el precio (ver la descripci\xF3n de la herramienta). Si el usuario no la dijo, PREG\xDANTALE.`),
|
|
33960
34246
|
language: MotionGraphicsLanguageSchema.optional().describe('Idioma de los textos del video: "es", "en" o "pt".'),
|
|
33961
34247
|
videoQuality: MotionGraphicsVideoQualitySchema.default(DEFAULT_MOTION_GRAPHICS_VIDEO_QUALITY).describe('Calidad IA: "economy" (econ\xF3mica) o "pro" (alta, por defecto). Afecta el precio de los clips IA.'),
|
|
33962
34248
|
videoResolution: MotionGraphicsVideoResolutionSchema.default(DEFAULT_MOTION_GRAPHICS_VIDEO_RESOLUTION).describe('Resoluci\xF3n: "480p" (por defecto), "720p" o "1080p". 1080p solo est\xE1 disponible con videoQuality="pro".'),
|
|
33963
|
-
notes:
|
|
34249
|
+
notes: z136.string().trim().max(MOTION_GRAPHICS_NOTES_MAX_CHARS).optional().describe("Opcional. Idea creativa, escenarios, acciones, di\xE1logo, voz o m\xFAsica en lenguaje natural. El director puede hacer solo motion graphics, escenas reales o escenas imposibles seg\xFAn la idea; las frases gu\xEDan el guion sin garantizar reproducci\xF3n exacta."),
|
|
33964
34250
|
hookVariants: MotionGraphicsHookVariantsSchema.optional().describe(`Opcional. Cantidad de videos independientes, de 1 a ${MOTION_GRAPHICS_HOOK_VARIANTS_MAX} (por defecto 1). Cada uno tiene su propio \xE1ngulo, guion y estilo visual, respetando notes, y reserva el precio completo de un video (ver la descripci\xF3n); \xFAsalo solo si el usuario lo pide.`),
|
|
33965
|
-
music:
|
|
34251
|
+
music: z136.boolean().optional().describe("Opcional. M\xFAsica y efectos dirigidos seg\xFAn el negocio y el video (por defecto true), con una copia adicional solo con voces. false entrega solo voces, sin m\xFAsica ni efectos a\xF1adidos ni el cargo de m\xFAsica. \xDAsalo solo si el usuario lo pide.")
|
|
33966
34252
|
}).strict();
|
|
33967
34253
|
function captureNextAction(capture) {
|
|
33968
34254
|
if (capture.status === "ready") return "Muestra la vista previa al usuario y confirma formato, duraci\xF3n, idioma, calidad IA, resoluci\xF3n y precio. Solo despu\xE9s llama create_animated_video con action=generate, este websiteCaptureId y confirmed=true.";
|
|
@@ -34015,33 +34301,33 @@ var FORMAT_LABELS = {
|
|
|
34015
34301
|
square: "cuadrado 1:1",
|
|
34016
34302
|
horizontal: "horizontal 16:9"
|
|
34017
34303
|
};
|
|
34018
|
-
var AnimatedVideoResultSchema =
|
|
34019
|
-
status:
|
|
34304
|
+
var AnimatedVideoResultSchema = z136.object({
|
|
34305
|
+
status: z136.enum([
|
|
34020
34306
|
"pending",
|
|
34021
34307
|
"done",
|
|
34022
34308
|
"error"
|
|
34023
34309
|
]),
|
|
34024
|
-
serviceMode:
|
|
34025
|
-
finalMp4Url:
|
|
34026
|
-
adCopy:
|
|
34027
|
-
adCopies:
|
|
34028
|
-
error:
|
|
34029
|
-
motionGraphics:
|
|
34310
|
+
serviceMode: z136.literal("motion_graphics"),
|
|
34311
|
+
finalMp4Url: z136.string().optional(),
|
|
34312
|
+
adCopy: z136.string().optional(),
|
|
34313
|
+
adCopies: z136.array(z136.string()).optional(),
|
|
34314
|
+
error: z136.string().optional(),
|
|
34315
|
+
motionGraphics: z136.object({
|
|
34030
34316
|
stage: MotionGraphicsStageSchema,
|
|
34031
34317
|
format: MotionGraphicsFormatSchema,
|
|
34032
|
-
durationSec:
|
|
34033
|
-
width:
|
|
34034
|
-
height:
|
|
34318
|
+
durationSec: z136.number(),
|
|
34319
|
+
width: z136.number().optional(),
|
|
34320
|
+
height: z136.number().optional(),
|
|
34035
34321
|
language: MotionGraphicsLanguageSchema.optional(),
|
|
34036
34322
|
videoQuality: MotionGraphicsVideoQualitySchema.optional().catch(void 0),
|
|
34037
34323
|
videoResolution: MotionGraphicsVideoResolutionSchema.optional().catch(void 0),
|
|
34038
|
-
creativeBatch:
|
|
34324
|
+
creativeBatch: z136.object({
|
|
34039
34325
|
index: MotionGraphicsHookVariantsSchema,
|
|
34040
34326
|
count: MotionGraphicsHookVariantsSchema,
|
|
34041
|
-
tasks:
|
|
34042
|
-
taskId:
|
|
34327
|
+
tasks: z136.array(z136.object({
|
|
34328
|
+
taskId: z136.string().uuid(),
|
|
34043
34329
|
index: MotionGraphicsHookVariantsSchema,
|
|
34044
|
-
status:
|
|
34330
|
+
status: z136.enum([
|
|
34045
34331
|
"queued",
|
|
34046
34332
|
"processing",
|
|
34047
34333
|
"done",
|
|
@@ -34049,29 +34335,29 @@ var AnimatedVideoResultSchema = z135.object({
|
|
|
34049
34335
|
])
|
|
34050
34336
|
})).max(MOTION_GRAPHICS_HOOK_VARIANTS_MAX)
|
|
34051
34337
|
}).optional().catch(void 0),
|
|
34052
|
-
queuedAt:
|
|
34053
|
-
jobsAhead:
|
|
34054
|
-
stageStartedAt:
|
|
34055
|
-
renderProgress:
|
|
34338
|
+
queuedAt: z136.string().optional(),
|
|
34339
|
+
jobsAhead: z136.number().int().nonnegative().optional(),
|
|
34340
|
+
stageStartedAt: z136.string().optional(),
|
|
34341
|
+
renderProgress: z136.number().optional(),
|
|
34056
34342
|
// AI clip fields (absent on a video paid before clips, or from an API
|
|
34057
34343
|
// that predates them). Lenient: an unreadable value is dropped
|
|
34058
34344
|
// instead of hiding the status.
|
|
34059
|
-
clipCount:
|
|
34060
|
-
clipsUsed:
|
|
34061
|
-
clipPriceCents:
|
|
34062
|
-
clipsRefunded:
|
|
34345
|
+
clipCount: z136.number().int().nonnegative().optional().catch(void 0),
|
|
34346
|
+
clipsUsed: z136.number().int().nonnegative().optional().catch(void 0),
|
|
34347
|
+
clipPriceCents: z136.number().finite().nonnegative().optional().catch(void 0),
|
|
34348
|
+
clipsRefunded: z136.number().int().nonnegative().optional().catch(void 0),
|
|
34063
34349
|
// Hook versions (absent from an API or a video before them): the
|
|
34064
34350
|
// count ordered and, once done, the signed URLs of the EXTRA versions
|
|
34065
34351
|
// (hook 2, hook 3); `finalMp4Url` is hook 1.
|
|
34066
|
-
hookVariants:
|
|
34067
|
-
cleanVideoUrl:
|
|
34068
|
-
cleanVariantUrls:
|
|
34069
|
-
variantUrls:
|
|
34352
|
+
hookVariants: z136.number().int().min(1).max(MOTION_GRAPHICS_HOOK_VARIANTS_MAX).optional().catch(void 0),
|
|
34353
|
+
cleanVideoUrl: z136.string().url().optional().catch(void 0),
|
|
34354
|
+
cleanVariantUrls: z136.array(z136.string().url()).max(MOTION_GRAPHICS_HOOK_VARIANTS_MAX - 1).optional().catch(void 0),
|
|
34355
|
+
variantUrls: z136.array(z136.string().min(1)).max(MOTION_GRAPHICS_HOOK_VARIANTS_MAX - 1).optional().catch(void 0),
|
|
34070
34356
|
// Extra hook versions given back, as the task recorded it once the
|
|
34071
34357
|
// refunds landed (absent until then, and from an API before it).
|
|
34072
|
-
variantsRefunded:
|
|
34073
|
-
activity:
|
|
34074
|
-
previewUrl:
|
|
34358
|
+
variantsRefunded: z136.number().int().nonnegative().optional().catch(void 0),
|
|
34359
|
+
activity: z136.array(MotionGraphicsActivitySchema).optional().catch(void 0),
|
|
34360
|
+
previewUrl: z136.string().optional()
|
|
34075
34361
|
}).passthrough()
|
|
34076
34362
|
}).passthrough();
|
|
34077
34363
|
function parseAnimatedVideoResult(value) {
|
|
@@ -34350,7 +34636,7 @@ function registerAnimatedVideoTools(server, api) {
|
|
|
34350
34636
|
nowMs: 0
|
|
34351
34637
|
})
|
|
34352
34638
|
});
|
|
34353
|
-
const parsedTaskIds =
|
|
34639
|
+
const parsedTaskIds = z136.array(z136.string().uuid()).min(1).max(MOTION_GRAPHICS_HOOK_VARIANTS_MAX).safeParse(created.taskIds);
|
|
34354
34640
|
const taskIds = parsedTaskIds.success && parsedTaskIds.data[0] === created.taskId ? parsedTaskIds.data : void 0;
|
|
34355
34641
|
const receipt = {
|
|
34356
34642
|
taskId: created.taskId,
|
|
@@ -34392,10 +34678,10 @@ function registerAnimatedVideoTools(server, api) {
|
|
|
34392
34678
|
__name(registerAnimatedVideoTools, "registerAnimatedVideoTools");
|
|
34393
34679
|
|
|
34394
34680
|
// src/tools/winning-product-image.ts
|
|
34395
|
-
import { z as
|
|
34396
|
-
var WinnerReferenceInput =
|
|
34397
|
-
runId:
|
|
34398
|
-
candidateSourceId:
|
|
34681
|
+
import { z as z137 } from "zod";
|
|
34682
|
+
var WinnerReferenceInput = z137.object({
|
|
34683
|
+
runId: z137.string().uuid(),
|
|
34684
|
+
candidateSourceId: z137.string().trim().min(1).max(160)
|
|
34399
34685
|
}).strict().describe("Referencia opaca de un producto devuelto por find_winning_products/get_product_research_run.");
|
|
34400
34686
|
async function resolveWinnerImages(api, winnerReference, images) {
|
|
34401
34687
|
if (!winnerReference) return images;
|
|
@@ -34432,62 +34718,62 @@ var createProductTaskSingleOutputDescription = renderCreateProductTaskAvoidWhenF
|
|
|
34432
34718
|
var CREATE_SERVICE_VIDEO_ON_MCP = PRODUCTMAKER_MCP_CAPABILITY_NAMES.includes("create_service_video");
|
|
34433
34719
|
var listTasksGuidance = getProductMakerCapabilityModelGuidance("list_tasks");
|
|
34434
34720
|
var editTaskDraftGuidance = getProductMakerCapabilityModelGuidance("edit_task_draft");
|
|
34435
|
-
var CreateProductTaskInput =
|
|
34721
|
+
var CreateProductTaskInput = z138.object({
|
|
34436
34722
|
images: ImagesInput.optional().describe("1\u20135 fotos del mismo producto. Omitir \xFAnicamente cuando projectId apunta a un proyecto propio con imagen guardada."),
|
|
34437
34723
|
winnerReference: WinnerReferenceInput.optional().describe(WINNER_IMAGE_CHOICE_GUIDANCE),
|
|
34438
|
-
projectId:
|
|
34439
|
-
productUrl:
|
|
34724
|
+
projectId: z138.string().uuid().optional().describe("Opcional. Proyecto guiado propio. Permite continuar sin volver a subir la imagen guardada; nunca evita cr\xE9ditos, cuotas ni confirmaciones."),
|
|
34725
|
+
productUrl: z138.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."),
|
|
34440
34726
|
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.'),
|
|
34441
34727
|
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.'),
|
|
34442
|
-
price:
|
|
34443
|
-
compareAtPrice:
|
|
34444
|
-
offer:
|
|
34445
|
-
paymentMethods:
|
|
34446
|
-
productContext:
|
|
34447
|
-
productLabel:
|
|
34728
|
+
price: z138.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.'),
|
|
34729
|
+
compareAtPrice: z138.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".'),
|
|
34730
|
+
offer: z138.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.'),
|
|
34731
|
+
paymentMethods: z138.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.'),
|
|
34732
|
+
productContext: z138.string().max(PRODUCT_CONTEXT_MAX_CHARS).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."),
|
|
34733
|
+
productLabel: z138.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.'),
|
|
34448
34734
|
videoPreset: VideoPresetEnum.default("auto").describe('Opcional. Tipo/narrativa del video \u2014 \xFAnico campo de "estilo" que el usuario controla. Default "auto" (la IA escoge tras analizar el producto). Todos los presets funcionan a 15s (single_call) y 30s (Multiescena); "problem_solution" en single_call abre sin el producto y el producto entra por referencia en su beat de soluci\xF3n. Opciones: "auto", "ugc_dynamic" (review con experiencia y prueba), "ugc_how_to" (tutorial con pasos y resultado visible), "ugc_unboxing_chaos" (apertura y primera prueba), "problem_solution" (problema concreto, producto como soluci\xF3n y CTA), "product_showcase" (producto ejecutando una funci\xF3n real), "wearable_showcase" (prenda o accesorio modelado), "cinematic" (TV spot con receta cinematogr\xE1fica), "wild_card" (idea imposible: un truco visual que en la vida real no podr\xEDa pasar, y al cierre la invitaci\xF3n a comprar), "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.'),
|
|
34449
34735
|
animationStyle: AnimationStyleEnum.optional().describe('Opcional. Estilo visual SOLO para presets animados ("pixar_problem" o "pixar_sassy") \u2014 la API lo rechaza con cualquier otro preset. Opciones: "pixar_3d" (default, look 3D de pel\xEDcula animada), "claymation" (plastilina stop-motion artesanal), "wes_anderson" (tableau sim\xE9trico pastel), "sci_fi" (futurista con HUDs y ne\xF3n), "realista" (fotorrealista, fiel al producto). Si el usuario pide un look animado sin nombrar el estilo, omite el campo.'),
|
|
34450
|
-
durationSeconds:
|
|
34451
|
-
|
|
34452
|
-
|
|
34736
|
+
durationSeconds: z138.union([
|
|
34737
|
+
z138.literal(15),
|
|
34738
|
+
z138.literal(30)
|
|
34453
34739
|
]).default(15).describe("Opcional. Duraci\xF3n objetivo del video: 15s (single_call, default) o 30s (Multiescena, aproximadamente 30s y facturado por escena)."),
|
|
34454
|
-
pauseForAngleReview:
|
|
34455
|
-
aiDecidesAll:
|
|
34740
|
+
pauseForAngleReview: z138.boolean().optional().describe("Compatibilidad heredada. MCP deriva esta pausa de aiDecidesAll: manual siempre pausa y autom\xE1tico siempre la omite. No uses este campo para saltar reviews."),
|
|
34741
|
+
aiDecidesAll: z138.boolean().optional().describe("Mapea el toggle web \u201CDejar que la IA decida todo\u201D. Si true, ProductMaker omite reviews. Act\xEDvalo \xFAnicamente cuando el usuario lo pida expl\xEDcitamente. Omitido/false = reviews conversacionales."),
|
|
34456
34742
|
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.'),
|
|
34457
34743
|
videoStructure: VideoStructureEnum.default("single_take").describe('Opcional. Formato: "single_take" = Un solo clip (generado completo; puede incluir cambios de plano) o "multi_scene" = Multiescena. Default: single_take.'),
|
|
34458
34744
|
videoQuality: VideoQualityEnum.default("economy").describe('Opcional. Calidad: "economy" = Econ\xF3mico, "medium" = Medio o "pro" = Pro. El proveedor se elige internamente. Default: economy.'),
|
|
34459
34745
|
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).'),
|
|
34460
|
-
videoAngleCount:
|
|
34746
|
+
videoAngleCount: z138.number().int().min(1).max(3).optional().describe("Cantidad de videos a renderizar: 1, 2 o 3. REQUERIDO cuando outputs incluye video (o se omite outputs, cuyo default incluye video); om\xEDtelo s\xF3lo cuando no se generar\xE1 video. Preg\xFAntalo incluso con aiDecidesAll=true porque cambia el costo."),
|
|
34461
34747
|
angleNarratives: PublicNarrativeStyleEnum.array().max(3).optional().describe('Opcional. Narrativa POR \xC1NGULO (posicional). Valores actuales: "review", "ugc_how_to", "ugc_unboxing_chaos", "problem_product_cta" (problema concreto, producto como soluci\xF3n y CTA), "product_showcase", "wearable_showcase", "cinematic", "wild_card", "animated_problem_pixar", "sassy_product_pixar", "market" (narrativa an\xE1lisis de mercado: adapta el video a los patrones ganadores estudiados de la competencia; requiere competitorScanEnabled: true). angleNarratives[i] fuerza la narrativa del \xE1ngulo i; las posiciones restantes las elige la IA. La lista no puede superar videoAngleCount.'),
|
|
34462
|
-
outputs:
|
|
34748
|
+
outputs: z138.array(z138.enum([
|
|
34463
34749
|
"landing",
|
|
34464
34750
|
"video",
|
|
34465
34751
|
"image"
|
|
34466
34752
|
])).min(1).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). ${createProductTaskSingleOutputDescription}`),
|
|
34467
|
-
includeInitialLandingGif:
|
|
34468
|
-
competitorScanEnabled:
|
|
34753
|
+
includeInitialLandingGif: z138.boolean().optional().describe("Opcional. En nuevas landings cl\xE1sicas habilitadas, omitirlo incluye el primer GIF del producto y su coste en el total. Env\xEDa false si el usuario lo excluye: no se genera ni se reserva su coste. Conserva la elecci\xF3n al confirmar y al reintentar."),
|
|
34754
|
+
competitorScanEnabled: z138.boolean().optional().describe("Opcional. Default false: omitirlo significa SIN estudio de mercado. Antes de crear, llama estimate_market_study_cost con kind=product_task, muestra al usuario el costo con y sin estudio y preg\xFAntale si lo quiere; env\xEDa true solo si lo acepta (o si solicita la narrativa an\xE1lisis de mercado). Si la tarea incluye video, true analiza anuncios de la competencia y desbloquea la narrativa an\xE1lisis de mercado; si el estudio no encuentra datos suficientes, degrada al planner cl\xE1sico."),
|
|
34469
34755
|
generateAiEdit: AiEditRequestSchema.optional().describe('Opcional. Pide la Edici\xF3n con IA: una pasada que a\xF1ade texto, efectos y m\xFAsica sobre cada video entregado (variante with_ai_edit; se cobra al entregar, por video). Nivel: "economy" (Econ\xF3mica, Sonnet 5.5, m\xE1s barata) o "pro" (Opus 5.5, mejor calidad, ~10 min). true equivale a "pro"; false/omitido = sin edici\xF3n.'),
|
|
34470
|
-
replicateWinnerWhenFound:
|
|
34471
|
-
brandId:
|
|
34472
|
-
actorId:
|
|
34756
|
+
replicateWinnerWhenFound: z138.boolean().optional().describe("Conserva la preferencia de copiar un ganador si existe. false estudia el mercado sin reemplazar un \xE1ngulo por el anuncio ganador. No a\xF1ade una pregunta obligatoria."),
|
|
34757
|
+
brandId: z138.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."),
|
|
34758
|
+
actorId: z138.string().trim().min(1).max(120).optional().describe("Opcional. ID de un personaje guardado (usa list_actors para verlos). Si lo env\xEDas, su foto y su voz mandan sobre la descripci\xF3n de texto."),
|
|
34473
34759
|
creativeGender: CreativeGenderEnum.optional().describe('Opcional. QUI\xC9N APARECE EN PANTALLA en todos los creativos de la task: el presentador del video, las personas fotografiadas en la landing y la persona del anuncio. NO es la voz. M\xE1ndalo en cuanto el usuario lo diga en cualquier momento de la conversaci\xF3n y con cualquier redacci\xF3n ("quiero una mujer", "que salga un hombre", "esto es para hombres"): si no lo mandas, el g\xE9nero lo inventa la IA y el usuario recibe lo contrario de lo que pidi\xF3 sin ning\xFAn aviso. "mixed" solo cuando pida expl\xEDcitamente ambos. Si adem\xE1s mandas actorId, el personaje guardado manda sobre este campo.'),
|
|
34474
|
-
creativeDirection:
|
|
34475
|
-
headline:
|
|
34476
|
-
subheadline:
|
|
34760
|
+
creativeDirection: z138.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.'),
|
|
34761
|
+
headline: z138.string().max(120).optional().describe("Opcional. Titular EXACTO a renderizar. Se imprime tal cual: la IA no lo reescribe, no lo acorta y no lo traduce. Se aplica a las IM\xC1GENES y a la portada de la landing de esta task (el video no lo usa; para el video usa scriptNotes). \xDAsalo cuando el copy es de marca y no es negociable. M\xE1x 120 caracteres. Si lo omites, la IA escribe el titular."),
|
|
34762
|
+
subheadline: z138.string().max(160).optional().describe("Opcional. Subt\xEDtulo EXACTO a renderizar, misma regla que headline. Independiente: puedes fijar solo el subt\xEDtulo. M\xE1x 160 caracteres."),
|
|
34477
34763
|
landingStrategy: LandingStrategyEnum.optional().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".'),
|
|
34478
34764
|
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".')
|
|
34479
34765
|
}).strict();
|
|
34480
|
-
var CreateServiceVideoInput =
|
|
34481
|
-
landingUrl:
|
|
34482
|
-
additionalContext:
|
|
34766
|
+
var CreateServiceVideoInput = z138.object({
|
|
34767
|
+
landingUrl: z138.string().url().describe("URL p\xFAblica de la landing del servicio. Es suficiente para generar el video; los medios son opcionales en la web."),
|
|
34768
|
+
additionalContext: z138.string().max(3e3).optional().describe('Instrucci\xF3n natural prioritaria. Ej.: "adapta el ritmo de estos competidores", "enfatiza reservas de demos" o restricciones de claims.'),
|
|
34483
34769
|
language: Lang.describe("Idioma del video: es, en o pt."),
|
|
34484
|
-
voiceId:
|
|
34485
|
-
brandId:
|
|
34486
|
-
reviewMode:
|
|
34770
|
+
voiceId: z138.string().trim().min(1).pipe(VoiceId).optional().describe('Opcional. Voz para toda la narraci\xF3n. Usa "auto" para que la IA la alinee con el presentador. Las voces del banco compartido fueron retiradas.'),
|
|
34771
|
+
brandId: z138.string().uuid().optional().describe("Marca guardada del usuario. Aplica voz, mensajes, paleta y persona predeterminada."),
|
|
34772
|
+
reviewMode: z138.enum([
|
|
34487
34773
|
"automatic",
|
|
34488
34774
|
"guided"
|
|
34489
34775
|
]).default("automatic").describe("automatic = la IA elige concepto y guion sin pausas; guided = el usuario aprueba concepto y storyboard."),
|
|
34490
|
-
videoPreset:
|
|
34776
|
+
videoPreset: z138.enum([
|
|
34491
34777
|
"auto",
|
|
34492
34778
|
"ugc_dynamic",
|
|
34493
34779
|
"ugc_how_to",
|
|
@@ -34497,128 +34783,128 @@ var CreateServiceVideoInput = z137.object({
|
|
|
34497
34783
|
videoQuality: VideoQualityEnum.default("economy").describe('Opcional. Calidad: "economy" = Econ\xF3mico, "medium" = Medio o "pro" = Pro. El proveedor se elige internamente. Default: economy.'),
|
|
34498
34784
|
videoResolution: VideoResolutionEnum.default("480p").describe('Opcional. Resoluci\xF3n: "480p" o "720p". 720p cuesta m\xE1s por segundo y solo est\xE1 disponible en "economy" y "pro" (Medio = 768P). Default: 480p.')
|
|
34499
34785
|
}).strict();
|
|
34500
|
-
var GetTaskStatusInput =
|
|
34501
|
-
taskId:
|
|
34502
|
-
waitSeconds:
|
|
34786
|
+
var GetTaskStatusInput = z138.object({
|
|
34787
|
+
taskId: z138.string().min(1),
|
|
34788
|
+
waitSeconds: z138.number().int().min(0).max(120).optional()
|
|
34503
34789
|
});
|
|
34504
|
-
var ListTasksInput =
|
|
34505
|
-
limit:
|
|
34506
|
-
offset:
|
|
34507
|
-
search:
|
|
34790
|
+
var ListTasksInput = z138.object({
|
|
34791
|
+
limit: z138.number().int().min(1).max(50).optional(),
|
|
34792
|
+
offset: z138.number().int().min(0).optional(),
|
|
34793
|
+
search: z138.string().optional()
|
|
34508
34794
|
});
|
|
34509
34795
|
var MCP_REVIEW_LIST_FIELD_MAX = IMAGE_WIZARD_LIST_FIELD_MAX;
|
|
34510
34796
|
var MCP_REVIEW_FEATURE_ITEM_MAX = IMAGE_FEATURE_ITEM_MAX;
|
|
34511
|
-
var EditTaskDraftInput =
|
|
34512
|
-
taskId:
|
|
34513
|
-
angleIndex:
|
|
34514
|
-
angle:
|
|
34515
|
-
angle:
|
|
34516
|
-
buyerPersona:
|
|
34517
|
-
actor:
|
|
34518
|
-
actorId:
|
|
34519
|
-
gender:
|
|
34797
|
+
var EditTaskDraftInput = z138.object({
|
|
34798
|
+
taskId: z138.string().min(1),
|
|
34799
|
+
angleIndex: z138.number().int().min(0),
|
|
34800
|
+
angle: z138.object({
|
|
34801
|
+
angle: z138.string().min(1).max(480),
|
|
34802
|
+
buyerPersona: z138.string().min(1).max(360),
|
|
34803
|
+
actor: z138.string().min(1).max(360).optional(),
|
|
34804
|
+
actorId: z138.string().trim().min(1).optional().describe("Opcional. ID de un personaje guardado (usa list_actors para verlos). Si lo env\xEDas, su foto y su voz mandan sobre la descripci\xF3n de texto."),
|
|
34805
|
+
gender: z138.enum([
|
|
34520
34806
|
"female",
|
|
34521
34807
|
"male"
|
|
34522
34808
|
]).optional(),
|
|
34523
|
-
scriptNotes:
|
|
34809
|
+
scriptNotes: z138.string().max(2e3).optional()
|
|
34524
34810
|
})
|
|
34525
34811
|
});
|
|
34526
|
-
var PendingActionPayload =
|
|
34527
|
-
|
|
34528
|
-
kind:
|
|
34812
|
+
var PendingActionPayload = z138.discriminatedUnion("kind", [
|
|
34813
|
+
z138.object({
|
|
34814
|
+
kind: z138.literal("product_reference_set_approval"),
|
|
34529
34815
|
scope: ProductReferenceScopeEnum.optional(),
|
|
34530
|
-
scopeKey:
|
|
34531
|
-
scopeSnapshotId:
|
|
34532
|
-
expectedScopeHash:
|
|
34816
|
+
scopeKey: z138.string().min(1).max(120).optional(),
|
|
34817
|
+
scopeSnapshotId: z138.string().uuid().optional(),
|
|
34818
|
+
expectedScopeHash: z138.string().min(1).max(128).optional(),
|
|
34533
34819
|
decision: ProductReferenceResolutionDecisionEnum,
|
|
34534
34820
|
reason: ProductReferenceRejectionReasonEnum.optional(),
|
|
34535
|
-
affectedRefKeys:
|
|
34536
|
-
affectedPhotoNumbers:
|
|
34537
|
-
recoveryAction:
|
|
34821
|
+
affectedRefKeys: z138.array(z138.string().min(1).max(200)).min(1).optional(),
|
|
34822
|
+
affectedPhotoNumbers: z138.array(z138.number().int().positive()).min(1).optional(),
|
|
34823
|
+
recoveryAction: z138.enum([
|
|
34538
34824
|
"remove",
|
|
34539
34825
|
"replace",
|
|
34540
34826
|
"upload",
|
|
34541
34827
|
"return_to_selection"
|
|
34542
34828
|
]).optional()
|
|
34543
34829
|
}),
|
|
34544
|
-
|
|
34545
|
-
kind:
|
|
34546
|
-
selectedPhotoNumbers:
|
|
34830
|
+
z138.object({
|
|
34831
|
+
kind: z138.literal("product_reference_confirmation"),
|
|
34832
|
+
selectedPhotoNumbers: z138.array(z138.number().int().positive().max(5)).min(1).max(5).optional().describe("Opcional. N\xFAmeros visibles de las fotos que el usuario quiere conservar; las dem\xE1s se deseleccionan. Debe quedar al menos una."),
|
|
34547
34833
|
userChoice: ProductReferenceUserChoiceEnum.optional(),
|
|
34548
|
-
confirmedProductNames:
|
|
34834
|
+
confirmedProductNames: z138.array(z138.string().min(1).max(120)).min(2).max(8).optional(),
|
|
34549
34835
|
videoStructure: VideoStructureEnum.optional().describe("Opcional. Formato para reanudar el video: single_take (Un solo clip, generado completo; puede incluir cambios de plano) o multi_scene (Multiescena). El backend vuelve a validar que sea compatible con el agrupamiento confirmado."),
|
|
34550
34836
|
videoQuality: VideoQualityEnum.optional().describe("Opcional. Calidad para reanudar el video: economy (Econ\xF3mico), medium (Medio) o pro. El proveedor se elige internamente y el backend vuelve a cotizar la ruta."),
|
|
34551
|
-
useMultiSceneShowcase:
|
|
34837
|
+
useMultiSceneShowcase: z138.boolean().optional().describe("Compatibilidad con confirmaciones antiguas: si es true junto con userChoice=variants, conserva el showcase multiescena.")
|
|
34552
34838
|
}),
|
|
34553
|
-
|
|
34554
|
-
kind:
|
|
34839
|
+
z138.object({
|
|
34840
|
+
kind: z138.literal("angle_review_approval"),
|
|
34555
34841
|
// All edits are optional — empty action body just approves the
|
|
34556
34842
|
// backend-generated draft as-is. Field shapes mirror the angle-review
|
|
34557
34843
|
// request contracts in @packages/types/wizard.ts; the handler routes to
|
|
34558
34844
|
// the dedicated video/image/autopilot endpoint based on StatusResponse.kind.
|
|
34559
|
-
customAngle:
|
|
34845
|
+
customAngle: z138.string().min(1).max(480).optional(),
|
|
34560
34846
|
// Keep the shared MCP action within the strictest downstream contract:
|
|
34561
34847
|
// autopilot/image resume endpoints cap these fields at 1500 characters.
|
|
34562
|
-
painPoints:
|
|
34563
|
-
benefits:
|
|
34848
|
+
painPoints: z138.string().max(MCP_REVIEW_LIST_FIELD_MAX).optional(),
|
|
34849
|
+
benefits: z138.string().max(MCP_REVIEW_LIST_FIELD_MAX).optional(),
|
|
34564
34850
|
// Same policy as the two fields above: the strictest downstream contract
|
|
34565
34851
|
// wins. The image endpoint caps each feature at 240 while landing/video
|
|
34566
34852
|
// accept 300, and this single action routes to whichever the task is — so
|
|
34567
34853
|
// accepting 300 here turns a 250-character feature on an image task into
|
|
34568
34854
|
// an opaque HTTP 400 after the model already believed it had been saved.
|
|
34569
|
-
features:
|
|
34570
|
-
buyerPersona:
|
|
34855
|
+
features: z138.array(z138.string().min(1).max(MCP_REVIEW_FEATURE_ITEM_MAX)).max(10).optional(),
|
|
34856
|
+
buyerPersona: z138.string().min(1).max(360).optional(),
|
|
34571
34857
|
narrativeStyle: PublicNarrativeStyleEnum.optional().describe('Narrativa del video. "problem_product_cta" (Problema y Soluci\xF3n) funciona en ambos formatos \u2014 en Un solo clip abre sin producto y el producto entra por referencia en su beat. La familia storyteller ("animated_storyteller", "animated_inner_journey", "animated_object_soul", "animated_timeline") no est\xE1 disponible por ahora.'),
|
|
34572
|
-
actor:
|
|
34573
|
-
actorId:
|
|
34574
|
-
creativeDirection:
|
|
34575
|
-
selectedAngleNumbers:
|
|
34576
|
-
angleEdits:
|
|
34577
|
-
angleNumber:
|
|
34578
|
-
narrativeSelection:
|
|
34858
|
+
actor: z138.string().min(1).max(360).optional().describe("Nueva descripci\xF3n del presentador. Al enviarla sin actorId, se elimina autom\xE1ticamente cualquier actor guardado previo de esos \xE1ngulos."),
|
|
34859
|
+
actorId: z138.string().max(120).nullable().optional(),
|
|
34860
|
+
creativeDirection: z138.string().max(2e3).optional(),
|
|
34861
|
+
selectedAngleNumbers: z138.array(z138.number().int().positive()).min(1).max(3).optional().describe("N\xFAmeros visibles (1-based) de los \xE1ngulos que el usuario quiere renderizar. Deben ser \xFAnicos, existir en la review y no superar el videoAngleCount autorizado."),
|
|
34862
|
+
angleEdits: z138.array(z138.object({
|
|
34863
|
+
angleNumber: z138.number().int().positive(),
|
|
34864
|
+
narrativeSelection: z138.enum([
|
|
34579
34865
|
"market",
|
|
34580
34866
|
"guided"
|
|
34581
34867
|
]).optional().describe("Elecci\xF3n expl\xEDcita: market usa el estudio disponible; guided usa la narrativa elegida para este \xE1ngulo."),
|
|
34582
|
-
angle:
|
|
34583
|
-
buyerPersona:
|
|
34868
|
+
angle: z138.string().min(1).max(480).optional(),
|
|
34869
|
+
buyerPersona: z138.string().min(1).max(360).optional(),
|
|
34584
34870
|
narrativeStyle: PublicNarrativeStyleEnum.optional().describe('Narrativa del video. "problem_product_cta" (Problema y Soluci\xF3n) funciona en ambos formatos \u2014 en Un solo clip abre sin producto y el producto entra por referencia en su beat. La familia storyteller ("animated_storyteller", "animated_inner_journey", "animated_object_soul", "animated_timeline") no est\xE1 disponible por ahora.'),
|
|
34585
|
-
actor:
|
|
34586
|
-
actorId:
|
|
34587
|
-
creativeDirection:
|
|
34871
|
+
actor: z138.string().min(1).max(360).optional(),
|
|
34872
|
+
actorId: z138.string().max(120).nullable().optional(),
|
|
34873
|
+
creativeDirection: z138.string().max(2e3).optional()
|
|
34588
34874
|
}).strict()).max(3).optional().describe("Ediciones independientes por n\xFAmero visible de \xE1ngulo. S\xF3lo se aceptan para \xE1ngulos incluidos en selectedAngleNumbers (o en la selecci\xF3n por defecto)."),
|
|
34589
|
-
gapDurationSeconds:
|
|
34590
|
-
|
|
34591
|
-
|
|
34875
|
+
gapDurationSeconds: z138.union([
|
|
34876
|
+
z138.literal(15),
|
|
34877
|
+
z138.literal(30)
|
|
34592
34878
|
]).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.")
|
|
34593
34879
|
}),
|
|
34594
|
-
|
|
34595
|
-
kind:
|
|
34596
|
-
angleId:
|
|
34597
|
-
angleNumber:
|
|
34598
|
-
content:
|
|
34599
|
-
creativeDirection:
|
|
34600
|
-
actor:
|
|
34880
|
+
z138.object({
|
|
34881
|
+
kind: z138.literal("service_angle_approval"),
|
|
34882
|
+
angleId: z138.string().uuid().optional(),
|
|
34883
|
+
angleNumber: z138.number().int().positive().optional(),
|
|
34884
|
+
content: z138.string().min(1).max(480).optional(),
|
|
34885
|
+
creativeDirection: z138.string().min(1).max(1500).optional(),
|
|
34886
|
+
actor: z138.string().min(1).max(360).optional()
|
|
34601
34887
|
}),
|
|
34602
|
-
|
|
34603
|
-
kind:
|
|
34604
|
-
voiceovers:
|
|
34605
|
-
visualDirections:
|
|
34606
|
-
voiceoversByScene:
|
|
34607
|
-
visualDirectionsByScene:
|
|
34888
|
+
z138.object({
|
|
34889
|
+
kind: z138.literal("service_script_approval"),
|
|
34890
|
+
voiceovers: z138.record(z138.string(), z138.string().min(1).max(600)).optional(),
|
|
34891
|
+
visualDirections: z138.record(z138.string(), z138.string().min(1).max(1200)).optional(),
|
|
34892
|
+
voiceoversByScene: z138.record(z138.string(), z138.string().min(1).max(600)).optional(),
|
|
34893
|
+
visualDirectionsByScene: z138.record(z138.string(), z138.string().min(1).max(1200)).optional()
|
|
34608
34894
|
})
|
|
34609
34895
|
]);
|
|
34610
|
-
var ResolvePendingActionInput =
|
|
34611
|
-
taskId:
|
|
34896
|
+
var ResolvePendingActionInput = z138.object({
|
|
34897
|
+
taskId: z138.string().min(1),
|
|
34612
34898
|
action: PendingActionPayload
|
|
34613
34899
|
}).strict();
|
|
34614
34900
|
function resolveVariantConfirmVideoTier(action) {
|
|
34615
34901
|
return action.useMultiSceneShowcase && action.userChoice === "variants" ? "standard" : void 0;
|
|
34616
34902
|
}
|
|
34617
34903
|
__name(resolveVariantConfirmVideoTier, "resolveVariantConfirmVideoTier");
|
|
34618
|
-
var StatusForResolveSchema =
|
|
34904
|
+
var StatusForResolveSchema = z138.object({
|
|
34619
34905
|
// Metadata used to select the backend resume contract. Older API responses
|
|
34620
34906
|
// may omit these fields, so the resolver keeps the generic fallback.
|
|
34621
|
-
kind:
|
|
34907
|
+
kind: z138.enum([
|
|
34622
34908
|
"autopilot",
|
|
34623
34909
|
"video",
|
|
34624
34910
|
"image",
|
|
@@ -34626,18 +34912,18 @@ var StatusForResolveSchema = z137.object({
|
|
|
34626
34912
|
"mix",
|
|
34627
34913
|
"service"
|
|
34628
34914
|
]).optional(),
|
|
34629
|
-
selectedOutputs:
|
|
34915
|
+
selectedOutputs: z138.array(z138.enum([
|
|
34630
34916
|
"landing",
|
|
34631
34917
|
"video",
|
|
34632
34918
|
"image"
|
|
34633
34919
|
])).optional(),
|
|
34634
|
-
videoAngleCount:
|
|
34635
|
-
ugcFormat:
|
|
34920
|
+
videoAngleCount: z138.number().int().min(1).max(3).optional(),
|
|
34921
|
+
ugcFormat: z138.enum([
|
|
34636
34922
|
"single_8s",
|
|
34637
34923
|
"single_15s",
|
|
34638
34924
|
"double_30s"
|
|
34639
34925
|
]).optional(),
|
|
34640
|
-
videoStyle:
|
|
34926
|
+
videoStyle: z138.enum([
|
|
34641
34927
|
"ugc",
|
|
34642
34928
|
"cartoon_3d",
|
|
34643
34929
|
"product_only",
|
|
@@ -34647,13 +34933,13 @@ var StatusForResolveSchema = z137.object({
|
|
|
34647
34933
|
videoQuality: VideoQualityEnum.optional(),
|
|
34648
34934
|
videoRouteId: VideoRouteIdEnum.optional(),
|
|
34649
34935
|
videoModel: TaskVideoModelEnum.optional(),
|
|
34650
|
-
videoTier:
|
|
34936
|
+
videoTier: z138.enum([
|
|
34651
34937
|
"legacy",
|
|
34652
34938
|
"standard",
|
|
34653
34939
|
"pro",
|
|
34654
34940
|
"single_call"
|
|
34655
34941
|
]).optional(),
|
|
34656
|
-
videoResolution:
|
|
34942
|
+
videoResolution: z138.enum([
|
|
34657
34943
|
"480p",
|
|
34658
34944
|
"720p"
|
|
34659
34945
|
]).optional(),
|
|
@@ -34661,60 +34947,60 @@ var StatusForResolveSchema = z137.object({
|
|
|
34661
34947
|
// A task created before a narrative was blocked/hidden must still parse,
|
|
34662
34948
|
// or resolve_pending_action could never approve it.
|
|
34663
34949
|
narrativeStyle: NarrativeStyle.optional(),
|
|
34664
|
-
pauseForVideoScriptReview:
|
|
34665
|
-
pendingUserAction:
|
|
34666
|
-
kind:
|
|
34667
|
-
candidateGrouping:
|
|
34668
|
-
selectedImageUrls:
|
|
34669
|
-
proposedProductNames:
|
|
34950
|
+
pauseForVideoScriptReview: z138.boolean().optional(),
|
|
34951
|
+
pendingUserAction: z138.object({
|
|
34952
|
+
kind: z138.string(),
|
|
34953
|
+
candidateGrouping: z138.string().optional(),
|
|
34954
|
+
selectedImageUrls: z138.array(z138.string()).optional(),
|
|
34955
|
+
proposedProductNames: z138.array(z138.string()).optional(),
|
|
34670
34956
|
// Adaptive reference questions are server-owned. Echo only the evidence
|
|
34671
34957
|
// identity and the choices shown by the current snapshot; never accept a
|
|
34672
34958
|
// client-supplied reference set as authorization.
|
|
34673
|
-
options:
|
|
34674
|
-
mode:
|
|
34959
|
+
options: z138.array(ProductReferenceUserChoiceEnum).max(4).optional(),
|
|
34960
|
+
mode: z138.enum([
|
|
34675
34961
|
"question",
|
|
34676
34962
|
"visual_recovery"
|
|
34677
34963
|
]).optional(),
|
|
34678
34964
|
/** Hand-mirrored from packages/types result.ts — see CLAUDE.md § MCP Server Sync. */
|
|
34679
|
-
canKeepSelection:
|
|
34680
|
-
decisionKey:
|
|
34681
|
-
questionOrdinal:
|
|
34682
|
-
questionCeiling:
|
|
34683
|
-
|
|
34684
|
-
|
|
34965
|
+
canKeepSelection: z138.boolean().optional(),
|
|
34966
|
+
decisionKey: z138.string().optional(),
|
|
34967
|
+
questionOrdinal: z138.number().int().min(1).max(5).optional(),
|
|
34968
|
+
questionCeiling: z138.union([
|
|
34969
|
+
z138.literal(3),
|
|
34970
|
+
z138.literal(5)
|
|
34685
34971
|
]).optional(),
|
|
34686
|
-
referenceEvidenceVersion:
|
|
34687
|
-
expectedEvidenceHash:
|
|
34688
|
-
question:
|
|
34689
|
-
prompt:
|
|
34690
|
-
helpText:
|
|
34691
|
-
options:
|
|
34972
|
+
referenceEvidenceVersion: z138.number().int().positive().optional(),
|
|
34973
|
+
expectedEvidenceHash: z138.string().optional(),
|
|
34974
|
+
question: z138.object({
|
|
34975
|
+
prompt: z138.string().min(1).max(240),
|
|
34976
|
+
helpText: z138.string().min(1).max(300).optional(),
|
|
34977
|
+
options: z138.array(z138.object({
|
|
34692
34978
|
value: ProductReferenceUserChoiceEnum,
|
|
34693
|
-
label:
|
|
34694
|
-
description:
|
|
34979
|
+
label: z138.string().min(1).max(120),
|
|
34980
|
+
description: z138.string().min(1).max(240).optional()
|
|
34695
34981
|
})).min(2).max(4)
|
|
34696
34982
|
}).optional(),
|
|
34697
34983
|
scope: ProductReferenceScopeEnum.optional(),
|
|
34698
|
-
scopeKey:
|
|
34699
|
-
scopeSnapshotId:
|
|
34700
|
-
scopeHash:
|
|
34701
|
-
baseReferenceSetHash:
|
|
34702
|
-
phase:
|
|
34984
|
+
scopeKey: z138.string().optional(),
|
|
34985
|
+
scopeSnapshotId: z138.string().uuid().optional(),
|
|
34986
|
+
scopeHash: z138.string().optional(),
|
|
34987
|
+
baseReferenceSetHash: z138.string().optional(),
|
|
34988
|
+
phase: z138.enum([
|
|
34703
34989
|
"preparation",
|
|
34704
34990
|
"final"
|
|
34705
34991
|
]).optional(),
|
|
34706
|
-
selectedRefKeys:
|
|
34707
|
-
allowedDecisions:
|
|
34708
|
-
previews:
|
|
34709
|
-
refKey:
|
|
34992
|
+
selectedRefKeys: z138.array(z138.string().min(1).max(200)).optional(),
|
|
34993
|
+
allowedDecisions: z138.array(ProductReferenceResolutionDecisionEnum).optional(),
|
|
34994
|
+
previews: z138.array(z138.object({
|
|
34995
|
+
refKey: z138.string().min(1).max(200)
|
|
34710
34996
|
}).passthrough()).optional(),
|
|
34711
|
-
customAngleDraft:
|
|
34712
|
-
painPointsDraft:
|
|
34713
|
-
benefitsDraft:
|
|
34714
|
-
featuresDraft:
|
|
34715
|
-
angleOptions:
|
|
34716
|
-
index:
|
|
34717
|
-
angle:
|
|
34997
|
+
customAngleDraft: z138.string().optional(),
|
|
34998
|
+
painPointsDraft: z138.string().optional(),
|
|
34999
|
+
benefitsDraft: z138.string().optional(),
|
|
35000
|
+
featuresDraft: z138.array(z138.string()).optional(),
|
|
35001
|
+
angleOptions: z138.array(z138.object({
|
|
35002
|
+
index: z138.number().int().nonnegative(),
|
|
35003
|
+
angle: z138.string().min(1)
|
|
34718
35004
|
}).passthrough()).min(1).max(5).optional(),
|
|
34719
35005
|
// Recommendation consumed only when the caller explicitly sends
|
|
34720
35006
|
// gapDurationSeconds: 30 in the angle approval action.
|
|
@@ -34724,17 +35010,17 @@ var StatusForResolveSchema = z137.object({
|
|
|
34724
35010
|
// keys. `actorId` (the saved custom actor the picker stored) was silently
|
|
34725
35011
|
// dropped here, so the approval handler below could not echo it back on the
|
|
34726
35012
|
// PATCH — the selection looked accepted and reached no render.
|
|
34727
|
-
angles:
|
|
34728
|
-
id:
|
|
34729
|
-
content:
|
|
34730
|
-
creativeDirection:
|
|
34731
|
-
actor:
|
|
34732
|
-
actorId:
|
|
35013
|
+
angles: z138.array(z138.object({
|
|
35014
|
+
id: z138.string().uuid(),
|
|
35015
|
+
content: z138.string(),
|
|
35016
|
+
creativeDirection: z138.string(),
|
|
35017
|
+
actor: z138.string(),
|
|
35018
|
+
actorId: z138.string().nullable().optional()
|
|
34733
35019
|
}).passthrough()).optional(),
|
|
34734
|
-
panels:
|
|
34735
|
-
beatId:
|
|
34736
|
-
voiceover:
|
|
34737
|
-
visualDirection:
|
|
35020
|
+
panels: z138.array(z138.object({
|
|
35021
|
+
beatId: z138.string(),
|
|
35022
|
+
voiceover: z138.string(),
|
|
35023
|
+
visualDirection: z138.string()
|
|
34738
35024
|
}).passthrough()).optional()
|
|
34739
35025
|
}).optional()
|
|
34740
35026
|
}).passthrough();
|
|
@@ -35087,8 +35373,8 @@ ANTES de invocar, resume outputs, cantidad de videos y datos comerciales, explic
|
|
|
35087
35373
|
server.registerTool("verify_landing_commerce", {
|
|
35088
35374
|
description: getProductMakerCapabilityModelGuidance("verify_landing_commerce").summary + " Verifica una correcci\xF3n manual de precios, tachados o packs de una landing editable existente. Solo relee Shopify y los recibos externos; no cambia precios, inventario, SKU ni compra una edici\xF3n. Pasa taskId y editId de la edici\xF3n ya conocida; nunca inventes IDs ni pidas repetir el cambio. Devuelve el estado can\xF3nico por proveedor: conserva el aviso pendiente hasta confirmar los valores reales. Solo despu\xE9s de confirmar todos los valores, el servidor puede volver a habilitar la compra. Web guardada no demuestra publicaci\xF3n p\xFAblica ni checkout; muestra las correcciones y enlaces pendientes sin c\xF3digos t\xE9cnicos. La disponibilidad del editor conserva sus permisos actuales.",
|
|
35089
35375
|
inputSchema: {
|
|
35090
|
-
taskId:
|
|
35091
|
-
editId:
|
|
35376
|
+
taskId: z138.string().uuid(),
|
|
35377
|
+
editId: z138.string().uuid()
|
|
35092
35378
|
},
|
|
35093
35379
|
annotations: OVERWRITES_EXTERNAL
|
|
35094
35380
|
}, async (input) => {
|
|
@@ -35359,16 +35645,16 @@ ANTES de invocar, resume outputs, cantidad de videos y datos comerciales, explic
|
|
|
35359
35645
|
if (input.search) qs.set("search", input.search);
|
|
35360
35646
|
const path3 = `/v1/tasks${qs.toString() ? "?" + qs.toString() : ""}`;
|
|
35361
35647
|
const r = await api.getJson(path3);
|
|
35362
|
-
const page =
|
|
35363
|
-
items:
|
|
35648
|
+
const page = z138.object({
|
|
35649
|
+
items: z138.array(z138.unknown())
|
|
35364
35650
|
}).passthrough().safeParse(r);
|
|
35365
35651
|
if (!page.success) return asText(r);
|
|
35366
35652
|
return asText({
|
|
35367
35653
|
...page.data,
|
|
35368
35654
|
items: page.data.items.map((item) => {
|
|
35369
|
-
const servicePage =
|
|
35370
|
-
taskId:
|
|
35371
|
-
resourceType:
|
|
35655
|
+
const servicePage = z138.object({
|
|
35656
|
+
taskId: z138.string().uuid(),
|
|
35657
|
+
resourceType: z138.literal("service_page")
|
|
35372
35658
|
}).passthrough().safeParse(item);
|
|
35373
35659
|
if (!servicePage.success) return item;
|
|
35374
35660
|
return {
|
|
@@ -35858,28 +36144,28 @@ ANTES de invocar, resume outputs, cantidad de videos y datos comerciales, explic
|
|
|
35858
36144
|
__name(registerTaskTools, "registerTaskTools");
|
|
35859
36145
|
|
|
35860
36146
|
// src/tools/brands.ts
|
|
35861
|
-
import { z as
|
|
36147
|
+
import { z as z139 } from "zod";
|
|
35862
36148
|
var guidance2 = getProductMakerCapabilityModelGuidance("create_brand");
|
|
35863
36149
|
var BRAND_START_REPLAY_WINDOW_MS = 15 * 60 * 1e3;
|
|
35864
36150
|
var BRAND_NOTES_MAX_CHARS = 2e4;
|
|
35865
36151
|
var BRAND_AMBIGUOUS_REQUEST_RETENTION_MS = 24 * 60 * 60 * 1e3;
|
|
35866
36152
|
var BRAND_CREATION_REQUEST_LIMIT = 500;
|
|
35867
36153
|
var brandCreationRequests = /* @__PURE__ */ new Map();
|
|
35868
|
-
var CreateBrandInput =
|
|
35869
|
-
action:
|
|
36154
|
+
var CreateBrandInput = z139.object({
|
|
36155
|
+
action: z139.enum([
|
|
35870
36156
|
"start",
|
|
35871
36157
|
"status",
|
|
35872
36158
|
"list"
|
|
35873
36159
|
]).default("start").describe("start cotiza (sin confirmed) o inicia (confirmed=true) el an\xE1lisis; status consulta brandId hasta ready o failed; list muestra las marcas que el usuario ya tiene."),
|
|
35874
|
-
sourceUrl:
|
|
35875
|
-
businessNotes:
|
|
35876
|
-
language:
|
|
36160
|
+
sourceUrl: z139.string().trim().min(1).max(BRAND_SOURCE_URL_MAX_LENGTH).optional().describe("Instagram, Facebook, TikTok o web p\xFAblica del negocio, como la dio el usuario (\xAB@sanlucasbarberia\xBB, \xABinstagram.com/sanlucasbarberia\xBB, \xABmibarberia.com\xBB). Obligatorio para start."),
|
|
36161
|
+
businessNotes: z139.string().trim().max(BRAND_NOTES_MAX_CHARS).optional().describe("Opcional. Lo que el usuario cuente de su negocio en el chat (servicios y precios, WhatsApp, ciudad o zona, horarios). Se guarda como documento del negocio y llega tal cual a la marca y a su landing. Copia sus palabras; no inventes datos."),
|
|
36162
|
+
language: z139.enum([
|
|
35877
36163
|
"es",
|
|
35878
36164
|
"en",
|
|
35879
36165
|
"pt"
|
|
35880
36166
|
]).optional().describe("Idioma de la conversaci\xF3n. Solo se usa si la p\xE1gina del negocio no deja claro su idioma."),
|
|
35881
|
-
confirmed:
|
|
35882
|
-
brandId:
|
|
36167
|
+
confirmed: z139.boolean().optional().describe("Solo true despu\xE9s de mostrar el precio y que el usuario acepte. Sin confirmed, start solo cotiza y no cobra."),
|
|
36168
|
+
brandId: z139.string().uuid().optional().describe("ID de la marca. Obligatorio para status; no lo inventes.")
|
|
35883
36169
|
}).strict();
|
|
35884
36170
|
function normalizeBrandSourceUrl(raw) {
|
|
35885
36171
|
const value = raw.trim();
|
|
@@ -36163,24 +36449,24 @@ __name(registerBrandTools, "registerBrandTools");
|
|
|
36163
36449
|
|
|
36164
36450
|
// src/tools/service-landing.ts
|
|
36165
36451
|
import { createHash as createHash4, randomUUID } from "crypto";
|
|
36166
|
-
import { z as
|
|
36452
|
+
import { z as z140 } from "zod";
|
|
36167
36453
|
var guidance3 = getProductMakerCapabilityModelGuidance("create_service_landing");
|
|
36168
36454
|
var SERVICE_LANDING_KEY_NAMESPACE = "6f1d8c2a-3b4e-5f60-8a7b-9c0d1e2f3a4b";
|
|
36169
36455
|
var SERVICE_LANDING_INFO_MAX_CHARS = 2e4;
|
|
36170
|
-
var CreateServiceLandingInput =
|
|
36171
|
-
action:
|
|
36456
|
+
var CreateServiceLandingInput = z140.object({
|
|
36457
|
+
action: z140.enum([
|
|
36172
36458
|
"prepare",
|
|
36173
36459
|
"build",
|
|
36174
36460
|
"status",
|
|
36175
36461
|
"publish"
|
|
36176
36462
|
]).default("prepare").describe("prepare (gratis) crea o retoma la landing de la marca y da el precio; build (pagado, confirmed=true) genera la p\xE1gina directamente con la marca y los datos guardados; status sigue el avance; publish la publica y devuelve su enlace."),
|
|
36177
|
-
brandId:
|
|
36178
|
-
servicePageId:
|
|
36179
|
-
businessInfo:
|
|
36180
|
-
confirmed:
|
|
36181
|
-
maxCredits:
|
|
36182
|
-
operationId:
|
|
36183
|
-
slug:
|
|
36463
|
+
brandId: z140.string().uuid().optional().describe("ID de la marca (de create_brand). Obligatorio para prepare. Nunca un taskId de producto."),
|
|
36464
|
+
servicePageId: z140.string().uuid().optional().describe("ID de la landing que devolvi\xF3 prepare. Obligatorio para build, status y publish."),
|
|
36465
|
+
businessInfo: z140.string().trim().max(SERVICE_LANDING_INFO_MAX_CHARS).optional().describe("Opcional. Lo que el usuario cont\xF3 del negocio y no estaba en su marca: servicios y precios, WhatsApp o tel\xE9fono, ciudad o zona, horarios. Copia sus palabras; no inventes datos."),
|
|
36466
|
+
confirmed: z140.boolean().optional().describe("Solo true despu\xE9s de mostrar el precio total y que el usuario acepte. Obligatorio para build."),
|
|
36467
|
+
maxCredits: z140.number().int().positive().optional().describe("El total en cr\xE9ditos que el usuario acept\xF3 (el que devolvi\xF3 prepare). Obligatorio para build: ninguna etapa se inicia si su precio supera este total."),
|
|
36468
|
+
operationId: z140.string().uuid().optional().describe("Opcional en status: la operaci\xF3n que devolvi\xF3 build."),
|
|
36469
|
+
slug: z140.string().trim().toLowerCase().regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/).max(64).optional().describe("Opcional en publish: la direcci\xF3n corta de la p\xE1gina (ej. sanlucas-barberia). Si falta se usa la sugerida desde el nombre del negocio.")
|
|
36184
36470
|
}).strict();
|
|
36185
36471
|
function serviceKey(...parts) {
|
|
36186
36472
|
return deriveUuidV5(parts.join(":"), SERVICE_LANDING_KEY_NAMESPACE);
|
|
@@ -36377,7 +36663,11 @@ function registerServiceLandingTools(server, api) {
|
|
|
36377
36663
|
server.registerTool("create_service_landing", {
|
|
36378
36664
|
description: guidance3.summary + " Parte del brandId de create_brand y termina con el enlace p\xFAblico de la p\xE1gina. NO es para productos de dropshipping (para eso generate_landing / create_product_task). Flujo: 1) action=prepare con brandId (gratis): prepara la landing y devuelve el precio estimado. 2) Pregunta una sola vez los datos que falten; puede omitirlos. 3) Cuando acepte el precio, action=build con servicePageId, confirmed=true, maxCredits y businessInfo: inicia una sola generaci\xF3n con la marca y las fuentes guardadas, sin una lectura pagada previa. 4) action=status hasta generated o error; generando se sigue en Tareas y contin\xFAa aunque cierre el chat. No llames build para continuar una etapa. 5) Muestra el enlace para verla y, si acepta, action=publish. Repetir una llamada no cobra dos veces. Con el enlace publicado sigue create_animated_video si quiere un video y create_service_images con el mismo brandId si quiere im\xE1genes.",
|
|
36379
36665
|
inputSchema: CreateServiceLandingInput.shape,
|
|
36380
|
-
|
|
36666
|
+
// action=publish exposes the page to the public internet.
|
|
36667
|
+
annotations: {
|
|
36668
|
+
...CREATES_PAID,
|
|
36669
|
+
openWorldHint: true
|
|
36670
|
+
}
|
|
36381
36671
|
}, async (rawInput) => {
|
|
36382
36672
|
try {
|
|
36383
36673
|
const input = CreateServiceLandingInput.parse(rawInput);
|
|
@@ -36555,7 +36845,7 @@ function registerServiceLandingTools(server, api) {
|
|
|
36555
36845
|
__name(registerServiceLandingTools, "registerServiceLandingTools");
|
|
36556
36846
|
|
|
36557
36847
|
// src/tools/service-images.ts
|
|
36558
|
-
import { z as
|
|
36848
|
+
import { z as z141 } from "zod";
|
|
36559
36849
|
var guidance4 = getProductMakerCapabilityModelGuidance("create_service_images");
|
|
36560
36850
|
var SERVICE_IMAGE_FORMATS = [
|
|
36561
36851
|
"4:5",
|
|
@@ -36568,23 +36858,23 @@ var LANGUAGE_TAGS = {
|
|
|
36568
36858
|
en: "en-US",
|
|
36569
36859
|
pt: "pt-BR"
|
|
36570
36860
|
};
|
|
36571
|
-
var CreateServiceImagesInput =
|
|
36572
|
-
brandId:
|
|
36573
|
-
instruction:
|
|
36574
|
-
delegateToAi:
|
|
36575
|
-
format:
|
|
36576
|
-
count:
|
|
36577
|
-
quality:
|
|
36861
|
+
var CreateServiceImagesInput = z141.object({
|
|
36862
|
+
brandId: z141.string().uuid().optional().describe("ID de un servicio propio y listo (de create_brand), obligatorio para cotizar y crear im\xE1genes nuevas. En un retry hist\xF3rico ya confirmado que originalmente no ten\xEDa este campo, conserva la petici\xF3n original. Nunca un taskId de producto."),
|
|
36863
|
+
instruction: z141.string().trim().max(4e3).optional().describe("Opcional. Qu\xE9 servicio o promoci\xF3n mostrar, con las palabras del usuario (ej. \xABcorte + barba a $35.000 los martes\xBB). Si se omite, la IA propone la imagen usando la informaci\xF3n del servicio."),
|
|
36864
|
+
delegateToAi: z141.boolean().optional().describe("true si el usuario prefiere que la IA elija qu\xE9 mostrar a partir de su servicio. Tambi\xE9n se delega cuando instruction est\xE1 vac\xEDo."),
|
|
36865
|
+
format: z141.enum(SERVICE_IMAGE_FORMATS).optional().describe('Formato: "4:5" (feed, por defecto), "9:16" (historias/reels) o "1:1" (cuadrado).'),
|
|
36866
|
+
count: z141.number().int().min(1).max(SERVICE_IMAGE_MAX_COUNT).optional().describe("Cu\xE1ntas im\xE1genes, de 1 a 3 (por defecto 1)."),
|
|
36867
|
+
quality: z141.enum([
|
|
36578
36868
|
"standard",
|
|
36579
36869
|
"high"
|
|
36580
36870
|
]).optional().describe('Calidad: "standard" (por defecto) o "high" (m\xE1s cara; no disponible en 4:5).'),
|
|
36581
|
-
language:
|
|
36871
|
+
language: z141.enum([
|
|
36582
36872
|
"es",
|
|
36583
36873
|
"en",
|
|
36584
36874
|
"pt"
|
|
36585
36875
|
]).optional().describe("Idioma de los textos de la imagen (por defecto es)."),
|
|
36586
|
-
adCopy:
|
|
36587
|
-
confirmed:
|
|
36876
|
+
adCopy: z141.boolean().optional().describe("Compatibilidad con clientes anteriores: el copy para publicar siempre est\xE1 incluido en cada imagen de servicios."),
|
|
36877
|
+
confirmed: z141.boolean().optional().describe("Solo true despu\xE9s de mostrar el precio y que el usuario acepte. Sin confirmed, solo cotiza y no cobra.")
|
|
36588
36878
|
}).strict();
|
|
36589
36879
|
function serviceImageModel(quality) {
|
|
36590
36880
|
return quality === "high" ? AD_CREATIVE_MODELS.PREMIUM : AD_CREATIVE_MODELS.STANDARD;
|
|
@@ -36701,7 +36991,7 @@ function registerServiceImageTools(server, api, options = {}) {
|
|
|
36701
36991
|
nextAction: "Dile al usuario que est\xE1s creando sus im\xE1genes. Llama get_task_status con este taskId (waitSeconds=45) hasta done (trae las im\xE1genes reales de ProductMaker) o error. Muestra los recursos de imagen devueltos directamente en el chat. Nunca generes ni recrees una imagen sustituta con otra herramienta. Si la vista previa falla, explica el fallo y conserva los enlaces protegidos devueltos."
|
|
36702
36992
|
});
|
|
36703
36993
|
} catch (error) {
|
|
36704
|
-
if (error instanceof
|
|
36994
|
+
if (error instanceof z141.ZodError) throw invalidInputFromZod(error);
|
|
36705
36995
|
throw translateError(error);
|
|
36706
36996
|
}
|
|
36707
36997
|
});
|
|
@@ -36709,7 +36999,7 @@ function registerServiceImageTools(server, api, options = {}) {
|
|
|
36709
36999
|
__name(registerServiceImageTools, "registerServiceImageTools");
|
|
36710
37000
|
|
|
36711
37001
|
// src/tools/creatives.ts
|
|
36712
|
-
import { z as
|
|
37002
|
+
import { z as z142 } from "zod";
|
|
36713
37003
|
var generateFreeVideoGuidance = getProductMakerCapabilityModelGuidance("generate_free_video");
|
|
36714
37004
|
var generateVideoCreativeGuidance = getProductMakerCapabilityModelGuidance("generate_video_creative");
|
|
36715
37005
|
var generateImageCreativesGuidance = getProductMakerCapabilityModelGuidance("generate_image_creatives");
|
|
@@ -36720,72 +37010,72 @@ var CreativeRefs = {
|
|
|
36720
37010
|
images: ImagesInput,
|
|
36721
37011
|
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.'),
|
|
36722
37012
|
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.'),
|
|
36723
|
-
price:
|
|
36724
|
-
offer:
|
|
36725
|
-
compareAtPrice:
|
|
36726
|
-
productContext:
|
|
36727
|
-
productLabel:
|
|
36728
|
-
buyerPersona:
|
|
36729
|
-
isCombo:
|
|
36730
|
-
competitorScanEnabled:
|
|
36731
|
-
brandId:
|
|
37013
|
+
price: z142.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.'),
|
|
37014
|
+
offer: z142.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.'),
|
|
37015
|
+
compareAtPrice: z142.string().min(1).max(120).optional().describe('Opcional. Precio anterior o de referencia para mostrar descuento. Ej. "$129.000" cuando price="$89.900".'),
|
|
37016
|
+
productContext: z142.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."),
|
|
37017
|
+
productLabel: z142.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.'),
|
|
37018
|
+
buyerPersona: z142.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.'),
|
|
37019
|
+
isCombo: z142.boolean().optional().describe("Opcional. Si true, el creativo presenta el producto como combo/pack. Default false."),
|
|
37020
|
+
competitorScanEnabled: z142.boolean().optional().describe("Opcional. Default false: omitirlo significa SIN estudio de mercado. Antes de crear, llama estimate_market_study_cost, muestra al usuario el costo con y sin estudio y preg\xFAntale si lo quiere; env\xEDa true solo si lo acepta (o si solicita la narrativa an\xE1lisis de mercado). En video, true analiza anuncios de la competencia y desbloquea la narrativa an\xE1lisis de mercado; si el estudio no encuentra datos suficientes, degrada al planner cl\xE1sico."),
|
|
37021
|
+
brandId: z142.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.")
|
|
36732
37022
|
};
|
|
36733
|
-
var VideoPresenter =
|
|
36734
|
-
|
|
36735
|
-
mode:
|
|
37023
|
+
var VideoPresenter = z142.discriminatedUnion("mode", [
|
|
37024
|
+
z142.object({
|
|
37025
|
+
mode: z142.literal("none")
|
|
36736
37026
|
}).strict(),
|
|
36737
|
-
|
|
36738
|
-
mode:
|
|
36739
|
-
actorId:
|
|
37027
|
+
z142.object({
|
|
37028
|
+
mode: z142.literal("saved_actor"),
|
|
37029
|
+
actorId: z142.string().trim().min(1).max(120).describe("ID devuelto por list_actors para un actor guardado del usuario.")
|
|
36740
37030
|
}).strict(),
|
|
36741
|
-
|
|
36742
|
-
mode:
|
|
36743
|
-
description:
|
|
37031
|
+
z142.object({
|
|
37032
|
+
mode: z142.literal("described"),
|
|
37033
|
+
description: z142.string().trim().min(1).max(500).describe("Descripci\xF3n concreta de la persona que presenta el producto.")
|
|
36744
37034
|
}).strict()
|
|
36745
37035
|
]);
|
|
36746
|
-
var VideoCreativeSetup =
|
|
36747
|
-
|
|
36748
|
-
mode:
|
|
37036
|
+
var VideoCreativeSetup = z142.discriminatedUnion("mode", [
|
|
37037
|
+
z142.object({
|
|
37038
|
+
mode: z142.literal("automatic").describe('S\xF3lo cuando el usuario pidi\xF3 expl\xEDcitamente "decide todo".')
|
|
36749
37039
|
}).strict(),
|
|
36750
|
-
|
|
36751
|
-
mode:
|
|
37040
|
+
z142.object({
|
|
37041
|
+
mode: z142.literal("guided"),
|
|
36752
37042
|
videoPreset: VideoPresetEnum.default("auto").describe('Narrativa opcional. Con estudio de mercado no la preguntes: se usa "auto" y el estudio define la estructura. Sin estudio conserva la elecci\xF3n del usuario. Todas las narrativas funcionan en Un solo clip y Multiescena.'),
|
|
36753
|
-
buyerPersona:
|
|
37043
|
+
buyerPersona: z142.string().trim().min(1).max(360),
|
|
36754
37044
|
animationStyle: AnimationStyleEnum.optional().describe('Opcional. Estilo visual SOLO para presets animados ("pixar_problem" o "pixar_sassy") \u2014 la API lo rechaza con cualquier otro preset. Opciones: "pixar_3d" (default, look 3D de pel\xEDcula animada), "claymation" (plastilina stop-motion artesanal), "wes_anderson" (tableau sim\xE9trico pastel), "sci_fi" (futurista con HUDs y ne\xF3n), "realista" (fotorrealista, fiel al producto). Si el usuario pide un look animado sin nombrar el estilo, omite el campo.'),
|
|
36755
37045
|
presenter: VideoPresenter,
|
|
36756
|
-
creativeDirection:
|
|
37046
|
+
creativeDirection: z142.string().trim().min(1).max(500).nullable().describe("Direcci\xF3n visual elegida. null significa que el usuario deleg\xF3 expl\xEDcitamente esta decisi\xF3n.")
|
|
36757
37047
|
}).strict()
|
|
36758
37048
|
]);
|
|
36759
|
-
var VideoInput =
|
|
37049
|
+
var VideoInput = z142.object({
|
|
36760
37050
|
images: CreativeRefs.images.optional(),
|
|
36761
37051
|
winnerReference: WinnerReferenceInput.optional().describe(WINNER_IMAGE_CHOICE_GUIDANCE),
|
|
36762
|
-
title:
|
|
36763
|
-
primaryAngle:
|
|
37052
|
+
title: z142.string().min(1).describe('T\xEDtulo corto del producto. Aparece en la primera vista del video. Ej. "Crema Anti-Edad Premium".'),
|
|
37053
|
+
primaryAngle: z142.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".'),
|
|
36764
37054
|
language: CreativeRefs.language,
|
|
36765
37055
|
country: CreativeRefs.country,
|
|
36766
|
-
aspectRatio:
|
|
37056
|
+
aspectRatio: z142.enum([
|
|
36767
37057
|
"9:16",
|
|
36768
37058
|
"16:9",
|
|
36769
37059
|
"Auto"
|
|
36770
37060
|
]).describe('Relaci\xF3n de aspecto. "9:16" = vertical (TikTok/Reels \u2014 default recomendado para ads). "16:9" = horizontal (YouTube). "Auto" = decide la IA.'),
|
|
36771
37061
|
generateAiEdit: AiEditRequestSchema.optional().describe('Opcional. Pide la Edici\xF3n con IA: una pasada que a\xF1ade texto, efectos y m\xFAsica sobre cada video entregado (variante with_ai_edit; se cobra al entregar, por video). Nivel: "economy" (Econ\xF3mica, Sonnet 5.5, m\xE1s barata) o "pro" (Opus 5.5, mejor calidad, ~10 min). true equivale a "pro"; false/omitido = sin edici\xF3n.'),
|
|
36772
|
-
durationSeconds:
|
|
36773
|
-
videoAngleCount:
|
|
37062
|
+
durationSeconds: z142.number().int().min(4).max(30).default(15).describe("Opcional. Duraci\xF3n objetivo: Un solo clip Econ\xF3mico o Medio usa 15s; Un solo clip Pro usa 15s o 30s; Multiescena usa 15s o 30s totales. La combinaci\xF3n de formato y calidad determina la ruta y el costo."),
|
|
37063
|
+
videoAngleCount: z142.number().int().min(1).max(3).describe("Cantidad de videos a renderizar: 1, 2 o 3. Es una decisi\xF3n de costo obligatoria: preg\xFAntala incluso cuando creativeSetup.mode=automatic."),
|
|
36774
37064
|
angleNarratives: PublicNarrativeStyleEnum.array().max(3).optional().describe('Opcional. Narrativa POR \xC1NGULO (posicional). Valores actuales: "review", "ugc_how_to", "ugc_unboxing_chaos", "problem_product_cta" (problema concreto, producto como soluci\xF3n y CTA), "product_showcase", "wearable_showcase", "cinematic", "wild_card", "animated_problem_pixar", "sassy_product_pixar", "market" (narrativa an\xE1lisis de mercado: adapta el video a los patrones ganadores estudiados de la competencia; requiere competitorScanEnabled: true). angleNarratives[i] fuerza la narrativa del \xE1ngulo i; las posiciones restantes las elige la IA. La lista no puede superar videoAngleCount.'),
|
|
36775
37065
|
creativeSetup: VideoCreativeSetup.describe("Configuraci\xF3n creativa obligatoria. En guided pregunta narrativa, p\xFAblico, presentador y direcci\xF3n antes de confirmar. Usa automatic s\xF3lo si el usuario deleg\xF3 todo expl\xEDcitamente."),
|
|
36776
37066
|
videoStructure: VideoStructureEnum.default("single_take").describe('Opcional. Formato: "single_take" = Un solo clip (generado completo; puede incluir cambios de plano) o "multi_scene" = Multiescena (varias tomas concatenadas). Default: single_take.'),
|
|
36777
37067
|
videoQuality: VideoQualityEnum.default("economy").describe('Opcional. Calidad: "economy" = Econ\xF3mico, "medium" = Medio o "pro" = Pro. El sistema elige el proveedor internamente. Default: economy.'),
|
|
36778
37068
|
videoResolution: VideoResolutionEnum.optional().describe('Opcional. Resoluci\xF3n del render: "480p" o "720p". Si se omite, se usa 480p; elige 720p expl\xEDcitamente si necesitas m\xE1s nitidez.'),
|
|
36779
|
-
features:
|
|
37069
|
+
features: z142.array(z142.string()).optional().describe('Opcional. Lista de features/beneficios clave a mencionar. Ej. ["resistente al agua", "bater\xEDa 24h", "carga inal\xE1mbrica"].'),
|
|
36780
37070
|
price: CreativeRefs.price,
|
|
36781
37071
|
offer: CreativeRefs.offer,
|
|
36782
37072
|
productContext: CreativeRefs.productContext,
|
|
36783
37073
|
productLabel: CreativeRefs.productLabel,
|
|
36784
|
-
cta:
|
|
36785
|
-
scriptNotes:
|
|
37074
|
+
cta: z142.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.'),
|
|
37075
|
+
scriptNotes: z142.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: creativeSetup.mode=guided activa siempre la revisi\xF3n palabra-por-palabra con get_video_script / approve_video_script.'),
|
|
36786
37076
|
creativeGender: CreativeGenderEnum.optional().describe('Opcional. QUI\xC9N APARECE EN PANTALLA en el video: mujer ("female"), hombre ("male") o ambos ("mixed"). NO es la voz; cuando se fija, la voz se alinea sola con el personaje. M\xE1ndalo en cuanto el usuario diga qui\xE9n debe SALIR en el video, con cualquier redacci\xF3n ("quiero una mujer", "que salga un hombre", "protagonista femenina"): si no lo mandas, el g\xE9nero lo inventa la IA y el usuario puede recibir lo contrario de lo que pidi\xF3 sin ning\xFAn aviso. NO lo mandes cuando hable del P\xDABLICO COMPRADOR ("esto es para hombres", "mi p\xFAblico son mujeres", "va dirigido a mam\xE1s"): eso es a qui\xE9n se le vende, no qui\xE9n act\xFAa, y fijarlo ah\xED pinea un protagonista que nadie pidi\xF3. "mixed" solo cuando pida expl\xEDcitamente ambos, y ten en cuenta que no garantiza una mezcla: no fija ning\xFAn g\xE9nero y cada creativo elige su protagonista. No lo preguntes como paso obligatorio. Si creativeSetup.presenter.mode="saved_actor", el personaje guardado manda sobre este campo.'),
|
|
36787
37077
|
competitorScanEnabled: CreativeRefs.competitorScanEnabled,
|
|
36788
|
-
replicateWinnerWhenFound:
|
|
37078
|
+
replicateWinnerWhenFound: z142.boolean().optional().describe("Conserva la preferencia de copiar un ganador si existe. false permite estudiar el mercado sin reemplazar un \xE1ngulo por el anuncio ganador. No a\xF1adas una pregunta obligatoria para este campo."),
|
|
36789
37079
|
brandId: CreativeRefs.brandId
|
|
36790
37080
|
}).strict();
|
|
36791
37081
|
var FreeVideoQualityEnum = VideoQualityEnum;
|
|
@@ -36803,7 +37093,7 @@ var FREE_VIDEO_DURATIONS_BY_QUALITY = {
|
|
|
36803
37093
|
length: 29
|
|
36804
37094
|
}, (_, index) => index + 2)
|
|
36805
37095
|
};
|
|
36806
|
-
var FreeVideoModelIdEnum =
|
|
37096
|
+
var FreeVideoModelIdEnum = z142.enum([
|
|
36807
37097
|
"grok-imagine-video-1-5-preview",
|
|
36808
37098
|
"minimax-h3/reference-to-video",
|
|
36809
37099
|
"wan/3-0-video",
|
|
@@ -36814,66 +37104,66 @@ var FREE_SELECTABLE_MODEL_IDS = [
|
|
|
36814
37104
|
"minimax-h3/reference-to-video",
|
|
36815
37105
|
"wan/3-0-video"
|
|
36816
37106
|
];
|
|
36817
|
-
var FreeVideoInput =
|
|
37107
|
+
var FreeVideoInput = z142.object({
|
|
36818
37108
|
images: ImagesInput.optional().describe("Opcional. 1-5 fotos del producto. Om\xEDtelo para generar desde texto puro."),
|
|
36819
|
-
rawPrompt:
|
|
37109
|
+
rawPrompt: z142.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."),
|
|
36820
37110
|
language: CreativeRefs.language,
|
|
36821
37111
|
quality: FreeVideoQualityEnum.describe('Calidad del video: "economy" = Econ\xF3mico, "medium" = Medio o "pro" = Pro (Wan).'),
|
|
36822
37112
|
// Retired in MCP 7.0.0 (Task 11): kept in the schema, accepted, and
|
|
36823
37113
|
// parsed ONLY so the handler can reject it with a message naming its
|
|
36824
37114
|
// replacement — dropping it silently would turn a caller's explicit
|
|
36825
37115
|
// Pro++ pick into a quiet fallback to Wan.
|
|
36826
|
-
freeProModel:
|
|
37116
|
+
freeProModel: z142.enum([
|
|
36827
37117
|
"wan",
|
|
36828
37118
|
"seedance"
|
|
36829
37119
|
]).optional().describe("ELIMINADO en MCP 7.0.0 \u2014 usa `modelId`. Enviar este campo produce un error."),
|
|
36830
37120
|
modelId: FreeVideoModelIdEnum.optional().describe('Opcional. Elige expl\xEDcitamente el modelo del registro en vez del default de la calidad: "grok-imagine-video-1-5-preview" (Grok 1.5), "minimax-h3/reference-to-video" (H3 Max) o "wan/3-0-video" (Wan 3.0). Om\xEDtelo para usar el modelo por defecto de la calidad elegida. El modelo determina la duraci\xF3n permitida (Grok 6/8/10/15s, H3 Max 4-15s, Wan 2-30s) y el precio. "bytedance/seedance-2-5" est\xE1 inactivo y se rechaza con un error claro, nunca un 500.'),
|
|
36831
|
-
durationSeconds:
|
|
37121
|
+
durationSeconds: z142.number().int().min(2).max(30).describe("Duraci\xF3n exacta. Econ\xF3mico: 6, 8, 10 o 15s; Medio: enteros de 4 a 15s; Pro Wan: 2 a 30s; Pro++ Seedance: 4 a 30s."),
|
|
36832
37122
|
videoResolution: VideoResolutionEnum.describe("Resoluci\xF3n del video: 480p o 720p."),
|
|
36833
|
-
generateAiMusic:
|
|
37123
|
+
generateAiMusic: z142.boolean().default(false).describe('Opcional. Agrega m\xFAsica instrumental IA debajo del audio nativo. Default false. Equivale a generateAiEdit (nivel "pro"): la Edici\xF3n con IA trae su propia m\xFAsica.'),
|
|
36834
37124
|
generateAiEdit: AiEditRequestSchema.optional().describe('Opcional. Pide la Edici\xF3n con IA: una pasada que a\xF1ade texto, gr\xE1ficos y m\xFAsica sobre el video ya entregado (variante with_ai_edit; se cobra al entregar). Nivel: "economy" (Econ\xF3mica, Sonnet 5.5, m\xE1s barata) o "pro" (Opus 5.5, mejor calidad, ~10 min). true equivale a "pro". Default sin edici\xF3n.'),
|
|
36835
|
-
addSubtitles:
|
|
36836
|
-
idempotencyKey:
|
|
37125
|
+
addSubtitles: z142.boolean().default(false).describe("Opcional. Quema subt\xEDtulos usando el rawPrompt exacto. Default false."),
|
|
37126
|
+
idempotencyKey: z142.string().uuid().optional().describe("Opcional. Se acepta por compatibilidad pero se ignora: la identidad del reintento se deriva autom\xE1ticamente de los argumentos sem\xE1nticos, as\xED que un retry con los mismos argumentos ya es idempotente sin este campo.")
|
|
36837
37127
|
}).strict();
|
|
36838
37128
|
var imageStyleList = ImageCreativeStyleEnum.options.join(" | ");
|
|
36839
|
-
var ImageCreativeSetup =
|
|
36840
|
-
|
|
36841
|
-
mode:
|
|
37129
|
+
var ImageCreativeSetup = z142.discriminatedUnion("mode", [
|
|
37130
|
+
z142.object({
|
|
37131
|
+
mode: z142.literal("automatic")
|
|
36842
37132
|
}).strict(),
|
|
36843
|
-
|
|
36844
|
-
mode:
|
|
36845
|
-
creativeArchetype:
|
|
37133
|
+
z142.object({
|
|
37134
|
+
mode: z142.literal("guided"),
|
|
37135
|
+
creativeArchetype: z142.union([
|
|
36846
37136
|
ImageCreativeArchetypeEnum,
|
|
36847
|
-
|
|
37137
|
+
z142.literal("ai_decides")
|
|
36848
37138
|
]),
|
|
36849
|
-
visualTreatment:
|
|
37139
|
+
visualTreatment: z142.union([
|
|
36850
37140
|
ImageCreativeVisualTreatmentEnum,
|
|
36851
|
-
|
|
37141
|
+
z142.literal("ai_decides")
|
|
36852
37142
|
]),
|
|
36853
37143
|
aspectRatio: ImageCreativeAspectRatioEnum,
|
|
36854
|
-
nVariants:
|
|
36855
|
-
|
|
36856
|
-
|
|
36857
|
-
|
|
36858
|
-
|
|
36859
|
-
|
|
37144
|
+
nVariants: z142.union([
|
|
37145
|
+
z142.literal(1),
|
|
37146
|
+
z142.literal(2),
|
|
37147
|
+
z142.literal(3),
|
|
37148
|
+
z142.literal(4),
|
|
37149
|
+
z142.literal(6)
|
|
36860
37150
|
]),
|
|
36861
|
-
imageModel:
|
|
36862
|
-
styleMix:
|
|
36863
|
-
generateAdCopy:
|
|
36864
|
-
competitorScanEnabled:
|
|
36865
|
-
productContext:
|
|
36866
|
-
creativeDirection:
|
|
36867
|
-
brandColors:
|
|
36868
|
-
headline:
|
|
36869
|
-
subheadline:
|
|
37151
|
+
imageModel: z142.enum(PUBLIC_IMAGE_QUALITY_INPUTS).describe('Calidad: "image:1k" (Est\xE1ndar) o "image:2k" (Premium). Los tokens gpt-image-2-5:{1k,2k} y gpt-image-2:{1k,2k} se siguen aceptando.'),
|
|
37152
|
+
styleMix: z142.boolean().nullable().describe("Decisi\xF3n s\xF3lo para 2\u20134 variantes. Usa null para 1 o 6, cuando la UI no hace esta pregunta."),
|
|
37153
|
+
generateAdCopy: z142.boolean(),
|
|
37154
|
+
competitorScanEnabled: z142.boolean(),
|
|
37155
|
+
productContext: z142.string().trim().min(1).max(3e3).nullable(),
|
|
37156
|
+
creativeDirection: z142.string().trim().min(1).max(500).nullable(),
|
|
37157
|
+
brandColors: z142.string().trim().min(1).max(240).nullable().describe('Colores en lenguaje natural, igual que en el chat web. Ej. "Azul y Dorado". null significa sin colores espec\xEDficos.'),
|
|
37158
|
+
headline: z142.string().trim().min(1).max(120).nullable(),
|
|
37159
|
+
subheadline: z142.string().trim().min(1).max(160).nullable()
|
|
36870
37160
|
}).strict()
|
|
36871
37161
|
]).superRefine((setup, context) => {
|
|
36872
37162
|
if (setup.mode !== "guided") return;
|
|
36873
37163
|
const needsStyleMix = setup.nVariants >= 2 && setup.nVariants <= 4;
|
|
36874
37164
|
if (needsStyleMix && setup.styleMix === null) {
|
|
36875
37165
|
context.addIssue({
|
|
36876
|
-
code:
|
|
37166
|
+
code: z142.ZodIssueCode.custom,
|
|
36877
37167
|
path: [
|
|
36878
37168
|
"styleMix"
|
|
36879
37169
|
],
|
|
@@ -36882,7 +37172,7 @@ var ImageCreativeSetup = z141.discriminatedUnion("mode", [
|
|
|
36882
37172
|
}
|
|
36883
37173
|
if (!needsStyleMix && setup.styleMix !== null) {
|
|
36884
37174
|
context.addIssue({
|
|
36885
|
-
code:
|
|
37175
|
+
code: z142.ZodIssueCode.custom,
|
|
36886
37176
|
path: [
|
|
36887
37177
|
"styleMix"
|
|
36888
37178
|
],
|
|
@@ -36891,7 +37181,7 @@ var ImageCreativeSetup = z141.discriminatedUnion("mode", [
|
|
|
36891
37181
|
}
|
|
36892
37182
|
if (imageResolutionOf(setup.imageModel) === "2k" && setup.aspectRatio === "4:5") {
|
|
36893
37183
|
context.addIssue({
|
|
36894
|
-
code:
|
|
37184
|
+
code: z142.ZodIssueCode.custom,
|
|
36895
37185
|
path: [
|
|
36896
37186
|
"aspectRatio"
|
|
36897
37187
|
],
|
|
@@ -36899,55 +37189,55 @@ var ImageCreativeSetup = z141.discriminatedUnion("mode", [
|
|
|
36899
37189
|
});
|
|
36900
37190
|
}
|
|
36901
37191
|
});
|
|
36902
|
-
var LandingCreativeSetup =
|
|
36903
|
-
|
|
36904
|
-
mode:
|
|
37192
|
+
var LandingCreativeSetup = z142.discriminatedUnion("mode", [
|
|
37193
|
+
z142.object({
|
|
37194
|
+
mode: z142.literal("automatic")
|
|
36905
37195
|
}).strict(),
|
|
36906
|
-
|
|
36907
|
-
mode:
|
|
37196
|
+
z142.object({
|
|
37197
|
+
mode: z142.literal("guided"),
|
|
36908
37198
|
landingStrategy: LandingStrategyEnum,
|
|
36909
37199
|
landingImageFormat: LandingImageFormatEnum,
|
|
36910
|
-
competitorScanEnabled:
|
|
36911
|
-
paymentMethods:
|
|
36912
|
-
productContext:
|
|
36913
|
-
creativeDirection:
|
|
36914
|
-
headline:
|
|
36915
|
-
subheadline:
|
|
37200
|
+
competitorScanEnabled: z142.boolean(),
|
|
37201
|
+
paymentMethods: z142.string().trim().min(1).max(240).nullable(),
|
|
37202
|
+
productContext: z142.string().trim().min(1).max(3e3).nullable(),
|
|
37203
|
+
creativeDirection: z142.string().trim().min(1).max(500).nullable(),
|
|
37204
|
+
headline: z142.string().trim().min(1).max(120).nullable(),
|
|
37205
|
+
subheadline: z142.string().trim().min(1).max(160).nullable()
|
|
36916
37206
|
}).strict()
|
|
36917
37207
|
]);
|
|
36918
|
-
var ImageInput =
|
|
37208
|
+
var ImageInput = z142.object({
|
|
36919
37209
|
images: CreativeRefs.images.optional(),
|
|
36920
37210
|
winnerReference: WinnerReferenceInput.optional().describe(WINNER_IMAGE_CHOICE_GUIDANCE),
|
|
36921
|
-
productTitle:
|
|
36922
|
-
primaryAngle:
|
|
37211
|
+
productTitle: z142.string().min(1).describe('Nombre del producto. Ej. "Crema Anti-Edad Premium".'),
|
|
37212
|
+
primaryAngle: z142.string().min(1).optional().describe('Opcional. \xC1ngulo de venta principal. Si se omite, ProductMaker lo genera y creativeSetup.mode="guided" lo muestra para revisi\xF3n antes de crear las im\xE1genes.'),
|
|
36923
37213
|
buyerPersona: CreativeRefs.buyerPersona,
|
|
36924
37214
|
language: CreativeRefs.language,
|
|
36925
37215
|
country: CreativeRefs.country,
|
|
36926
37216
|
creativeSetup: ImageCreativeSetup.describe("Configuraci\xF3n obligatoria equivalente al wizard web. Usa guided salvo que el usuario delegue expl\xEDcitamente todo; presenta las decisiones guiadas en un \xFAnico checklist compacto."),
|
|
36927
37217
|
imageStyle: ImageCreativeStyleEnum.optional().describe(`Opcional. Estilo del shot del producto. Valores: ${imageStyleList}. Si se omite, la IA escoge seg\xFAn el archetype.`),
|
|
36928
|
-
features:
|
|
37218
|
+
features: z142.array(z142.string()).optional().describe('Opcional. Features/beneficios a destacar visualmente. Ej. ["resistente al agua", "ultra liviano"].'),
|
|
36929
37219
|
price: CreativeRefs.price,
|
|
36930
|
-
omitPrice:
|
|
37220
|
+
omitPrice: z142.boolean().optional().describe("Pon true \xFAnicamente cuando el usuario pida expl\xEDcitamente una imagen sin precio. Evita que el backend reutilice un precio extra\xEDdo del producto. No lo combines con price ni compareAtPrice."),
|
|
36931
37221
|
compareAtPrice: CreativeRefs.compareAtPrice,
|
|
36932
37222
|
offer: CreativeRefs.offer,
|
|
36933
37223
|
productLabel: CreativeRefs.productLabel,
|
|
36934
37224
|
isCombo: CreativeRefs.isCombo,
|
|
36935
37225
|
brandId: CreativeRefs.brandId
|
|
36936
37226
|
});
|
|
36937
|
-
var EditImageCreativeInput =
|
|
36938
|
-
taskId:
|
|
36939
|
-
variantIndex:
|
|
36940
|
-
clientRequestId:
|
|
36941
|
-
instruction:
|
|
37227
|
+
var EditImageCreativeInput = z142.object({
|
|
37228
|
+
taskId: z142.string().uuid().describe("ID de la tarea de imagen ya finalizada."),
|
|
37229
|
+
variantIndex: z142.number().int().min(0).max(10).describe("\xCDndice de la variante a corregir (0 = primera imagen)."),
|
|
37230
|
+
clientRequestId: z142.string().min(1).max(160).optional().describe("Identidad estable de esta correcci\xF3n; reutil\xEDzala al reconciliar una respuesta perdida, no para otra instrucci\xF3n."),
|
|
37231
|
+
instruction: z142.string().min(1).max(1e3).describe('Qu\xE9 cambiar en esa imagen. Ej. "quita el badge de env\xEDo gratis", "cambia el fondo a blanco".')
|
|
36942
37232
|
});
|
|
36943
|
-
var LandingInput =
|
|
36944
|
-
includeInitialLandingGif:
|
|
37233
|
+
var LandingInput = z142.object({
|
|
37234
|
+
includeInitialLandingGif: z142.boolean().optional().describe("Opcional. En nuevas landings cl\xE1sicas habilitadas, omitirlo incluye el primer GIF del producto y su coste en el total. Env\xEDa false si el usuario lo excluye: no se genera ni se reserva su coste. Conserva la elecci\xF3n al confirmar y al reintentar."),
|
|
36945
37235
|
images: CreativeRefs.images.optional(),
|
|
36946
37236
|
winnerReference: WinnerReferenceInput.optional().describe(WINNER_IMAGE_CHOICE_GUIDANCE),
|
|
36947
37237
|
language: CreativeRefs.language,
|
|
36948
37238
|
country: CreativeRefs.country,
|
|
36949
37239
|
creativeSetup: LandingCreativeSetup.describe("Configuraci\xF3n obligatoria de landing cl\xE1sica equivalente al wizard web. No expone landing editable/code mientras siga siendo admin-only."),
|
|
36950
|
-
salesAngle:
|
|
37240
|
+
salesAngle: z142.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.'),
|
|
36951
37241
|
buyerPersona: CreativeRefs.buyerPersona,
|
|
36952
37242
|
price: CreativeRefs.price,
|
|
36953
37243
|
compareAtPrice: CreativeRefs.compareAtPrice,
|
|
@@ -37015,8 +37305,9 @@ function resolveLandingSetup(setup) {
|
|
|
37015
37305
|
};
|
|
37016
37306
|
}
|
|
37017
37307
|
__name(resolveLandingSetup, "resolveLandingSetup");
|
|
37018
|
-
function registerCreativeTools(server, api, { transport = "http" } = {}) {
|
|
37308
|
+
function registerCreativeTools(server, api, { transport = "http", proxyBaseUrl = null } = {}) {
|
|
37019
37309
|
server.registerTool("generate_free_video", {
|
|
37310
|
+
title: "Custom-script video",
|
|
37020
37311
|
description: generateFreeVideoGuidance.summary + ' Si el usuario adjunta o menciona una imagen local, llama primero request_media_upload con mediaKind=image y usa el assetId devuelto; no pidas path ni URL. Devuelve un taskId y procesa el job de forma as\xEDncrona.\n\nREQUERIDOS: rawPrompt, quality, durationSeconds y videoResolution.\n\nCALIDAD: "economy" = Econ\xF3mico, "medium" = Medio o "pro" = Pro (Wan). Economy admite 6/8/10/15s; Medium admite cualquier duraci\xF3n entera de 4 a 15s; Pro admite enteros de 2 a 30s y audio natural. `freeProModel` se elimin\xF3 \u2014 usa `modelId` (ver su descripci\xF3n); Seedance 2.5 est\xE1 inactivo y se rechaza con un error claro.\n\nSUBT\xCDTULOS: si addSubtitles=true, el rawPrompt completo se usa como texto exacto de subt\xEDtulos y debe tener m\xE1ximo 500 caracteres; no existe una pregunta adicional de transcripci\xF3n.\n\nPara videos normales con narrativa/\xE1ngulo usa generate_video_creative. Para tareas combinadas de producto usa create_product_task.',
|
|
37021
37312
|
inputSchema: FreeVideoInput.shape,
|
|
37022
37313
|
annotations: CREATES_PAID_OPEN_WORLD
|
|
@@ -37229,13 +37520,46 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
|
|
|
37229
37520
|
server.registerTool("get_image_edit_status", {
|
|
37230
37521
|
description: imageEditStatusGuidance.summary,
|
|
37231
37522
|
inputSchema: {
|
|
37232
|
-
taskId:
|
|
37233
|
-
operationId:
|
|
37523
|
+
taskId: z142.string().uuid(),
|
|
37524
|
+
operationId: z142.string().uuid()
|
|
37234
37525
|
},
|
|
37235
|
-
annotations: READS_INTERNAL
|
|
37526
|
+
annotations: READS_INTERNAL,
|
|
37527
|
+
_meta: taskResultsToolMeta()
|
|
37236
37528
|
}, async (input) => {
|
|
37237
37529
|
try {
|
|
37238
|
-
|
|
37530
|
+
const { assetUrl, assetKey: _assetKey, ...operation } = ImageCreativeEditStatusResponse.parse(await api.getJson(`/v1/creatives/images/${encodeURIComponent(input.taskId)}/operations/${encodeURIComponent(input.operationId)}`));
|
|
37531
|
+
const imageExtension = assetUrl ? new URL(assetUrl).pathname.match(/\.(png|jpe?g|webp)$/i)?.[0]?.toLowerCase() : void 0;
|
|
37532
|
+
const proxyUrl = operation.status === "published" && assetUrl ? buildProxyUrl({
|
|
37533
|
+
r2Url: assetUrl,
|
|
37534
|
+
filename: `image-edit-${operation.variantIndex + 1}${imageExtension ?? ".bin"}`,
|
|
37535
|
+
proxyBaseUrl
|
|
37536
|
+
}) : null;
|
|
37537
|
+
const view = buildTaskResultsView({
|
|
37538
|
+
images: proxyUrl ? [
|
|
37539
|
+
{
|
|
37540
|
+
url: proxyUrl,
|
|
37541
|
+
alt: `Corrected image ${operation.variantIndex + 1}`
|
|
37542
|
+
}
|
|
37543
|
+
] : [],
|
|
37544
|
+
videos: [],
|
|
37545
|
+
links: []
|
|
37546
|
+
}, proxyBaseUrl);
|
|
37547
|
+
if (operation.status === "failed") view.state = "error";
|
|
37548
|
+
else if (operation.status !== "published") view.state = "pending";
|
|
37549
|
+
const deliveredUrl = view.images[0]?.url;
|
|
37550
|
+
const response = {
|
|
37551
|
+
...operation,
|
|
37552
|
+
...deliveredUrl ? {
|
|
37553
|
+
assetUrl: deliveredUrl
|
|
37554
|
+
} : {}
|
|
37555
|
+
};
|
|
37556
|
+
return {
|
|
37557
|
+
...asText(response),
|
|
37558
|
+
structuredContent: response,
|
|
37559
|
+
_meta: {
|
|
37560
|
+
taskResults: view
|
|
37561
|
+
}
|
|
37562
|
+
};
|
|
37239
37563
|
} catch (error) {
|
|
37240
37564
|
throw translateError(error);
|
|
37241
37565
|
}
|
|
@@ -37287,10 +37611,10 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
|
|
|
37287
37611
|
__name(registerCreativeTools, "registerCreativeTools");
|
|
37288
37612
|
|
|
37289
37613
|
// src/tools/creative-variants.ts
|
|
37290
|
-
import { z as
|
|
37614
|
+
import { z as z143 } from "zod";
|
|
37291
37615
|
var estimateCreativeVariantGuidance = getProductMakerCapabilityModelGuidance("estimate_creative_variant");
|
|
37292
37616
|
var createCreativeVariantGuidance = getProductMakerCapabilityModelGuidance("create_creative_variant");
|
|
37293
|
-
var LandingSectionKeyEnum =
|
|
37617
|
+
var LandingSectionKeyEnum = z143.enum([
|
|
37294
37618
|
"problem_hook",
|
|
37295
37619
|
"problem_cost",
|
|
37296
37620
|
"solution_mechanism",
|
|
@@ -37330,24 +37654,24 @@ var LandingSectionKeyEnum = z142.enum([
|
|
|
37330
37654
|
"lifestyle_fit",
|
|
37331
37655
|
"join_lifestyle"
|
|
37332
37656
|
]);
|
|
37333
|
-
var CreativeVariantKindEnum =
|
|
37657
|
+
var CreativeVariantKindEnum = z143.enum([
|
|
37334
37658
|
"video",
|
|
37335
37659
|
"image",
|
|
37336
37660
|
"landing"
|
|
37337
37661
|
]);
|
|
37338
|
-
var EstimateCreativeVariantInputBase =
|
|
37339
|
-
sourceTaskId:
|
|
37662
|
+
var EstimateCreativeVariantInputBase = z143.object({
|
|
37663
|
+
sourceTaskId: z143.string().uuid().describe("ID de la tarea original completada."),
|
|
37340
37664
|
kind: CreativeVariantKindEnum,
|
|
37341
37665
|
videoPreset: TaskCreativeVariantVideoPresetEnum.optional().describe("Opcional para video. Solo cambia el preset p\xFAblico; el servidor conserva el modelo/tier del task origen."),
|
|
37342
37666
|
videoResolution: VideoResolutionEnum.optional().describe("Opcional para video. Resoluci\xF3n del nuevo render."),
|
|
37343
|
-
nVariants:
|
|
37667
|
+
nVariants: z143.number().int().min(1).max(3).optional().describe("Opcional para imagen. N\xFAmero de im\xE1genes adicionales (1-3)."),
|
|
37344
37668
|
sectionKey: LandingSectionKeyEnum.optional().describe("Opcional para landing. Si ya conoces la secci\xF3n, evita cotizar la clasificaci\xF3n con IA."),
|
|
37345
|
-
instructions:
|
|
37669
|
+
instructions: z143.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.")
|
|
37346
37670
|
}).strict();
|
|
37347
37671
|
var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine((input, ctx) => {
|
|
37348
37672
|
if (input.kind === "video" && input.nVariants !== void 0) {
|
|
37349
37673
|
ctx.addIssue({
|
|
37350
|
-
code:
|
|
37674
|
+
code: z143.ZodIssueCode.custom,
|
|
37351
37675
|
path: [
|
|
37352
37676
|
"nVariants"
|
|
37353
37677
|
],
|
|
@@ -37357,7 +37681,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
37357
37681
|
if (input.kind !== "video") {
|
|
37358
37682
|
if (input.videoPreset !== void 0) {
|
|
37359
37683
|
ctx.addIssue({
|
|
37360
|
-
code:
|
|
37684
|
+
code: z143.ZodIssueCode.custom,
|
|
37361
37685
|
path: [
|
|
37362
37686
|
"videoPreset"
|
|
37363
37687
|
],
|
|
@@ -37366,7 +37690,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
37366
37690
|
}
|
|
37367
37691
|
if (input.videoResolution !== void 0) {
|
|
37368
37692
|
ctx.addIssue({
|
|
37369
|
-
code:
|
|
37693
|
+
code: z143.ZodIssueCode.custom,
|
|
37370
37694
|
path: [
|
|
37371
37695
|
"videoResolution"
|
|
37372
37696
|
],
|
|
@@ -37376,7 +37700,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
37376
37700
|
}
|
|
37377
37701
|
if (input.kind !== "image" && input.nVariants !== void 0) {
|
|
37378
37702
|
ctx.addIssue({
|
|
37379
|
-
code:
|
|
37703
|
+
code: z143.ZodIssueCode.custom,
|
|
37380
37704
|
path: [
|
|
37381
37705
|
"nVariants"
|
|
37382
37706
|
],
|
|
@@ -37386,7 +37710,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
37386
37710
|
if (input.kind !== "landing") {
|
|
37387
37711
|
if (input.sectionKey !== void 0) {
|
|
37388
37712
|
ctx.addIssue({
|
|
37389
|
-
code:
|
|
37713
|
+
code: z143.ZodIssueCode.custom,
|
|
37390
37714
|
path: [
|
|
37391
37715
|
"sectionKey"
|
|
37392
37716
|
],
|
|
@@ -37395,7 +37719,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
37395
37719
|
}
|
|
37396
37720
|
if (input.instructions !== void 0) {
|
|
37397
37721
|
ctx.addIssue({
|
|
37398
|
-
code:
|
|
37722
|
+
code: z143.ZodIssueCode.custom,
|
|
37399
37723
|
path: [
|
|
37400
37724
|
"instructions"
|
|
37401
37725
|
],
|
|
@@ -37404,31 +37728,31 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
37404
37728
|
}
|
|
37405
37729
|
}
|
|
37406
37730
|
});
|
|
37407
|
-
var CreateCreativeVariantInputBase =
|
|
37408
|
-
sourceTaskId:
|
|
37731
|
+
var CreateCreativeVariantInputBase = z143.object({
|
|
37732
|
+
sourceTaskId: z143.string().uuid().describe("ID de la tarea original completada."),
|
|
37409
37733
|
kind: CreativeVariantKindEnum,
|
|
37410
|
-
confirm:
|
|
37411
|
-
clientRequestId:
|
|
37412
|
-
angle:
|
|
37413
|
-
buyerPersona:
|
|
37414
|
-
scriptNotes:
|
|
37415
|
-
creativeDirection:
|
|
37734
|
+
confirm: z143.literal(true).describe("Confirmaci\xF3n expl\xEDcita del usuario despu\xE9s de mostrarle el estimado de cr\xE9ditos."),
|
|
37735
|
+
clientRequestId: z143.string().uuid().describe("UUID estable para esta intenci\xF3n (obligatorio por compatibilidad de contrato; el servidor deriva la identidad real del request desde los argumentos sem\xE1nticos, as\xED que un UUID nuevo en un retry NO crea una variante duplicada). Si la API devuelve status=error, una nueva generaci\xF3n exige otra confirmaci\xF3n."),
|
|
37736
|
+
angle: z143.string().trim().min(1).max(500).optional().describe("\xC1ngulo nuevo para video o imagen."),
|
|
37737
|
+
buyerPersona: z143.string().trim().min(1).max(360).optional().describe("Buyer persona coherente con el \xE1ngulo."),
|
|
37738
|
+
scriptNotes: z143.string().trim().min(1).max(2e3).optional().describe("Direcci\xF3n breve del guion para una variante de video."),
|
|
37739
|
+
creativeDirection: z143.string().trim().min(1).max(2e3).optional().describe("Direcci\xF3n creativa para una variante de imagen."),
|
|
37416
37740
|
videoPreset: TaskCreativeVariantVideoPresetEnum.optional().describe("Opcional para video. Preset p\xFAblico; el servidor resuelve modelo/tier desde el task origen."),
|
|
37417
37741
|
videoResolution: VideoResolutionEnum.optional().describe("Opcional para video."),
|
|
37418
|
-
generateAiMusic:
|
|
37419
|
-
nVariants:
|
|
37742
|
+
generateAiMusic: z143.boolean().optional().describe("Opcional para video."),
|
|
37743
|
+
nVariants: z143.number().int().min(1).max(3).optional().describe("Opcional para imagen. N\xFAmero de im\xE1genes adicionales (1-3)."),
|
|
37420
37744
|
imageStyle: ImageCreativeStyleEnum.optional().describe("Opcional para imagen."),
|
|
37421
37745
|
creativeArchetype: ImageCreativeArchetypeEnum.optional().describe("Opcional para imagen."),
|
|
37422
37746
|
visualTreatment: ImageCreativeVisualTreatmentEnum.optional().describe("Opcional para imagen."),
|
|
37423
37747
|
aspectRatio: ImageCreativeAspectRatioEnum.optional().describe("Opcional para imagen."),
|
|
37424
|
-
styleMix:
|
|
37748
|
+
styleMix: z143.boolean().optional().describe("Opcional para imagen."),
|
|
37425
37749
|
sectionKey: LandingSectionKeyEnum.optional().describe("Opcional para landing. Si se omite, se clasifica con IA a partir de instructions."),
|
|
37426
|
-
instructions:
|
|
37750
|
+
instructions: z143.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.")
|
|
37427
37751
|
}).strict();
|
|
37428
37752
|
var CreateCreativeVariantInput = CreateCreativeVariantInputBase.superRefine((input, ctx) => {
|
|
37429
37753
|
if ((input.kind === "video" || input.kind === "image") && !input.angle) {
|
|
37430
37754
|
ctx.addIssue({
|
|
37431
|
-
code:
|
|
37755
|
+
code: z143.ZodIssueCode.custom,
|
|
37432
37756
|
path: [
|
|
37433
37757
|
"angle"
|
|
37434
37758
|
],
|
|
@@ -37437,7 +37761,7 @@ var CreateCreativeVariantInput = CreateCreativeVariantInputBase.superRefine((inp
|
|
|
37437
37761
|
}
|
|
37438
37762
|
if (input.kind === "landing" && !input.sectionKey && !input.instructions) {
|
|
37439
37763
|
ctx.addIssue({
|
|
37440
|
-
code:
|
|
37764
|
+
code: z143.ZodIssueCode.custom,
|
|
37441
37765
|
path: [
|
|
37442
37766
|
"instructions"
|
|
37443
37767
|
],
|
|
@@ -37483,7 +37807,7 @@ var CreateCreativeVariantInput = CreateCreativeVariantInputBase.superRefine((inp
|
|
|
37483
37807
|
for (const field of fieldsByKind[input.kind]) {
|
|
37484
37808
|
if (input[field] !== void 0) {
|
|
37485
37809
|
ctx.addIssue({
|
|
37486
|
-
code:
|
|
37810
|
+
code: z143.ZodIssueCode.custom,
|
|
37487
37811
|
path: [
|
|
37488
37812
|
field
|
|
37489
37813
|
],
|
|
@@ -37641,13 +37965,13 @@ function registerCreativeVariantTools(server, api) {
|
|
|
37641
37965
|
__name(registerCreativeVariantTools, "registerCreativeVariantTools");
|
|
37642
37966
|
|
|
37643
37967
|
// src/tools/script-review.ts
|
|
37644
|
-
import { z as
|
|
37968
|
+
import { z as z144 } from "zod";
|
|
37645
37969
|
var getVideoScriptGuidance = getProductMakerCapabilityModelGuidance("get_video_script");
|
|
37646
37970
|
var regenerateVideoScriptGuidance = getProductMakerCapabilityModelGuidance("regenerate_video_script");
|
|
37647
37971
|
var approveVideoScriptGuidance = getProductMakerCapabilityModelGuidance("approve_video_script");
|
|
37648
|
-
var GetVideoScriptInput =
|
|
37649
|
-
taskId:
|
|
37650
|
-
angleKey:
|
|
37972
|
+
var GetVideoScriptInput = z144.object({
|
|
37973
|
+
taskId: z144.string().min(1).describe("ID de la tarea (la misma que devolvi\xF3 create_product_task / generate_video_creative)."),
|
|
37974
|
+
angleKey: z144.string().min(1).optional().describe('Clave persistida del \xE1ngulo cuyo guion se quiere leer (formato "angle_0", "angle_1", ...). Usa exactamente el angleKey recibido en reviewNextAction.calls al aprobar \xE1ngulos, incluso si s\xF3lo elegiste uno. Si se omite, el backend resuelve a "angle_0" literal; elegir el segundo candidato no crea angle_0. No derives esta clave del n\xFAmero visible del candidato.')
|
|
37651
37975
|
}).strict();
|
|
37652
37976
|
var ApproveVideoScriptInput = ApproveVideoScriptCapabilityRequest;
|
|
37653
37977
|
var RegenerateVideoScriptInput = RegenerateVideoScriptCapabilityRequest.extend({
|
|
@@ -37753,7 +38077,7 @@ Consulta get_task_status con esta tarea antes de reintentar. Si la tarea termin\
|
|
|
37753
38077
|
__name(registerScriptReviewTools, "registerScriptReviewTools");
|
|
37754
38078
|
|
|
37755
38079
|
// src/tools/connections.ts
|
|
37756
|
-
import { z as
|
|
38080
|
+
import { z as z145 } from "zod";
|
|
37757
38081
|
var listShopifyShopsGuidance = getProductMakerCapabilityModelGuidance("list_shopify_shops");
|
|
37758
38082
|
var listShopifyProductsGuidance = getProductMakerCapabilityModelGuidance("list_shopify_products");
|
|
37759
38083
|
var listMetaAdAccountsGuidance = getProductMakerCapabilityModelGuidance("list_meta_ad_accounts");
|
|
@@ -37762,7 +38086,7 @@ var listTikTokAdvertisersGuidance = getProductMakerCapabilityModelGuidance("list
|
|
|
37762
38086
|
function registerConnectionTools(server, api) {
|
|
37763
38087
|
server.registerTool("list_shopify_shops", {
|
|
37764
38088
|
description: listShopifyShopsGuidance.summary + " Si la lista est\xE1 vac\xEDa, dile al usuario que conecte una tienda en productmaker.app/integrations antes de intentar publicar.",
|
|
37765
|
-
inputSchema:
|
|
38089
|
+
inputSchema: z145.object({}).shape,
|
|
37766
38090
|
annotations: READS_INTERNAL
|
|
37767
38091
|
}, async () => {
|
|
37768
38092
|
try {
|
|
@@ -37774,10 +38098,10 @@ function registerConnectionTools(server, api) {
|
|
|
37774
38098
|
});
|
|
37775
38099
|
server.registerTool("list_shopify_products", {
|
|
37776
38100
|
description: listShopifyProductsGuidance.summary + ' 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.',
|
|
37777
|
-
inputSchema:
|
|
37778
|
-
shopDomain:
|
|
37779
|
-
query:
|
|
37780
|
-
taskId:
|
|
38101
|
+
inputSchema: z145.object({
|
|
38102
|
+
shopDomain: z145.string().min(3).max(120),
|
|
38103
|
+
query: z145.string().trim().max(120).optional(),
|
|
38104
|
+
taskId: z145.string().uuid().optional()
|
|
37781
38105
|
}).shape,
|
|
37782
38106
|
annotations: READS_EXTERNAL
|
|
37783
38107
|
}, async ({ shopDomain, query, taskId }) => {
|
|
@@ -37795,7 +38119,7 @@ function registerConnectionTools(server, api) {
|
|
|
37795
38119
|
});
|
|
37796
38120
|
server.registerTool("list_meta_ad_accounts", {
|
|
37797
38121
|
description: listMetaAdAccountsGuidance.summary + " Para publicar, prefiere `list_meta_destinations`, que tambi\xE9n devuelve el portfolio y las p\xE1ginas disponibles. Si est\xE1 vac\xEDa, ind\xEDcale al usuario conectar Meta en productmaker.app/integrations.",
|
|
37798
|
-
inputSchema:
|
|
38122
|
+
inputSchema: z145.object({}).shape,
|
|
37799
38123
|
annotations: READS_EXTERNAL
|
|
37800
38124
|
}, async () => {
|
|
37801
38125
|
try {
|
|
@@ -37807,7 +38131,7 @@ function registerConnectionTools(server, api) {
|
|
|
37807
38131
|
});
|
|
37808
38132
|
server.registerTool("list_meta_destinations", {
|
|
37809
38133
|
description: listMetaDestinationsGuidance.summary + " Es obligatorio llamar esta herramienta antes de publicar, mostrar las opciones y enviar al publicador el `portfolioId`, `adAccountId` y `pageId` elegidos; nunca elijas silenciosamente la primera p\xE1gina o cuenta. No modifica Meta.",
|
|
37810
|
-
inputSchema:
|
|
38134
|
+
inputSchema: z145.object({}).shape,
|
|
37811
38135
|
annotations: READS_EXTERNAL,
|
|
37812
38136
|
_meta: {
|
|
37813
38137
|
"openai/widgetAccessible": true
|
|
@@ -37822,7 +38146,7 @@ function registerConnectionTools(server, api) {
|
|
|
37822
38146
|
});
|
|
37823
38147
|
server.registerTool("list_tiktok_advertisers", {
|
|
37824
38148
|
description: listTikTokAdvertisersGuidance.summary + " Si est\xE1 vac\xEDa, ind\xEDcale al usuario conectar TikTok en productmaker.app/integrations.",
|
|
37825
|
-
inputSchema:
|
|
38149
|
+
inputSchema: z145.object({}).shape,
|
|
37826
38150
|
annotations: READS_EXTERNAL
|
|
37827
38151
|
}, async () => {
|
|
37828
38152
|
try {
|
|
@@ -37836,7 +38160,7 @@ function registerConnectionTools(server, api) {
|
|
|
37836
38160
|
__name(registerConnectionTools, "registerConnectionTools");
|
|
37837
38161
|
|
|
37838
38162
|
// src/tools/actors.ts
|
|
37839
|
-
import { z as
|
|
38163
|
+
import { z as z146 } from "zod";
|
|
37840
38164
|
var listActorsGuidance = getProductMakerCapabilityModelGuidance("list_actors");
|
|
37841
38165
|
var actorVoiceGuidance = {
|
|
37842
38166
|
capabilities: getProductMakerCapabilityModelGuidance("get_actor_voice_capabilities"),
|
|
@@ -37876,7 +38200,7 @@ function sanitizeVoiceResult(value) {
|
|
|
37876
38200
|
__name(sanitizeVoiceResult, "sanitizeVoiceResult");
|
|
37877
38201
|
function registerActorTools(server, api) {
|
|
37878
38202
|
const actor = {
|
|
37879
|
-
actorId:
|
|
38203
|
+
actorId: z146.string().min(1).max(64)
|
|
37880
38204
|
};
|
|
37881
38205
|
const safe = /* @__PURE__ */ __name(async (request) => {
|
|
37882
38206
|
try {
|
|
@@ -37905,8 +38229,8 @@ function registerActorTools(server, api) {
|
|
|
37905
38229
|
inputSchema: {
|
|
37906
38230
|
...actor,
|
|
37907
38231
|
draft: ActorVoiceDraftSchema,
|
|
37908
|
-
requestKey:
|
|
37909
|
-
acceptedQuoteId:
|
|
38232
|
+
requestKey: z146.string().trim().min(1).max(128),
|
|
38233
|
+
acceptedQuoteId: z146.string().trim().min(1).max(128)
|
|
37910
38234
|
},
|
|
37911
38235
|
annotations: CREATES_PAID
|
|
37912
38236
|
}, async ({ actorId, ...body }) => safe(api.postJson(`/v1/actors/${encodeURIComponent(actorId)}/voice/previews`, body)));
|
|
@@ -37914,7 +38238,7 @@ function registerActorTools(server, api) {
|
|
|
37914
38238
|
description: actorVoiceGuidance.get.summary,
|
|
37915
38239
|
inputSchema: {
|
|
37916
38240
|
...actor,
|
|
37917
|
-
candidateId:
|
|
38241
|
+
candidateId: z146.string().min(1).max(64)
|
|
37918
38242
|
},
|
|
37919
38243
|
// Polling can resume the first paid submission of an accepted quote.
|
|
37920
38244
|
// The immutable candidate/operation key keeps recovery idempotent.
|
|
@@ -37927,16 +38251,16 @@ function registerActorTools(server, api) {
|
|
|
37927
38251
|
description: actorVoiceGuidance.approve.summary,
|
|
37928
38252
|
inputSchema: {
|
|
37929
38253
|
...actor,
|
|
37930
|
-
candidateId:
|
|
37931
|
-
expectedRevision:
|
|
37932
|
-
expectedApprovedRevision:
|
|
38254
|
+
candidateId: z146.string().min(1).max(64),
|
|
38255
|
+
expectedRevision: z146.number().int().positive(),
|
|
38256
|
+
expectedApprovedRevision: z146.number().int().positive().nullable().optional()
|
|
37933
38257
|
},
|
|
37934
38258
|
// Replaces the selected voice and withdraws the old catalog grants.
|
|
37935
38259
|
annotations: OVERWRITES_INTERNAL
|
|
37936
38260
|
}, async ({ actorId, ...body }) => safe(api.postJson(`/v1/actors/${encodeURIComponent(actorId)}/voice/approve`, body)));
|
|
37937
38261
|
server.registerTool("list_actors", {
|
|
37938
38262
|
description: listActorsGuidance.summary + " Devuelve id, nombre, descripci\xF3n, g\xE9nero y voz privada del personaje. Cuando el personaje tiene una voz personalizada, `voice.identityId` y `voice.revision` identifican la versi\xF3n aprobada \u2014 conserva la revisi\xF3n al llamar `approve_actor_voice` (expectedApprovedRevision) para reemplazarla. Los IDs de proveedor nunca se exponen por MCP. Usa el `id` como `actorId` al crear o editar una tarea. Solo aparecen los personajes listos para usar; si la lista est\xE1 vac\xEDa, dile al usuario que cree uno en productmaker.app antes de intentar asignarlo. No modifica nada.",
|
|
37939
|
-
inputSchema:
|
|
38263
|
+
inputSchema: z146.object({}).shape,
|
|
37940
38264
|
annotations: READS_INTERNAL
|
|
37941
38265
|
}, async () => {
|
|
37942
38266
|
try {
|
|
@@ -37969,7 +38293,7 @@ function registerActorTools(server, api) {
|
|
|
37969
38293
|
__name(registerActorTools, "registerActorTools");
|
|
37970
38294
|
|
|
37971
38295
|
// src/tools/publish.ts
|
|
37972
|
-
import { z as
|
|
38296
|
+
import { z as z147 } from "zod";
|
|
37973
38297
|
|
|
37974
38298
|
// src/tools/meta-publish-attempt-id.ts
|
|
37975
38299
|
var META_PUBLISH_NAMESPACE = "7b19c9ac-b4eb-4db1-970e-c2f24ce30b98";
|
|
@@ -38008,40 +38332,40 @@ var publishToShopifyGuidance = getProductMakerCapabilityModelGuidance("publish_t
|
|
|
38008
38332
|
var publishToMetaGuidance = getProductMakerCapabilityModelGuidance("publish_to_meta");
|
|
38009
38333
|
var publishCreativesToMetaGuidance = getProductMakerCapabilityModelGuidance("publish_creatives_to_meta");
|
|
38010
38334
|
var publishToTikTokGuidance = getProductMakerCapabilityModelGuidance("publish_to_tiktok");
|
|
38011
|
-
var ShopifyInput =
|
|
38012
|
-
taskId:
|
|
38013
|
-
shopDomain:
|
|
38335
|
+
var ShopifyInput = z147.object({
|
|
38336
|
+
taskId: z147.string().min(1),
|
|
38337
|
+
shopDomain: z147.string().min(1),
|
|
38014
38338
|
/** Override: number of landing images to include. Default = ALL images the
|
|
38015
38339
|
* task generated (the MCP fetches /v1/results first to count). Bounded
|
|
38016
38340
|
* generously here; the backend validates each index against
|
|
38017
38341
|
* landing.images.length and rejects out-of-range. */
|
|
38018
|
-
numLandingImages:
|
|
38019
|
-
variantIndex:
|
|
38342
|
+
numLandingImages: z147.number().int().min(1).max(20).optional(),
|
|
38343
|
+
variantIndex: z147.number().int().min(0).optional(),
|
|
38020
38344
|
/** ASK THE USER before setting: "¿Tu producto tiene variantes (talla, color, etc.)?".
|
|
38021
38345
|
* When true, the landing renders variant-picker pills; when false, it ships
|
|
38022
38346
|
* a single-option product page. Defaults to false. */
|
|
38023
|
-
showVariantPicker:
|
|
38347
|
+
showVariantPicker: z147.boolean().optional(),
|
|
38024
38348
|
/** Default `auto` updates the product previously published for this task and
|
|
38025
38349
|
* shop. Use `existing` with targetProductId to choose another product, or
|
|
38026
38350
|
* `new` only when the user explicitly wants a duplicate/new listing. */
|
|
38027
|
-
publicationMode:
|
|
38351
|
+
publicationMode: z147.enum([
|
|
38028
38352
|
"auto",
|
|
38029
38353
|
"existing",
|
|
38030
38354
|
"new"
|
|
38031
38355
|
]).optional(),
|
|
38032
38356
|
/** Shopify product GID, numeric id, or admin product URL. Required when
|
|
38033
38357
|
* publicationMode is `existing`. */
|
|
38034
|
-
targetProductId:
|
|
38358
|
+
targetProductId: z147.string().trim().min(1).max(300).optional(),
|
|
38035
38359
|
/** Add an announcement bar text above add-to-cart (optional). */
|
|
38036
|
-
announcementBarEnabled:
|
|
38037
|
-
announcementBarText:
|
|
38360
|
+
announcementBarEnabled: z147.boolean().optional(),
|
|
38361
|
+
announcementBarText: z147.string().trim().min(1).max(SHOPIFY_ANNOUNCEMENT_BAR_MAX_LENGTH).optional(),
|
|
38038
38362
|
commerceCorrection: PublishCommerceCorrectionSchema.optional().describe("Complete seller-confirmed commerce values, with the revision and fingerprint returned in recovery. Never invent missing prices or convert currency."),
|
|
38039
|
-
confirmed:
|
|
38363
|
+
confirmed: z147.boolean().optional().describe("Required as true only when submitting commerceCorrection, after showing its values and obtaining explicit seller confirmation.")
|
|
38040
38364
|
});
|
|
38041
38365
|
var ShopifyInputWithAnnouncementBarValidation = ShopifyInput.superRefine((value, ctx) => {
|
|
38042
38366
|
if (value.commerceCorrection && value.confirmed !== true) {
|
|
38043
38367
|
ctx.addIssue({
|
|
38044
|
-
code:
|
|
38368
|
+
code: z147.ZodIssueCode.custom,
|
|
38045
38369
|
path: [
|
|
38046
38370
|
"confirmed"
|
|
38047
38371
|
],
|
|
@@ -38050,7 +38374,7 @@ var ShopifyInputWithAnnouncementBarValidation = ShopifyInput.superRefine((value,
|
|
|
38050
38374
|
}
|
|
38051
38375
|
if (value.announcementBarEnabled && !value.announcementBarText) {
|
|
38052
38376
|
ctx.addIssue({
|
|
38053
|
-
code:
|
|
38377
|
+
code: z147.ZodIssueCode.custom,
|
|
38054
38378
|
path: [
|
|
38055
38379
|
"announcementBarText"
|
|
38056
38380
|
],
|
|
@@ -38059,7 +38383,7 @@ var ShopifyInputWithAnnouncementBarValidation = ShopifyInput.superRefine((value,
|
|
|
38059
38383
|
}
|
|
38060
38384
|
if (!value.announcementBarEnabled && value.announcementBarText) {
|
|
38061
38385
|
ctx.addIssue({
|
|
38062
|
-
code:
|
|
38386
|
+
code: z147.ZodIssueCode.custom,
|
|
38063
38387
|
path: [
|
|
38064
38388
|
"announcementBarText"
|
|
38065
38389
|
],
|
|
@@ -38073,90 +38397,90 @@ function parseShopifyInput(rawInput) {
|
|
|
38073
38397
|
return parsed.data;
|
|
38074
38398
|
}
|
|
38075
38399
|
__name(parseShopifyInput, "parseShopifyInput");
|
|
38076
|
-
var McpCampaignConfig =
|
|
38400
|
+
var McpCampaignConfig = z147.object({
|
|
38077
38401
|
/** Legacy USD amount. Meta retains its existing conversion; the TikTok
|
|
38078
38402
|
* schema below restricts this input to USD advertiser accounts. */
|
|
38079
|
-
dailyBudgetUsd:
|
|
38403
|
+
dailyBudgetUsd: z147.number().positive().optional(),
|
|
38080
38404
|
/** ISO-3166-1 alpha-2 country codes or resolvable names (CountryIso2Schema
|
|
38081
38405
|
* normalizes at validation). Meta accepts max 1 (single-country ad set).
|
|
38082
38406
|
* TikTok takes only the first entry. */
|
|
38083
|
-
targetCountries:
|
|
38084
|
-
campaignName:
|
|
38407
|
+
targetCountries: z147.array(CountryIso2Schema).max(1).optional(),
|
|
38408
|
+
campaignName: z147.string().trim().min(1).max(100).optional()
|
|
38085
38409
|
});
|
|
38086
38410
|
var McpAdAccountId = MetaAdAccountId.describe('Meta ad account id. Accepts "act_1234567890" or the bare numeric id "1234567890".');
|
|
38087
|
-
var MetaInput =
|
|
38088
|
-
taskId:
|
|
38411
|
+
var MetaInput = z147.object({
|
|
38412
|
+
taskId: z147.string().min(1),
|
|
38089
38413
|
/** Exact Meta Business portfolio selected from `list_meta_destinations`. */
|
|
38090
|
-
portfolioId:
|
|
38414
|
+
portfolioId: z147.string().trim().min(1),
|
|
38091
38415
|
/** Exact Facebook Page selected from `list_meta_destinations`. */
|
|
38092
|
-
pageId:
|
|
38416
|
+
pageId: z147.string().trim().min(1),
|
|
38093
38417
|
adAccountId: McpAdAccountId,
|
|
38094
|
-
pixelId:
|
|
38095
|
-
landingUrl:
|
|
38418
|
+
pixelId: z147.string().min(1).optional(),
|
|
38419
|
+
landingUrl: z147.string().url().optional(),
|
|
38096
38420
|
/** Select which angles to publish by 0-based index. Omit to publish all
|
|
38097
38421
|
* successful angles. Example: a task with 3 angles → `[0,2]` publishes #0 + #2. */
|
|
38098
|
-
angleIndexes:
|
|
38422
|
+
angleIndexes: z147.array(z147.number().int().nonnegative()).optional(),
|
|
38099
38423
|
campaignConfig: McpCampaignConfig.optional(),
|
|
38100
38424
|
/** Override the ad's primary text (body copy). If omitted, ProductMaker
|
|
38101
38425
|
* uses the auto-generated copy attached to the task. Capped at 500 chars
|
|
38102
38426
|
* (Meta's primary-text limit). */
|
|
38103
|
-
primaryText:
|
|
38427
|
+
primaryText: z147.string().trim().min(1).max(500).optional(),
|
|
38104
38428
|
/** Optional WhatsApp number. When set, the ad button opens a WhatsApp chat
|
|
38105
38429
|
* (wa.me) instead of a landing page. */
|
|
38106
|
-
phoneNumber:
|
|
38430
|
+
phoneNumber: z147.string().min(8).max(32).optional()
|
|
38107
38431
|
});
|
|
38108
|
-
var GroupedTaskSource =
|
|
38109
|
-
type:
|
|
38110
|
-
taskId:
|
|
38111
|
-
angleIndex:
|
|
38112
|
-
videoVariant:
|
|
38432
|
+
var GroupedTaskSource = z147.object({
|
|
38433
|
+
type: z147.literal("task"),
|
|
38434
|
+
taskId: z147.string().min(1),
|
|
38435
|
+
angleIndex: z147.number().int().min(0).optional(),
|
|
38436
|
+
videoVariant: z147.enum([
|
|
38113
38437
|
"with_subtitles",
|
|
38114
38438
|
"without_subtitles"
|
|
38115
38439
|
]).optional(),
|
|
38116
|
-
imageVariantIndex:
|
|
38117
|
-
});
|
|
38118
|
-
var GroupedMediaSource =
|
|
38119
|
-
type:
|
|
38120
|
-
url:
|
|
38121
|
-
path:
|
|
38122
|
-
thumbnailUrl:
|
|
38123
|
-
thumbnailPath:
|
|
38440
|
+
imageVariantIndex: z147.number().int().min(0).optional()
|
|
38441
|
+
});
|
|
38442
|
+
var GroupedMediaSource = z147.object({
|
|
38443
|
+
type: z147.literal("media"),
|
|
38444
|
+
url: z147.string().url().optional().describe("Public HTTPS media URL. For a file on the user laptop, use request_media_upload on the hosted MCP instead."),
|
|
38445
|
+
path: z147.string().min(1).optional().describe('Local file path only when this MCP runs over stdio. On the hosted MCP, call request_media_upload and then use source.type="asset".'),
|
|
38446
|
+
thumbnailUrl: z147.string().url().optional(),
|
|
38447
|
+
thumbnailPath: z147.string().min(1).optional()
|
|
38124
38448
|
}).refine((value) => Boolean(value.url) !== Boolean(value.path), {
|
|
38125
38449
|
message: "media source needs exactly one of `url` or `path`"
|
|
38126
38450
|
}).refine((value) => !(value.thumbnailUrl && value.thumbnailPath), {
|
|
38127
38451
|
message: "media source needs at most one of `thumbnailUrl` or `thumbnailPath`"
|
|
38128
38452
|
});
|
|
38129
|
-
var GroupedUploadedAssetSource =
|
|
38130
|
-
type:
|
|
38453
|
+
var GroupedUploadedAssetSource = z147.object({
|
|
38454
|
+
type: z147.literal("asset"),
|
|
38131
38455
|
/** Returned by check_media_upload. ProductMaker resolves ownership server-side. */
|
|
38132
|
-
assetId:
|
|
38133
|
-
thumbnailAssetId:
|
|
38456
|
+
assetId: z147.string().uuid(),
|
|
38457
|
+
thumbnailAssetId: z147.string().uuid().optional()
|
|
38134
38458
|
});
|
|
38135
|
-
var GroupedCreative =
|
|
38136
|
-
type:
|
|
38459
|
+
var GroupedCreative = z147.object({
|
|
38460
|
+
type: z147.enum([
|
|
38137
38461
|
"image",
|
|
38138
38462
|
"video"
|
|
38139
38463
|
]),
|
|
38140
|
-
source:
|
|
38464
|
+
source: z147.union([
|
|
38141
38465
|
GroupedTaskSource,
|
|
38142
38466
|
GroupedMediaSource,
|
|
38143
38467
|
GroupedUploadedAssetSource
|
|
38144
38468
|
]),
|
|
38145
|
-
title:
|
|
38146
|
-
description:
|
|
38469
|
+
title: z147.string().max(120).optional(),
|
|
38470
|
+
description: z147.string().max(500).optional(),
|
|
38147
38471
|
cta: MetaCtaType.optional(),
|
|
38148
|
-
creativeId:
|
|
38472
|
+
creativeId: z147.string().min(1).optional()
|
|
38149
38473
|
});
|
|
38150
|
-
var GroupedCampaignConfigBase =
|
|
38474
|
+
var GroupedCampaignConfigBase = z147.object({
|
|
38151
38475
|
campaignType: MetaCampaignType.default("sales"),
|
|
38152
38476
|
optimizationMode: MetaOptimizationMode.default("advantage_plus"),
|
|
38153
38477
|
budgetMode: MetaBudgetMode.optional(),
|
|
38154
|
-
campaignDailyBudget:
|
|
38155
|
-
adSets:
|
|
38156
|
-
tempId:
|
|
38157
|
-
name:
|
|
38158
|
-
dailyBudget:
|
|
38159
|
-
creativeIds:
|
|
38478
|
+
campaignDailyBudget: z147.number().int().positive().optional(),
|
|
38479
|
+
adSets: z147.array(z147.object({
|
|
38480
|
+
tempId: z147.string().min(1).max(80),
|
|
38481
|
+
name: z147.string().min(1).max(100),
|
|
38482
|
+
dailyBudget: z147.number().int().positive().optional(),
|
|
38483
|
+
creativeIds: z147.array(z147.string().min(1)).min(1)
|
|
38160
38484
|
})).min(1).max(CAMPAIGN_LIMITS.MAX_AD_SETS),
|
|
38161
38485
|
/**
|
|
38162
38486
|
* Valid values are 'landing' and 'whatsapp' — there is no 'website'. This
|
|
@@ -38164,17 +38488,17 @@ var GroupedCampaignConfigBase = z146.object({
|
|
|
38164
38488
|
* opaque `invalid_literal` and made the WhatsApp destination — supported by
|
|
38165
38489
|
* the API since #849 — unreachable from this tool.
|
|
38166
38490
|
*/
|
|
38167
|
-
destination:
|
|
38168
|
-
|
|
38169
|
-
type:
|
|
38170
|
-
landingUrl:
|
|
38491
|
+
destination: z147.discriminatedUnion("type", [
|
|
38492
|
+
z147.object({
|
|
38493
|
+
type: z147.literal("landing"),
|
|
38494
|
+
landingUrl: z147.string().url()
|
|
38171
38495
|
}),
|
|
38172
|
-
|
|
38173
|
-
type:
|
|
38174
|
-
phoneNumber:
|
|
38496
|
+
z147.object({
|
|
38497
|
+
type: z147.literal("whatsapp"),
|
|
38498
|
+
phoneNumber: z147.string().min(8).max(32)
|
|
38175
38499
|
})
|
|
38176
38500
|
]),
|
|
38177
|
-
saveAsDefault:
|
|
38501
|
+
saveAsDefault: z147.boolean().default(false),
|
|
38178
38502
|
budgetPolicy: MetaBudgetPolicy.optional()
|
|
38179
38503
|
});
|
|
38180
38504
|
function resolveBudgetMode(value) {
|
|
@@ -38188,7 +38512,7 @@ var GroupedCampaignConfig = GroupedCampaignConfigBase.superRefine((value, ctx) =
|
|
|
38188
38512
|
const budgetMode = resolveBudgetMode(value);
|
|
38189
38513
|
if (budgetMode === "campaign" && value.campaignDailyBudget === void 0) {
|
|
38190
38514
|
ctx.addIssue({
|
|
38191
|
-
code:
|
|
38515
|
+
code: z147.ZodIssueCode.custom,
|
|
38192
38516
|
path: [
|
|
38193
38517
|
"campaignDailyBudget"
|
|
38194
38518
|
],
|
|
@@ -38199,7 +38523,7 @@ var GroupedCampaignConfig = GroupedCampaignConfigBase.superRefine((value, ctx) =
|
|
|
38199
38523
|
value.adSets.forEach((adSet, index) => {
|
|
38200
38524
|
if (adSet.dailyBudget === void 0) {
|
|
38201
38525
|
ctx.addIssue({
|
|
38202
|
-
code:
|
|
38526
|
+
code: z147.ZodIssueCode.custom,
|
|
38203
38527
|
path: [
|
|
38204
38528
|
"adSets",
|
|
38205
38529
|
index,
|
|
@@ -38214,25 +38538,25 @@ var GroupedCampaignConfig = GroupedCampaignConfigBase.superRefine((value, ctx) =
|
|
|
38214
38538
|
...value,
|
|
38215
38539
|
budgetMode: resolveBudgetMode(value)
|
|
38216
38540
|
}));
|
|
38217
|
-
var PublishCreativesToMetaInputBase =
|
|
38218
|
-
creatives:
|
|
38541
|
+
var PublishCreativesToMetaInputBase = z147.object({
|
|
38542
|
+
creatives: z147.array(GroupedCreative).min(1).max(CAMPAIGN_LIMITS.MAX_CREATIVES),
|
|
38219
38543
|
/** Exact Meta Business portfolio selected from `list_meta_destinations`. */
|
|
38220
|
-
portfolioId:
|
|
38544
|
+
portfolioId: z147.string().trim().min(1),
|
|
38221
38545
|
/** Exact Facebook Page selected from `list_meta_destinations`. */
|
|
38222
|
-
pageId:
|
|
38546
|
+
pageId: z147.string().trim().min(1),
|
|
38223
38547
|
adAccountId: McpAdAccountId,
|
|
38224
|
-
pixelId:
|
|
38548
|
+
pixelId: z147.string().min(1).optional(),
|
|
38225
38549
|
/** Required unless `campaignConfig.destination` is whatsapp or a creative
|
|
38226
38550
|
* comes from a task (the API then lands on the published service landing
|
|
38227
38551
|
* of its service tasks) — see the superRefine below. Mirrors the API's
|
|
38228
38552
|
* `FacebookCreateCampaignFromTasksBodySchema`. */
|
|
38229
|
-
landingUrl:
|
|
38553
|
+
landingUrl: z147.string().url().optional(),
|
|
38230
38554
|
/** ISO-2 code or resolvable country name — mirrors the API contract
|
|
38231
38555
|
* (CountryIso2Schema), which normalizes names to alpha-2 at validation.
|
|
38232
38556
|
* Omitted: the brand country of its service tasks (the API answers
|
|
38233
38557
|
* META_NO_TARGET_COUNTRY when it has none); "US" for products. */
|
|
38234
38558
|
targetCountry: CountryIso2Schema.optional(),
|
|
38235
|
-
campaignName:
|
|
38559
|
+
campaignName: z147.string().trim().min(1).max(100),
|
|
38236
38560
|
campaignConfig: GroupedCampaignConfig.optional()
|
|
38237
38561
|
});
|
|
38238
38562
|
var PublishCreativesToMetaInput = PublishCreativesToMetaInputBase.superRefine((value, ctx) => {
|
|
@@ -38240,7 +38564,7 @@ var PublishCreativesToMetaInput = PublishCreativesToMetaInputBase.superRefine((v
|
|
|
38240
38564
|
const hasTaskCreative = value.creatives.some((creative) => creative.source.type === "task");
|
|
38241
38565
|
if (destination?.type !== "whatsapp" && destination?.type !== "landing" && !value.landingUrl && !hasTaskCreative) {
|
|
38242
38566
|
ctx.addIssue({
|
|
38243
|
-
code:
|
|
38567
|
+
code: z147.ZodIssueCode.custom,
|
|
38244
38568
|
path: [
|
|
38245
38569
|
"landingUrl"
|
|
38246
38570
|
],
|
|
@@ -38249,7 +38573,7 @@ var PublishCreativesToMetaInput = PublishCreativesToMetaInputBase.superRefine((v
|
|
|
38249
38573
|
}
|
|
38250
38574
|
if (destination?.type === "landing" && value.landingUrl && destination.landingUrl !== value.landingUrl) {
|
|
38251
38575
|
ctx.addIssue({
|
|
38252
|
-
code:
|
|
38576
|
+
code: z147.ZodIssueCode.custom,
|
|
38253
38577
|
path: [
|
|
38254
38578
|
"campaignConfig",
|
|
38255
38579
|
"destination",
|
|
@@ -38268,16 +38592,16 @@ var PublishCreativesToMetaInput = PublishCreativesToMetaInputBase.superRefine((v
|
|
|
38268
38592
|
}
|
|
38269
38593
|
return value;
|
|
38270
38594
|
});
|
|
38271
|
-
var PresignUploadResponseSchema =
|
|
38272
|
-
uploadUrl:
|
|
38595
|
+
var PresignUploadResponseSchema = z147.object({
|
|
38596
|
+
uploadUrl: z147.string().url().refine((value) => {
|
|
38273
38597
|
const url2 = new URL(value);
|
|
38274
38598
|
return !url2.username && !url2.password && url2.protocol === "https:";
|
|
38275
38599
|
}, "presigned upload URL must be HTTPS without embedded credentials"),
|
|
38276
|
-
publicUrl:
|
|
38600
|
+
publicUrl: z147.string().url().refine((value) => {
|
|
38277
38601
|
const url2 = new URL(value);
|
|
38278
38602
|
return !url2.username && !url2.password;
|
|
38279
38603
|
}, "public media URL must not contain embedded credentials"),
|
|
38280
|
-
stagingAssetId:
|
|
38604
|
+
stagingAssetId: z147.string().uuid()
|
|
38281
38605
|
});
|
|
38282
38606
|
async function uploadResolvedMedia(api, media, assetRole) {
|
|
38283
38607
|
if (!media.buffer) throw new ResolveMediaError("INVALID_INPUT", "resolved media has no bytes");
|
|
@@ -38303,14 +38627,14 @@ async function resolveAndUploadMedia(api, ref, expectedKind, assetRole, transpor
|
|
|
38303
38627
|
}
|
|
38304
38628
|
__name(resolveAndUploadMedia, "resolveAndUploadMedia");
|
|
38305
38629
|
var TikTokMcpCampaignConfig = McpCampaignConfig.extend({
|
|
38306
|
-
dailyBudgetUsd:
|
|
38307
|
-
dailyBudget:
|
|
38630
|
+
dailyBudgetUsd: z147.number().finite().positive().optional().describe("Legacy USD amount, supported only for USD advertiser accounts. No FX conversion."),
|
|
38631
|
+
dailyBudget: z147.number().finite().positive().optional().describe("Daily budget in the selected advertiser currency major units; pass budgetCurrency together."),
|
|
38308
38632
|
budgetCurrency: TikTokBudgetCurrencySchema.optional(),
|
|
38309
38633
|
destination: TikTokCampaignDestinationSchema.optional()
|
|
38310
38634
|
}).superRefine((value, ctx) => {
|
|
38311
38635
|
if (value.dailyBudgetUsd !== void 0 && (value.dailyBudget !== void 0 || value.budgetCurrency !== void 0)) {
|
|
38312
38636
|
ctx.addIssue({
|
|
38313
|
-
code:
|
|
38637
|
+
code: z147.ZodIssueCode.custom,
|
|
38314
38638
|
path: [
|
|
38315
38639
|
"dailyBudgetUsd"
|
|
38316
38640
|
],
|
|
@@ -38319,7 +38643,7 @@ var TikTokMcpCampaignConfig = McpCampaignConfig.extend({
|
|
|
38319
38643
|
}
|
|
38320
38644
|
if (value.dailyBudget !== void 0 !== (value.budgetCurrency !== void 0)) {
|
|
38321
38645
|
ctx.addIssue({
|
|
38322
|
-
code:
|
|
38646
|
+
code: z147.ZodIssueCode.custom,
|
|
38323
38647
|
path: [
|
|
38324
38648
|
"budgetCurrency"
|
|
38325
38649
|
],
|
|
@@ -38327,23 +38651,27 @@ var TikTokMcpCampaignConfig = McpCampaignConfig.extend({
|
|
|
38327
38651
|
});
|
|
38328
38652
|
}
|
|
38329
38653
|
});
|
|
38330
|
-
var TikTokInput =
|
|
38331
|
-
taskId:
|
|
38332
|
-
advertiserId:
|
|
38333
|
-
pixelId:
|
|
38334
|
-
landingUrl:
|
|
38335
|
-
angleIndexes:
|
|
38654
|
+
var TikTokInput = z147.object({
|
|
38655
|
+
taskId: z147.string().min(1),
|
|
38656
|
+
advertiserId: z147.string().min(1),
|
|
38657
|
+
pixelId: z147.string().min(1).optional(),
|
|
38658
|
+
landingUrl: z147.string().url().optional(),
|
|
38659
|
+
angleIndexes: z147.array(z147.number().int().nonnegative()).optional(),
|
|
38336
38660
|
campaignConfig: TikTokMcpCampaignConfig.optional(),
|
|
38337
38661
|
/** Override the ad's description (TikTok's body copy). If omitted,
|
|
38338
38662
|
* ProductMaker uses the auto-generated copy attached to the task. */
|
|
38339
|
-
primaryText:
|
|
38663
|
+
primaryText: z147.string().trim().min(1).max(500).optional()
|
|
38340
38664
|
});
|
|
38341
38665
|
function registerPublishTools(server, api, opts = {}) {
|
|
38342
38666
|
const transport = opts.transport ?? "http";
|
|
38343
38667
|
server.registerTool("publish_to_shopify", {
|
|
38344
38668
|
description: publishToShopifyGuidance.summary + " Precondici\xF3n: la tarea debe estar en status='complete' \u2014 usa `get_task_status` antes. Por defecto `publicationMode=auto`: si esta tarea ya fue publicada en esa tienda, actualiza ESE MISMO producto y conserva URL, precio, variantes, inventario y SKU; si no, crea uno. Usa `existing` + `targetProductId` cuando el usuario elija otro producto y `new` s\xF3lo si pide crear uno nuevo. ANTES de llamar preg\xFAntale \"\xBFTu producto tiene variantes (talla, color, etc.)?\" y pasa `showVariantPicker: true` si responde que s\xED. Por defecto publica TODAS las im\xE1genes generadas; pasa `numLandingImages` s\xF3lo si el usuario pide menos. En code landings con una oferta expl\xEDcita de packs, ProductMaker s\xF3lo publica el selector si variantes reales de Shopify cobran los totales exactos; la respuesta explica si las cre\xF3, verific\xF3 o descart\xF3 por no existir ahorro. Si devuelve SHOPIFY_PUBLISH_INPUT_REQUIRED, muestra todos los problemas de recovery y solicita los importes faltantes. Solo env\xEDa commerceCorrection con su revisi\xF3n/huella y confirmed:true despu\xE9s de confirmar los valores exactos; nunca inventes importes, conviertas moneda ni elimines ofertas. Si adem\xE1s trae correctionRejected, lo que el vendedor confirm\xF3 fue rechazado: l\xE9ele `reason` ANTES de volver a pedir datos. Un contexto obsoleto requiere volver a revisar; open_editor (incluido SHOPIFY_PUBLISH_CORRECTION_NOT_CONVERGED) requiere el editor de ProductMaker y NO se reenv\xEDa la misma correcci\xF3n. SHOPIFY_PUBLISH_VERIFICATION_FAILED trae en `reason` el siguiente paso: reintenta una vez y si se repite sigue ese paso. Si devuelve SHOPIFY_THEME_SETUP_REQUIRED, el tema de la tienda no tiene el bloque Code Landing y nada se public\xF3: env\xEDa al usuario el themeEditorDeepLink del error para abrir el editor del tema de Shopify, p\xEDdele que agregue el bloque y pulse Guardar (una sola vez por tienda), y reintenta s\xF3lo despu\xE9s. Ante resultado incierto, verifica la tarea/tienda antes de volver a publicar, especialmente con new; commerceCorrectionSaved no prueba publicaci\xF3n. La respuesta es markdown con el link a la landing y al producto.",
|
|
38345
38669
|
inputSchema: ShopifyInput.shape,
|
|
38346
|
-
|
|
38670
|
+
// Publication reaches the public storefront, beyond private account data.
|
|
38671
|
+
annotations: {
|
|
38672
|
+
...OVERWRITES_EXTERNAL,
|
|
38673
|
+
openWorldHint: true
|
|
38674
|
+
}
|
|
38347
38675
|
}, async (input, extra) => {
|
|
38348
38676
|
try {
|
|
38349
38677
|
const parsed = parseShopifyInput(input);
|
|
@@ -38460,7 +38788,13 @@ function registerPublishTools(server, api, opts = {}) {
|
|
|
38460
38788
|
server.registerTool("publish_creatives_to_meta", {
|
|
38461
38789
|
description: publishCreativesToMetaGuidance.summary + ' Publica tus creativos en Meta sin gastar cr\xE9ditos de ProductMaker. El presupuesto de anuncios se paga directamente desde tu cuenta de Meta. Crear creativos o copy con IA sigue usando cr\xE9ditos. Si la petici\xF3n natural adjunta o menciona archivos locales, llama primero `request_media_upload`; tras OK usa cada resultado como `source.type="asset"` con su `assetId` y el `type` que coincide con `mediaKind`. No pidas path ni URL. Despu\xE9s llama `list_meta_destinations` y pregunta en el chat por el `portfolioId`, `adAccountId` y `pageId` exactos; el App es solo para archivos. Si la carga fue parcial, conserva los assets confirmados, procesa s\xF3lo los pendientes y publica \xFAnicamente assets confirmados; nunca inventes assets ni superes diez creativos. Las rutas locales s\xF3lo funcionan con MCP stdio. Los medios propios se eliminan despu\xE9s de publicar correctamente; si falla antes de enviar una mutaci\xF3n a Meta, quedan durante el TTL. Para videos la miniatura es opcional y ProductMaker puede generarla con FFmpeg. `campaignConfig.destination` acepta landing o WhatsApp. Para landing basta `campaignConfig.destination.landingUrl`; no dupliques la URL arriba. Con creativos de tarea de servicio puedes omitir `landingUrl` y `targetCountry`: se usan su landing de servicio publicada y el pa\xEDs de su marca; si falta alguno, pregunta al usuario (nunca asumas un pa\xEDs). Si omites `budgetMode`, se infiere `ad_set` cuando cada ad set trae `dailyBudget`, o `campaign` cuando env\xEDas `campaignDailyBudget`. No actives la campa\xF1a autom\xE1ticamente. Si el resultado es ambiguo, NO reenv\xEDes: revisa Meta Ads Manager.',
|
|
38462
38790
|
inputSchema: PublishCreativesToMetaInputBase.shape,
|
|
38463
|
-
|
|
38791
|
+
// May fetch arbitrary public media URLs and consumes owned upload assets
|
|
38792
|
+
// after successful publication, even though the campaign starts paused.
|
|
38793
|
+
annotations: {
|
|
38794
|
+
...CREATES_EXTERNAL,
|
|
38795
|
+
openWorldHint: true,
|
|
38796
|
+
destructiveHint: true
|
|
38797
|
+
}
|
|
38464
38798
|
}, async (rawInput, extra) => {
|
|
38465
38799
|
try {
|
|
38466
38800
|
const parsed = PublishCreativesToMetaInput.safeParse(rawInput);
|
|
@@ -38696,39 +39030,40 @@ __name(toTikTokCampaignConfig, "toTikTokCampaignConfig");
|
|
|
38696
39030
|
|
|
38697
39031
|
// src/tools/meta-upload.ts
|
|
38698
39032
|
import { registerAppTool } from "@modelcontextprotocol/ext-apps/server";
|
|
38699
|
-
import { z as
|
|
39033
|
+
import { z as z148 } from "zod";
|
|
38700
39034
|
var requestMediaUploadGuidance = getProductMakerCapabilityModelGuidance("request_media_upload");
|
|
38701
|
-
var
|
|
38702
|
-
|
|
39035
|
+
var checkMediaUploadGuidance = getProductMakerCapabilityModelGuidance("check_media_upload");
|
|
39036
|
+
var UploadSessionInputBase = z148.object({
|
|
39037
|
+
mediaKind: z148.enum([
|
|
38703
39038
|
"image",
|
|
38704
39039
|
"video",
|
|
38705
39040
|
"mixed"
|
|
38706
39041
|
]).default("mixed").describe("The kind of file the user will select. Use image for product references and landing pages, video for a single video, and mixed only when the consumer supports both."),
|
|
38707
|
-
assetRole:
|
|
39042
|
+
assetRole: z148.enum([
|
|
38708
39043
|
"media",
|
|
38709
39044
|
"thumbnail"
|
|
38710
39045
|
]).default("media").describe("Use media for the creative itself; thumbnail only for an image thumbnail."),
|
|
38711
|
-
uploadPurpose:
|
|
39046
|
+
uploadPurpose: z148.enum([
|
|
38712
39047
|
"product_reference",
|
|
38713
39048
|
"creative",
|
|
38714
39049
|
"generic"
|
|
38715
39050
|
]).default("generic").describe("The user's purpose for these files. Use product_reference for a product photo used by tasks or landings, creative for media that will become an ad or creative, and generic only when neither applies."),
|
|
38716
|
-
maxFiles:
|
|
39051
|
+
maxFiles: z148.number().int().min(1).max(10).optional().describe("Optional batch limit: image accepts 1-5, video exactly 1, and mixed 1-10.")
|
|
38717
39052
|
});
|
|
38718
|
-
var OpenAiFileReference =
|
|
38719
|
-
download_url:
|
|
38720
|
-
file_id:
|
|
38721
|
-
mime_type:
|
|
38722
|
-
file_name:
|
|
39053
|
+
var OpenAiFileReference = z148.object({
|
|
39054
|
+
download_url: z148.string().url().describe("Required by ChatGPT's openai/fileParams schema. The App prefers getFileDownloadUrl(file_id) for a fresh URL, and the server never fetches this value."),
|
|
39055
|
+
file_id: z148.string().min(1),
|
|
39056
|
+
mime_type: z148.string().min(1).optional(),
|
|
39057
|
+
file_name: z148.string().min(1).optional()
|
|
38723
39058
|
});
|
|
38724
39059
|
var UploadSessionToolInputShape = {
|
|
38725
39060
|
...UploadSessionInputBase.shape,
|
|
38726
|
-
files:
|
|
39061
|
+
files: z148.array(OpenAiFileReference).max(10).optional().describe("Optional OpenAI-hosted file references provided by ChatGPT. They are only metadata for the UI bridge and are never forwarded to ProductMaker.")
|
|
38727
39062
|
};
|
|
38728
|
-
var UploadSessionToolInput =
|
|
39063
|
+
var UploadSessionToolInput = z148.object(UploadSessionToolInputShape).superRefine((value, ctx) => {
|
|
38729
39064
|
if (value.assetRole === "thumbnail" && value.mediaKind !== "image") {
|
|
38730
39065
|
ctx.addIssue({
|
|
38731
|
-
code:
|
|
39066
|
+
code: z148.ZodIssueCode.custom,
|
|
38732
39067
|
path: [
|
|
38733
39068
|
"mediaKind"
|
|
38734
39069
|
],
|
|
@@ -38737,7 +39072,7 @@ var UploadSessionToolInput = z147.object(UploadSessionToolInputShape).superRefin
|
|
|
38737
39072
|
}
|
|
38738
39073
|
if (value.mediaKind === "video" && value.maxFiles !== void 0 && value.maxFiles !== 1) {
|
|
38739
39074
|
ctx.addIssue({
|
|
38740
|
-
code:
|
|
39075
|
+
code: z148.ZodIssueCode.custom,
|
|
38741
39076
|
path: [
|
|
38742
39077
|
"maxFiles"
|
|
38743
39078
|
],
|
|
@@ -38746,7 +39081,7 @@ var UploadSessionToolInput = z147.object(UploadSessionToolInputShape).superRefin
|
|
|
38746
39081
|
}
|
|
38747
39082
|
if (value.mediaKind === "image" && (value.maxFiles ?? 1) > 5) {
|
|
38748
39083
|
ctx.addIssue({
|
|
38749
|
-
code:
|
|
39084
|
+
code: z148.ZodIssueCode.custom,
|
|
38750
39085
|
path: [
|
|
38751
39086
|
"maxFiles"
|
|
38752
39087
|
],
|
|
@@ -38754,38 +39089,38 @@ var UploadSessionToolInput = z147.object(UploadSessionToolInputShape).superRefin
|
|
|
38754
39089
|
});
|
|
38755
39090
|
}
|
|
38756
39091
|
});
|
|
38757
|
-
var UploadSessionResponse =
|
|
38758
|
-
sessionId:
|
|
38759
|
-
uploadPageUrl:
|
|
38760
|
-
mediaKind:
|
|
39092
|
+
var UploadSessionResponse = z148.object({
|
|
39093
|
+
sessionId: z148.string().uuid(),
|
|
39094
|
+
uploadPageUrl: z148.string().url(),
|
|
39095
|
+
mediaKind: z148.enum([
|
|
38761
39096
|
"image",
|
|
38762
39097
|
"video",
|
|
38763
39098
|
"mixed"
|
|
38764
39099
|
]),
|
|
38765
|
-
assetRole:
|
|
39100
|
+
assetRole: z148.enum([
|
|
38766
39101
|
"media",
|
|
38767
39102
|
"thumbnail"
|
|
38768
39103
|
]),
|
|
38769
|
-
maxFiles:
|
|
38770
|
-
expiresAt:
|
|
39104
|
+
maxFiles: z148.number().int().positive(),
|
|
39105
|
+
expiresAt: z148.string().datetime()
|
|
38771
39106
|
});
|
|
38772
|
-
var UploadStatusInput =
|
|
38773
|
-
sessionId:
|
|
39107
|
+
var UploadStatusInput = z148.object({
|
|
39108
|
+
sessionId: z148.string().uuid().describe("The sessionId returned by request_media_upload.")
|
|
38774
39109
|
});
|
|
38775
|
-
var UniversalUploadStatusResponse =
|
|
38776
|
-
|
|
38777
|
-
status:
|
|
38778
|
-
assets:
|
|
39110
|
+
var UniversalUploadStatusResponse = z148.discriminatedUnion("status", [
|
|
39111
|
+
z148.object({
|
|
39112
|
+
status: z148.literal("pending"),
|
|
39113
|
+
assets: z148.array(z148.never()).length(0)
|
|
38779
39114
|
}),
|
|
38780
|
-
|
|
38781
|
-
status:
|
|
38782
|
-
assets:
|
|
38783
|
-
assetId:
|
|
38784
|
-
mediaKind:
|
|
39115
|
+
z148.object({
|
|
39116
|
+
status: z148.literal("uploaded"),
|
|
39117
|
+
assets: z148.array(z148.object({
|
|
39118
|
+
assetId: z148.string().uuid(),
|
|
39119
|
+
mediaKind: z148.enum([
|
|
38785
39120
|
"image",
|
|
38786
39121
|
"video"
|
|
38787
39122
|
]),
|
|
38788
|
-
assetRole:
|
|
39123
|
+
assetRole: z148.enum([
|
|
38789
39124
|
"media",
|
|
38790
39125
|
"thumbnail"
|
|
38791
39126
|
])
|
|
@@ -38793,13 +39128,13 @@ var UniversalUploadStatusResponse = z147.discriminatedUnion("status", [
|
|
|
38793
39128
|
})
|
|
38794
39129
|
]);
|
|
38795
39130
|
var UniversalUploadStructuredResponse = UploadSessionResponse.extend({
|
|
38796
|
-
uploadPurpose:
|
|
39131
|
+
uploadPurpose: z148.enum([
|
|
38797
39132
|
"product_reference",
|
|
38798
39133
|
"creative",
|
|
38799
39134
|
"generic"
|
|
38800
39135
|
]),
|
|
38801
|
-
checkToolName:
|
|
38802
|
-
resolveEndpoint:
|
|
39136
|
+
checkToolName: z148.literal("check_media_upload"),
|
|
39137
|
+
resolveEndpoint: z148.string().url()
|
|
38803
39138
|
});
|
|
38804
39139
|
function contextualizeUploadPageUrl(uploadPageUrl, uploadPurpose) {
|
|
38805
39140
|
const url2 = new URL(uploadPageUrl);
|
|
@@ -38831,7 +39166,7 @@ __name(resolveEndpointForSession, "resolveEndpointForSession");
|
|
|
38831
39166
|
function registerMediaUploadTools(server, api) {
|
|
38832
39167
|
registerAppTool(server, "request_media_upload", {
|
|
38833
39168
|
title: "Subir archivos",
|
|
38834
|
-
description: requestMediaUploadGuidance.summary + "
|
|
39169
|
+
description: requestMediaUploadGuidance.summary + " Prepara una sesi\xF3n privada de carga para usar im\xE1genes o videos nuevos en una operaci\xF3n de ProductMaker solicitada por la persona. El selector inline permite elegir archivos; el enlace seguro es una alternativa si ese selector no est\xE1 disponible. files acepta referencias del host con download_url y file_id; se puede omitir para usar el selector. uploadPurpose distingue product_reference, creative y generic. Las referencias de producto y landing usan mediaKind=image; mixed corresponde a consumidores compatibles con ambos tipos. La sesi\xF3n se consulta con check_media_upload despu\xE9s de que la persona termine. El resultado proporciona assetId y mediaKind para la operaci\xF3n original. No corresponde si ya hay un assetId reutilizable o si la persona proh\xEDbe subir, compartir o almacenar el archivo. No genera, publica ni consume cr\xE9ditos.",
|
|
38835
39170
|
inputSchema: UploadSessionToolInputShape,
|
|
38836
39171
|
annotations: CREATES_INTERNAL,
|
|
38837
39172
|
outputSchema: UniversalUploadStructuredResponse.shape,
|
|
@@ -38872,7 +39207,7 @@ function registerMediaUploadTools(server, api) {
|
|
|
38872
39207
|
}
|
|
38873
39208
|
});
|
|
38874
39209
|
server.registerTool("check_media_upload", {
|
|
38875
|
-
description:
|
|
39210
|
+
description: checkMediaUploadGuidance.summary + " Consulta el estado de una sesi\xF3n de carga existente. pending indica que todav\xEDa no hay archivos verificados; uploaded devuelve assets con assetId, mediaKind y assetRole. Cada archivo sirve \xFAnicamente para consumidores compatibles con su tipo. Es una lectura sin generaci\xF3n, publicaci\xF3n ni consumo de cr\xE9ditos.",
|
|
38876
39211
|
inputSchema: UploadStatusInput.shape,
|
|
38877
39212
|
annotations: READS_INTERNAL,
|
|
38878
39213
|
_meta: {
|
|
@@ -38890,15 +39225,15 @@ function registerMediaUploadTools(server, api) {
|
|
|
38890
39225
|
__name(registerMediaUploadTools, "registerMediaUploadTools");
|
|
38891
39226
|
|
|
38892
39227
|
// src/tools/campaigns.ts
|
|
38893
|
-
import { z as
|
|
39228
|
+
import { z as z149 } from "zod";
|
|
38894
39229
|
var getMetaCampaignStatusGuidance = getProductMakerCapabilityModelGuidance("get_meta_campaign_status");
|
|
38895
39230
|
var getTikTokCampaignStatusGuidance = getProductMakerCapabilityModelGuidance("get_tiktok_campaign_status");
|
|
38896
|
-
var MetaStatusInput =
|
|
38897
|
-
campaignId:
|
|
39231
|
+
var MetaStatusInput = z149.object({
|
|
39232
|
+
campaignId: z149.string().min(1).describe("Meta campaign ID returned by publish_to_meta.")
|
|
38898
39233
|
});
|
|
38899
|
-
var TikTokStatusInput =
|
|
38900
|
-
campaignId:
|
|
38901
|
-
advertiserId:
|
|
39234
|
+
var TikTokStatusInput = z149.object({
|
|
39235
|
+
campaignId: z149.string().min(1).describe("TikTok campaign ID returned by publish_to_tiktok."),
|
|
39236
|
+
advertiserId: z149.string().min(1).describe("The same advertiserId used to publish the campaign.")
|
|
38902
39237
|
});
|
|
38903
39238
|
function registerCampaignTools(server, api) {
|
|
38904
39239
|
server.registerTool("get_meta_campaign_status", {
|
|
@@ -39003,10 +39338,10 @@ function renderTikTokStatusMarkdown(r) {
|
|
|
39003
39338
|
__name(renderTikTokStatusMarkdown, "renderTikTokStatusMarkdown");
|
|
39004
39339
|
|
|
39005
39340
|
// ../../packages/product-economics/src/product-costing.ts
|
|
39006
|
-
import { z as
|
|
39341
|
+
import { z as z151 } from "zod";
|
|
39007
39342
|
|
|
39008
39343
|
// ../../packages/product-economics/src/product-costing-countries.ts
|
|
39009
|
-
import { z as
|
|
39344
|
+
import { z as z150 } from "zod";
|
|
39010
39345
|
|
|
39011
39346
|
// ../../packages/product-economics/src/product-costing-shipping-references.json
|
|
39012
39347
|
var product_costing_shipping_references_default = {
|
|
@@ -39070,21 +39405,21 @@ var product_costing_shipping_references_default = {
|
|
|
39070
39405
|
};
|
|
39071
39406
|
|
|
39072
39407
|
// ../../packages/product-economics/src/product-costing-countries.ts
|
|
39073
|
-
var ShippingReferenceSchema =
|
|
39074
|
-
reference:
|
|
39075
|
-
min:
|
|
39076
|
-
max:
|
|
39077
|
-
updatedAt:
|
|
39078
|
-
confidence:
|
|
39408
|
+
var ShippingReferenceSchema = z150.object({
|
|
39409
|
+
reference: z150.number().positive(),
|
|
39410
|
+
min: z150.number().nonnegative(),
|
|
39411
|
+
max: z150.number().positive(),
|
|
39412
|
+
updatedAt: z150.string().date(),
|
|
39413
|
+
confidence: z150.enum([
|
|
39079
39414
|
"high",
|
|
39080
39415
|
"medium",
|
|
39081
39416
|
"medium-low"
|
|
39082
39417
|
]),
|
|
39083
|
-
sources:
|
|
39418
|
+
sources: z150.array(z150.string().url().startsWith("https://")).min(1).max(3)
|
|
39084
39419
|
}).superRefine(({ reference, min, max }, context) => {
|
|
39085
39420
|
if (min > reference || reference > max) {
|
|
39086
39421
|
context.addIssue({
|
|
39087
|
-
code:
|
|
39422
|
+
code: z150.ZodIssueCode.custom,
|
|
39088
39423
|
message: "shipping_reference_outside_range",
|
|
39089
39424
|
path: [
|
|
39090
39425
|
"reference"
|
|
@@ -39092,7 +39427,7 @@ var ShippingReferenceSchema = z149.object({
|
|
|
39092
39427
|
});
|
|
39093
39428
|
}
|
|
39094
39429
|
});
|
|
39095
|
-
var SHIPPING_REFERENCES =
|
|
39430
|
+
var SHIPPING_REFERENCES = z150.record(ShippingReferenceSchema).parse(product_costing_shipping_references_default);
|
|
39096
39431
|
var PRODUCT_COSTING_COUNTRY_CODES = Object.freeze(Object.keys(SHIPPING_REFERENCES));
|
|
39097
39432
|
var SPANISH_LOCALE_COUNTRIES = /* @__PURE__ */ new Set([
|
|
39098
39433
|
"MX",
|
|
@@ -39274,18 +39609,18 @@ function getProductCostingCountryName(country) {
|
|
|
39274
39609
|
__name(getProductCostingCountryName, "getProductCostingCountryName");
|
|
39275
39610
|
|
|
39276
39611
|
// ../../packages/product-economics/src/product-costing.ts
|
|
39277
|
-
var ProductCostingInputSchema =
|
|
39278
|
-
country:
|
|
39279
|
-
supplierCost:
|
|
39280
|
-
shippingCost:
|
|
39281
|
-
returnRate:
|
|
39282
|
-
cancellationRate:
|
|
39283
|
-
adsManagerCpa:
|
|
39284
|
-
desiredMargin:
|
|
39612
|
+
var ProductCostingInputSchema = z151.object({
|
|
39613
|
+
country: z151.string().refine(isProductCostingCountryCode, "unsupported_country"),
|
|
39614
|
+
supplierCost: z151.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").positive("supplier_cost_positive"),
|
|
39615
|
+
shippingCost: z151.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").nonnegative("shipping_cost_nonnegative"),
|
|
39616
|
+
returnRate: z151.number().finite().min(0, "rate_nonnegative").lt(1, "rate_below_one"),
|
|
39617
|
+
cancellationRate: z151.number().finite().min(0, "rate_nonnegative").lt(1, "rate_below_one"),
|
|
39618
|
+
adsManagerCpa: z151.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").nonnegative("cpa_nonnegative"),
|
|
39619
|
+
desiredMargin: z151.number().finite().min(0, "margin_nonnegative").lt(1, "margin_below_one")
|
|
39285
39620
|
}).superRefine(({ returnRate, cancellationRate }, context) => {
|
|
39286
39621
|
if (returnRate + cancellationRate >= 1) {
|
|
39287
39622
|
context.addIssue({
|
|
39288
|
-
code:
|
|
39623
|
+
code: z151.ZodIssueCode.custom,
|
|
39289
39624
|
message: "delivery_rate_positive",
|
|
39290
39625
|
path: [
|
|
39291
39626
|
"cancellationRate"
|
|
@@ -39382,15 +39717,15 @@ function calculateProductCosting(rawInput) {
|
|
|
39382
39717
|
__name(calculateProductCosting, "calculateProductCosting");
|
|
39383
39718
|
|
|
39384
39719
|
// src/tools/product-costing.ts
|
|
39385
|
-
import { z as
|
|
39720
|
+
import { z as z152 } from "zod";
|
|
39386
39721
|
var costProductGuidance = getProductMakerCapabilityModelGuidance("cost_product");
|
|
39387
|
-
var optionalAmount = /* @__PURE__ */ __name((description) =>
|
|
39388
|
-
var optionalPercent = /* @__PURE__ */ __name((description) =>
|
|
39389
|
-
var ProductCostingToolInput =
|
|
39390
|
-
country:
|
|
39722
|
+
var optionalAmount = /* @__PURE__ */ __name((description) => z152.number().finite().max(Number.MAX_SAFE_INTEGER).nonnegative().optional().describe(description), "optionalAmount");
|
|
39723
|
+
var optionalPercent = /* @__PURE__ */ __name((description) => z152.number().finite().min(0).lt(100).optional().describe(description), "optionalPercent");
|
|
39724
|
+
var ProductCostingToolInput = z152.object({
|
|
39725
|
+
country: z152.string().trim().toUpperCase().refine(isProductCostingCountryCode, {
|
|
39391
39726
|
message: "Pa\xEDs no soportado por ProductMaker."
|
|
39392
39727
|
}).optional().describe("C\xF3digo ISO-2 de cualquiera de los pa\xEDses disponibles en ProductMaker. Omite para Colombia."),
|
|
39393
|
-
supplierCost:
|
|
39728
|
+
supplierCost: z152.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."),
|
|
39394
39729
|
supplierCostCop: optionalAmount("Alias anterior para costo del proveedor en Colombia. Prefiere supplierCost."),
|
|
39395
39730
|
shippingCost: optionalAmount("Costo de un env\xEDo en la moneda del pa\xEDs. Omite para usar el valor gu\xEDa vigente."),
|
|
39396
39731
|
shippingCostCop: optionalAmount("Alias anterior para flete en Colombia. Prefiere shippingCost."),
|
|
@@ -39553,7 +39888,7 @@ function formatPercent(value) {
|
|
|
39553
39888
|
__name(formatPercent, "formatPercent");
|
|
39554
39889
|
|
|
39555
39890
|
// src/tools/product-research.ts
|
|
39556
|
-
import { z as
|
|
39891
|
+
import { z as z153 } from "zod";
|
|
39557
39892
|
|
|
39558
39893
|
// src/tools/product-research-idempotency.ts
|
|
39559
39894
|
var RESEARCH_IDEMPOTENCY_WINDOW_MS = 2 * 60 * 60 * 1e3;
|
|
@@ -39924,17 +40259,17 @@ var TERMINAL2 = /* @__PURE__ */ new Set([
|
|
|
39924
40259
|
]);
|
|
39925
40260
|
var POLL_INTERVAL_MS = 5e3;
|
|
39926
40261
|
var MAX_WAIT_SECONDS = 120;
|
|
39927
|
-
var FindInput =
|
|
40262
|
+
var FindInput = z153.object({
|
|
39928
40263
|
targetCountry: ResearchTargetCountryEnum.describe("Pa\xEDs objetivo. S\xF3lo CO, MX, PE, EC y CL tienen cobertura de investigaci\xF3n."),
|
|
39929
40264
|
categoryKey: ResearchCategoryEnum.default("auto").describe('Categor\xEDa a investigar. Usa "auto" para que la IA elija seg\xFAn el mercado.'),
|
|
39930
|
-
confirmed:
|
|
39931
|
-
waitSeconds:
|
|
40265
|
+
confirmed: z153.boolean().describe(`Ponlo en true SOLO despu\xE9s de que el usuario haya aceptado el cargo de ${PRICE_CREDITS} cr\xE9ditos. No lo asumas.`),
|
|
40266
|
+
waitSeconds: z153.number().int().min(0).max(MAX_WAIT_SECONDS).optional().describe("Segundos a esperar el reporte antes de devolver el runId. M\xE1ximo 120.")
|
|
39932
40267
|
});
|
|
39933
|
-
var GetInput =
|
|
39934
|
-
runId:
|
|
40268
|
+
var GetInput = z153.object({
|
|
40269
|
+
runId: z153.string().uuid().describe("ID del run devuelto por find_winning_products.")
|
|
39935
40270
|
});
|
|
39936
|
-
var ListInput =
|
|
39937
|
-
limit:
|
|
40271
|
+
var ListInput = z153.object({
|
|
40272
|
+
limit: z153.number().int().min(1).max(20).default(10)
|
|
39938
40273
|
});
|
|
39939
40274
|
function textResult(text4) {
|
|
39940
40275
|
return {
|
|
@@ -40073,17 +40408,17 @@ _Esta llamada no cobr\xF3 cr\xE9ditos: se reutiliz\xF3 una investigaci\xF3n reci
|
|
|
40073
40408
|
__name(registerProductResearchTools, "registerProductResearchTools");
|
|
40074
40409
|
|
|
40075
40410
|
// src/tools/dropshipping-projects.ts
|
|
40076
|
-
import { z as
|
|
40411
|
+
import { z as z154 } from "zod";
|
|
40077
40412
|
var startDropshippingProjectGuidance = getProductMakerCapabilityModelGuidance("start_dropshipping_project");
|
|
40078
40413
|
var costDropshippingProjectGuidance = getProductMakerCapabilityModelGuidance("cost_dropshipping_project");
|
|
40079
40414
|
var guideDropshippingLaunchGuidance = getProductMakerCapabilityModelGuidance("guide_dropshipping_launch");
|
|
40080
|
-
var ProjectId =
|
|
40081
|
-
var DropshippingProjectStatus =
|
|
40415
|
+
var ProjectId = z154.string().uuid();
|
|
40416
|
+
var DropshippingProjectStatus = z154.enum([
|
|
40082
40417
|
"active",
|
|
40083
40418
|
"completed",
|
|
40084
40419
|
"archived"
|
|
40085
40420
|
]);
|
|
40086
|
-
var DropshippingProjectPhase =
|
|
40421
|
+
var DropshippingProjectPhase = z154.enum([
|
|
40087
40422
|
"research",
|
|
40088
40423
|
"costing",
|
|
40089
40424
|
"media",
|
|
@@ -40092,21 +40427,21 @@ var DropshippingProjectPhase = z153.enum([
|
|
|
40092
40427
|
"advertising",
|
|
40093
40428
|
"complete"
|
|
40094
40429
|
]);
|
|
40095
|
-
var DropshippingRewardMilestone =
|
|
40430
|
+
var DropshippingRewardMilestone = z154.enum([
|
|
40096
40431
|
"product_selected",
|
|
40097
40432
|
"costing_saved",
|
|
40098
40433
|
"product_media_ready",
|
|
40099
40434
|
"creatives_completed",
|
|
40100
40435
|
"campaign_created"
|
|
40101
40436
|
]);
|
|
40102
|
-
var TargetCountry =
|
|
40437
|
+
var TargetCountry = z154.enum([
|
|
40103
40438
|
"CO",
|
|
40104
40439
|
"MX",
|
|
40105
40440
|
"PE",
|
|
40106
40441
|
"EC",
|
|
40107
40442
|
"CL"
|
|
40108
40443
|
]);
|
|
40109
|
-
var ResearchCategory =
|
|
40444
|
+
var ResearchCategory = z154.enum([
|
|
40110
40445
|
"auto",
|
|
40111
40446
|
"automotive",
|
|
40112
40447
|
"kitchen",
|
|
@@ -40121,7 +40456,7 @@ var ResearchCategory = z153.enum([
|
|
|
40121
40456
|
"garden",
|
|
40122
40457
|
"cleaning"
|
|
40123
40458
|
]);
|
|
40124
|
-
var HttpUrl3 =
|
|
40459
|
+
var HttpUrl3 = z154.string().url().max(2e3).refine((value) => {
|
|
40125
40460
|
try {
|
|
40126
40461
|
return [
|
|
40127
40462
|
"http:",
|
|
@@ -40131,45 +40466,45 @@ var HttpUrl3 = z153.string().url().max(2e3).refine((value) => {
|
|
|
40131
40466
|
return false;
|
|
40132
40467
|
}
|
|
40133
40468
|
}, "URL must use HTTP(S)");
|
|
40134
|
-
var ResearchCandidateOrigin =
|
|
40135
|
-
type:
|
|
40469
|
+
var ResearchCandidateOrigin = z154.object({
|
|
40470
|
+
type: z154.literal("research_candidate"),
|
|
40136
40471
|
runId: ProjectId,
|
|
40137
|
-
candidateSourceId:
|
|
40472
|
+
candidateSourceId: z154.string().min(1).max(160)
|
|
40138
40473
|
});
|
|
40139
|
-
var TaskOrigin =
|
|
40140
|
-
type:
|
|
40474
|
+
var TaskOrigin = z154.object({
|
|
40475
|
+
type: z154.literal("task"),
|
|
40141
40476
|
taskId: ProjectId
|
|
40142
40477
|
});
|
|
40143
|
-
var ManualOrigin =
|
|
40144
|
-
type:
|
|
40478
|
+
var ManualOrigin = z154.object({
|
|
40479
|
+
type: z154.literal("manual"),
|
|
40145
40480
|
clientRequestId: ProjectId,
|
|
40146
|
-
country:
|
|
40481
|
+
country: z154.string().min(2).max(3).optional()
|
|
40147
40482
|
});
|
|
40148
|
-
var StartProjectInput =
|
|
40149
|
-
origin:
|
|
40483
|
+
var StartProjectInput = z154.object({
|
|
40484
|
+
origin: z154.discriminatedUnion("type", [
|
|
40150
40485
|
ResearchCandidateOrigin,
|
|
40151
40486
|
TaskOrigin,
|
|
40152
40487
|
ManualOrigin
|
|
40153
40488
|
]),
|
|
40154
|
-
productName:
|
|
40489
|
+
productName: z154.string().trim().min(1).max(240).optional(),
|
|
40155
40490
|
productUrl: HttpUrl3.optional(),
|
|
40156
|
-
country:
|
|
40157
|
-
languageCode:
|
|
40491
|
+
country: z154.string().min(2).max(3).optional(),
|
|
40492
|
+
languageCode: z154.string().min(2).max(16).optional()
|
|
40158
40493
|
}).strict();
|
|
40159
|
-
var CostProjectInput =
|
|
40494
|
+
var CostProjectInput = z154.object({
|
|
40160
40495
|
projectId: ProjectId,
|
|
40161
|
-
version:
|
|
40162
|
-
country:
|
|
40496
|
+
version: z154.number().int().positive().describe("Version le\xEDda del proyecto. Copia la \xFAltima que viste; no se la pidas al usuario."),
|
|
40497
|
+
country: z154.string().trim().toUpperCase().refine(isProductCostingCountryCode, {
|
|
40163
40498
|
message: "Pa\xEDs no soportado por ProductMaker."
|
|
40164
40499
|
}).describe("C\xF3digo ISO-2 del pa\xEDs del proyecto. Usa el `targetCountry` que devuelve el proyecto."),
|
|
40165
|
-
supplierCost:
|
|
40166
|
-
shippingCost:
|
|
40167
|
-
returnRate:
|
|
40168
|
-
cancellationRate:
|
|
40169
|
-
adsManagerCpa:
|
|
40170
|
-
desiredMargin:
|
|
40171
|
-
});
|
|
40172
|
-
var GuideInput =
|
|
40500
|
+
supplierCost: z154.number().finite().positive().describe("Costo real de una unidad con el proveedor, en la moneda del pa\xEDs. \xDANICO dato que debes pedirle al usuario: nunca lo inventes."),
|
|
40501
|
+
shippingCost: z154.number().finite().nonnegative().optional().describe("Costo de un env\xEDo. Omite para usar el valor gu\xEDa del pa\xEDs en vez de inventarlo."),
|
|
40502
|
+
returnRate: z154.number().finite().min(0).lt(1).optional().describe("Fracci\xF3n de pedidos devueltos (0.25 = 25%). Omite para usar el valor gu\xEDa del pa\xEDs."),
|
|
40503
|
+
cancellationRate: z154.number().finite().min(0).lt(1).optional().describe("Fracci\xF3n de pedidos cancelados (0.15 = 15%). Omite para usar el valor gu\xEDa del pa\xEDs."),
|
|
40504
|
+
adsManagerCpa: z154.number().finite().nonnegative().optional().describe("Costo por compra de Ads Manager. Omite para usar el valor gu\xEDa del pa\xEDs."),
|
|
40505
|
+
desiredMargin: z154.number().finite().min(0).lt(1).optional().describe("Margen deseado (0.1 = 10%). Omite para usar el valor gu\xEDa del pa\xEDs.")
|
|
40506
|
+
});
|
|
40507
|
+
var GuideInput = z154.object({
|
|
40173
40508
|
projectId: ProjectId.optional()
|
|
40174
40509
|
}).strict();
|
|
40175
40510
|
var NEXT_ACTION_KINDS = [
|
|
@@ -40187,29 +40522,29 @@ var NEXT_ACTION_KINDS = [
|
|
|
40187
40522
|
"repair_meta_campaign",
|
|
40188
40523
|
"review_paused_campaign"
|
|
40189
40524
|
];
|
|
40190
|
-
var DropshippingProjectNextAction =
|
|
40191
|
-
kind:
|
|
40192
|
-
href:
|
|
40525
|
+
var DropshippingProjectNextAction = z154.discriminatedUnion("kind", NEXT_ACTION_KINDS.map((kind2) => z154.object({
|
|
40526
|
+
kind: z154.literal(kind2),
|
|
40527
|
+
href: z154.string().min(1)
|
|
40193
40528
|
})));
|
|
40194
|
-
var CostingInputSchema =
|
|
40195
|
-
country:
|
|
40196
|
-
supplierCost:
|
|
40197
|
-
shippingCost:
|
|
40198
|
-
returnRate:
|
|
40199
|
-
cancellationRate:
|
|
40200
|
-
adsManagerCpa:
|
|
40201
|
-
desiredMargin:
|
|
40529
|
+
var CostingInputSchema = z154.object({
|
|
40530
|
+
country: z154.string().min(2).max(3),
|
|
40531
|
+
supplierCost: z154.number().finite().positive(),
|
|
40532
|
+
shippingCost: z154.number().finite().nonnegative(),
|
|
40533
|
+
returnRate: z154.number().finite().min(0).lt(1),
|
|
40534
|
+
cancellationRate: z154.number().finite().min(0).lt(1),
|
|
40535
|
+
adsManagerCpa: z154.number().finite().nonnegative(),
|
|
40536
|
+
desiredMargin: z154.number().finite().min(0).lt(1)
|
|
40202
40537
|
}).superRefine(({ returnRate, cancellationRate }, context) => {
|
|
40203
40538
|
if (returnRate + cancellationRate >= 1) context.addIssue({
|
|
40204
|
-
code:
|
|
40539
|
+
code: z154.ZodIssueCode.custom,
|
|
40205
40540
|
path: [
|
|
40206
40541
|
"cancellationRate"
|
|
40207
40542
|
]
|
|
40208
40543
|
});
|
|
40209
40544
|
});
|
|
40210
|
-
var FiniteCostingNumber =
|
|
40211
|
-
var CostingResultSchema =
|
|
40212
|
-
country:
|
|
40545
|
+
var FiniteCostingNumber = z154.number().finite();
|
|
40546
|
+
var CostingResultSchema = z154.object({
|
|
40547
|
+
country: z154.string().min(2).max(3),
|
|
40213
40548
|
deliveryRate: FiniteCostingNumber,
|
|
40214
40549
|
adjustedShippingCost: FiniteCostingNumber,
|
|
40215
40550
|
adjustedCpa: FiniteCostingNumber,
|
|
@@ -40222,45 +40557,45 @@ var CostingResultSchema = z153.object({
|
|
|
40222
40557
|
estimatedProfit: FiniteCostingNumber,
|
|
40223
40558
|
actualMargin: FiniteCostingNumber,
|
|
40224
40559
|
breakEvenAdsManagerCpa: FiniteCostingNumber,
|
|
40225
|
-
wasPriceAutoAdjusted:
|
|
40226
|
-
profitabilityStatus:
|
|
40227
|
-
});
|
|
40228
|
-
var RewardProgressSchema =
|
|
40229
|
-
campaignKey:
|
|
40230
|
-
totalCredits:
|
|
40231
|
-
earnedCredits:
|
|
40232
|
-
milestones:
|
|
40560
|
+
wasPriceAutoAdjusted: z154.boolean(),
|
|
40561
|
+
profitabilityStatus: z154.literal("healthy")
|
|
40562
|
+
});
|
|
40563
|
+
var RewardProgressSchema = z154.object({
|
|
40564
|
+
campaignKey: z154.literal("dropshipping-launch-v1"),
|
|
40565
|
+
totalCredits: z154.number().int().positive(),
|
|
40566
|
+
earnedCredits: z154.number().int().nonnegative(),
|
|
40567
|
+
milestones: z154.array(z154.object({
|
|
40233
40568
|
milestone: DropshippingRewardMilestone,
|
|
40234
|
-
credits:
|
|
40235
|
-
available:
|
|
40236
|
-
earned:
|
|
40569
|
+
credits: z154.number().int().positive(),
|
|
40570
|
+
available: z154.boolean().optional(),
|
|
40571
|
+
earned: z154.boolean(),
|
|
40237
40572
|
grantId: ProjectId.nullable(),
|
|
40238
|
-
grantedAt:
|
|
40573
|
+
grantedAt: z154.string().datetime().nullable()
|
|
40239
40574
|
})).length(DropshippingRewardMilestone.options.length)
|
|
40240
40575
|
});
|
|
40241
|
-
var ProjectResponseSchema =
|
|
40576
|
+
var ProjectResponseSchema = z154.object({
|
|
40242
40577
|
id: ProjectId,
|
|
40243
40578
|
taskId: ProjectId.nullable(),
|
|
40244
40579
|
status: DropshippingProjectStatus,
|
|
40245
|
-
originKey:
|
|
40580
|
+
originKey: z154.string().min(1),
|
|
40246
40581
|
researchRunId: ProjectId.nullable(),
|
|
40247
|
-
candidateSourceId:
|
|
40248
|
-
productName:
|
|
40582
|
+
candidateSourceId: z154.string().nullable(),
|
|
40583
|
+
productName: z154.string().nullable(),
|
|
40249
40584
|
productUrl: HttpUrl3.nullable(),
|
|
40250
40585
|
referenceImageUrl: HttpUrl3.nullable(),
|
|
40251
|
-
targetCountry:
|
|
40252
|
-
languageCode:
|
|
40253
|
-
hasReferenceImage:
|
|
40254
|
-
hasWinnerReferenceImage:
|
|
40586
|
+
targetCountry: z154.string().nullable(),
|
|
40587
|
+
languageCode: z154.string().nullable(),
|
|
40588
|
+
hasReferenceImage: z154.boolean(),
|
|
40589
|
+
hasWinnerReferenceImage: z154.boolean(),
|
|
40255
40590
|
costingInput: CostingInputSchema.nullable(),
|
|
40256
40591
|
costingResult: CostingResultSchema.nullable(),
|
|
40257
|
-
costingVersion:
|
|
40258
|
-
version:
|
|
40592
|
+
costingVersion: z154.number().int().positive().nullable(),
|
|
40593
|
+
version: z154.number().int().positive(),
|
|
40259
40594
|
phase: DropshippingProjectPhase,
|
|
40260
40595
|
nextAction: DropshippingProjectNextAction.nullable(),
|
|
40261
40596
|
rewards: RewardProgressSchema,
|
|
40262
|
-
createdAt:
|
|
40263
|
-
updatedAt:
|
|
40597
|
+
createdAt: z154.string().datetime(),
|
|
40598
|
+
updatedAt: z154.string().datetime()
|
|
40264
40599
|
}).passthrough();
|
|
40265
40600
|
var NEXT_ACTION_GUIDANCE = {
|
|
40266
40601
|
cost_product: "Llama `cost_dropshipping_project` con el costo real del proveedor. Nunca lo inventes: p\xEDdeselo al usuario. Es el \xDANICO dato que debes pedirle: omite env\xEDo, devoluciones, cancelaciones, CPA y margen para que ProductMaker use los valores gu\xEDa del pa\xEDs, y luego dile al usuario cu\xE1les us\xF3 por si quiere corregirlos.",
|
|
@@ -40380,58 +40715,58 @@ function registerDropshippingProjectTools(server, api) {
|
|
|
40380
40715
|
__name(registerDropshippingProjectTools, "registerDropshippingProjectTools");
|
|
40381
40716
|
|
|
40382
40717
|
// src/tools/daily-control.ts
|
|
40383
|
-
import { z as
|
|
40718
|
+
import { z as z155 } from "zod";
|
|
40384
40719
|
var getDailyControlGuidance = getProductMakerCapabilityModelGuidance("get_daily_control");
|
|
40385
40720
|
var linkCampaignToProjectGuidance = getProductMakerCapabilityModelGuidance("link_campaign_to_project");
|
|
40386
40721
|
var reportDailyReturnsGuidance = getProductMakerCapabilityModelGuidance("report_daily_returns");
|
|
40387
|
-
var ProviderCampaignIdentifier =
|
|
40388
|
-
var DateOnly =
|
|
40389
|
-
var DailyControlInput =
|
|
40722
|
+
var ProviderCampaignIdentifier = z155.string().min(1).max(64).regex(/^[A-Za-z0-9_-]+$/);
|
|
40723
|
+
var DateOnly = z155.string().regex(/^\d{4}-\d{2}-\d{2}$/);
|
|
40724
|
+
var DailyControlInput = z155.object({
|
|
40390
40725
|
date: DateOnly.optional().describe("D\xEDa final de la ventana (YYYY-MM-DD). Si se omite, la ventana termina AYER en UTC \u2014 nunca hoy, porque el d\xEDa en curso est\xE1 incompleto \u2014 y el servidor puede correrla un d\xEDa m\xE1s atr\xE1s si en la zona horaria de la tienda ese d\xEDa todav\xEDa no ha cerrado."),
|
|
40391
|
-
days:
|
|
40392
|
-
projectId:
|
|
40726
|
+
days: z155.number().int().min(1).max(14).default(3).describe("Tama\xF1o de la ventana, 1 a 14 d\xEDas."),
|
|
40727
|
+
projectId: z155.string().uuid().optional().describe("Opcional: limita el reporte a un solo proyecto en vez de todos los productos activos.")
|
|
40393
40728
|
}).strict();
|
|
40394
|
-
var LinkCampaignInput =
|
|
40395
|
-
projectId:
|
|
40396
|
-
provider:
|
|
40729
|
+
var LinkCampaignInput = z155.object({
|
|
40730
|
+
projectId: z155.string().uuid(),
|
|
40731
|
+
provider: z155.enum([
|
|
40397
40732
|
"meta",
|
|
40398
40733
|
"tiktok"
|
|
40399
40734
|
]),
|
|
40400
40735
|
accountId: ProviderCampaignIdentifier.describe("Cuenta publicitaria del proveedor. Para Meta el servidor la IGNORA y deriva la cuenta real de la campa\xF1a \u2014 se sigue pidiendo aqu\xED solo porque TikTok s\xED la necesita para resolver la campa\xF1a."),
|
|
40401
40736
|
campaignId: ProviderCampaignIdentifier
|
|
40402
40737
|
}).strict();
|
|
40403
|
-
var ReportReturnsInput =
|
|
40404
|
-
projectId:
|
|
40738
|
+
var ReportReturnsInput = z155.object({
|
|
40739
|
+
projectId: z155.string().uuid(),
|
|
40405
40740
|
date: DateOnly,
|
|
40406
|
-
returnedOrders:
|
|
40407
|
-
note:
|
|
40408
|
-
}).strict();
|
|
40409
|
-
var DailyControlStatusMirror = DailyControlStatusEnum.or(
|
|
40410
|
-
var DailyControlLightMirror = DailyControlLightEnum.or(
|
|
40411
|
-
var DailyControlSourceMirror = DailyControlSourceEnum.or(
|
|
40412
|
-
var DailyControlVerdictMirror = DailyControlVerdictEnum.or(
|
|
40413
|
-
var DailyControlDayMirror =
|
|
40414
|
-
date:
|
|
40415
|
-
returnedOrders:
|
|
40741
|
+
returnedOrders: z155.number().int().min(0).max(1e5),
|
|
40742
|
+
note: z155.string().trim().max(280).optional()
|
|
40743
|
+
}).strict();
|
|
40744
|
+
var DailyControlStatusMirror = DailyControlStatusEnum.or(z155.string());
|
|
40745
|
+
var DailyControlLightMirror = DailyControlLightEnum.or(z155.string());
|
|
40746
|
+
var DailyControlSourceMirror = DailyControlSourceEnum.or(z155.string());
|
|
40747
|
+
var DailyControlVerdictMirror = DailyControlVerdictEnum.or(z155.string());
|
|
40748
|
+
var DailyControlDayMirror = z155.object({
|
|
40749
|
+
date: z155.string(),
|
|
40750
|
+
returnedOrders: z155.number(),
|
|
40416
40751
|
returnedSource: DailyControlSourceMirror,
|
|
40417
40752
|
cancelledSource: DailyControlSourceMirror,
|
|
40418
|
-
hasEstimates:
|
|
40753
|
+
hasEstimates: z155.boolean(),
|
|
40419
40754
|
light: DailyControlLightMirror
|
|
40420
40755
|
}).passthrough();
|
|
40421
|
-
var DailyControlProductMirror =
|
|
40422
|
-
projectId:
|
|
40423
|
-
productName:
|
|
40756
|
+
var DailyControlProductMirror = z155.object({
|
|
40757
|
+
projectId: z155.string().uuid(),
|
|
40758
|
+
productName: z155.string().nullable(),
|
|
40424
40759
|
status: DailyControlStatusMirror,
|
|
40425
|
-
statusMessage:
|
|
40426
|
-
days:
|
|
40760
|
+
statusMessage: z155.string().nullable(),
|
|
40761
|
+
days: z155.array(DailyControlDayMirror),
|
|
40427
40762
|
verdict: DailyControlVerdictMirror.nullable(),
|
|
40428
|
-
rationale:
|
|
40763
|
+
rationale: z155.string().nullable()
|
|
40429
40764
|
}).passthrough();
|
|
40430
|
-
var DailyControlResponseMirror =
|
|
40431
|
-
since:
|
|
40432
|
-
until:
|
|
40433
|
-
products:
|
|
40434
|
-
archivedProjectSpend:
|
|
40765
|
+
var DailyControlResponseMirror = z155.object({
|
|
40766
|
+
since: z155.string(),
|
|
40767
|
+
until: z155.string(),
|
|
40768
|
+
products: z155.array(DailyControlProductMirror),
|
|
40769
|
+
archivedProjectSpend: z155.array(z155.unknown())
|
|
40435
40770
|
}).passthrough();
|
|
40436
40771
|
var MCP_WARNINGS_KEY = "_mcpDailyControlWarnings";
|
|
40437
40772
|
function withWarnings(response) {
|
|
@@ -40507,7 +40842,7 @@ __name(registerDailyControlTools, "registerDailyControlTools");
|
|
|
40507
40842
|
import { createHash as createHash5 } from "crypto";
|
|
40508
40843
|
import { readFile, stat } from "fs/promises";
|
|
40509
40844
|
import { basename as basename2, isAbsolute, resolve as resolve3 } from "path";
|
|
40510
|
-
import { z as
|
|
40845
|
+
import { z as z156 } from "zod";
|
|
40511
40846
|
var getProductControlGuidance = getProductMakerCapabilityModelGuidance("get_product_control");
|
|
40512
40847
|
var listImportableProductsGuidance = getProductMakerCapabilityModelGuidance("list_importable_products");
|
|
40513
40848
|
var importProductControlsGuidance = getProductMakerCapabilityModelGuidance("import_product_controls");
|
|
@@ -40529,42 +40864,42 @@ var listLinkableCampaignsGuidance = getProductMakerCapabilityModelGuidance("list
|
|
|
40529
40864
|
var linkProductCampaignGuidance = getProductMakerCapabilityModelGuidance("link_product_campaign");
|
|
40530
40865
|
var deleteMonthlyExpenseGuidance = getProductMakerCapabilityModelGuidance("delete_monthly_expense");
|
|
40531
40866
|
var removeProductControlGuidance = getProductMakerCapabilityModelGuidance("remove_product_control");
|
|
40532
|
-
var DateOnly2 =
|
|
40867
|
+
var DateOnly2 = z156.string().regex(/^\d{4}-\d{2}-\d{2}$/);
|
|
40533
40868
|
var IMPORT_BATCH_MAX2 = 50;
|
|
40534
|
-
var ProductControlListInput =
|
|
40535
|
-
days:
|
|
40869
|
+
var ProductControlListInput = z156.object({
|
|
40870
|
+
days: z156.number().int().min(1).max(14).default(3).describe("Tama\xF1o de la ventana, 1 a 14 d\xEDas."),
|
|
40536
40871
|
date: DateOnly2.optional().describe("D\xEDa final de la ventana (YYYY-MM-DD). Si se omite, termina en el \xFAltimo d\xEDa cerrado \u2014 nunca hoy, que est\xE1 incompleto."),
|
|
40537
|
-
shopDomain:
|
|
40872
|
+
shopDomain: z156.string().min(1).optional().describe("Tienda, si tiene m\xE1s de una conectada.")
|
|
40538
40873
|
}).strict();
|
|
40539
|
-
var ImportableProductsInput =
|
|
40540
|
-
q:
|
|
40541
|
-
after:
|
|
40542
|
-
shopDomain:
|
|
40874
|
+
var ImportableProductsInput = z156.object({
|
|
40875
|
+
q: z156.string().trim().max(120).optional().describe("B\xFAsqueda por t\xEDtulo. Sin esto un cat\xE1logo grande es inrecorrible."),
|
|
40876
|
+
after: z156.string().max(512).optional().describe("Cursor de la p\xE1gina anterior, tal como vino en la respuesta."),
|
|
40877
|
+
shopDomain: z156.string().min(1).optional()
|
|
40543
40878
|
}).strict();
|
|
40544
|
-
var ImportProductControlsInput =
|
|
40545
|
-
shopifyProductIds:
|
|
40879
|
+
var ImportProductControlsInput = z156.object({
|
|
40880
|
+
shopifyProductIds: z156.array(z156.string().min(1).max(64)).min(1).max(IMPORT_BATCH_MAX2).describe("Identificadores tal como vinieron de list_importable_products. NO los inventes ni los deduzcas del t\xEDtulo."),
|
|
40546
40881
|
// Obligatorio, como en el endpoint: con dos tiendas conectadas, importar
|
|
40547
40882
|
// sin decir a cuál archivaría el producto bajo la tienda equivocada.
|
|
40548
|
-
shopDomain:
|
|
40883
|
+
shopDomain: z156.string().min(1)
|
|
40549
40884
|
}).strict();
|
|
40550
|
-
var SetProductCostingInput =
|
|
40885
|
+
var SetProductCostingInput = z156.object({
|
|
40551
40886
|
/* La identidad, de UNA forma: la del producto nuestro, o la pareja
|
|
40552
40887
|
tienda + id de Shopify. El esquema canónico también las admite juntas,
|
|
40553
40888
|
pero pedirlas por separado evita que el modelo emita una mezcla que
|
|
40554
40889
|
diga dos productos a la vez. */
|
|
40555
|
-
productControlId:
|
|
40556
|
-
manualSellingPrice:
|
|
40557
|
-
shopDomain:
|
|
40558
|
-
shopifyProductId:
|
|
40559
|
-
targetCountry:
|
|
40560
|
-
supplierCost:
|
|
40561
|
-
shippingCost:
|
|
40562
|
-
returnRate:
|
|
40563
|
-
cancellationRate:
|
|
40564
|
-
}).strict();
|
|
40565
|
-
var Count =
|
|
40566
|
-
var ReportProductCountsInput =
|
|
40567
|
-
productControlId:
|
|
40890
|
+
productControlId: z156.string().uuid().optional().describe("El id nuestro (lo devuelven get_product_control y create_manual_product). Obligatorio para productos manuales o importados sin tienda."),
|
|
40891
|
+
manualSellingPrice: z156.number().positive().nullable().optional().describe("Precio de venta \u2014 SOLO con productControlId: para el producto sin tienda no hay otra verdad. En un producto de Shopify NO se manda: el precio lo manda la tienda."),
|
|
40892
|
+
shopDomain: z156.string().min(1).optional(),
|
|
40893
|
+
shopifyProductId: z156.string().min(1).max(64).optional(),
|
|
40894
|
+
targetCountry: z156.string().length(2).describe("Pa\xEDs de venta en ISO-2, que fija moneda y referencia de flete."),
|
|
40895
|
+
supplierCost: z156.number().finite().positive().describe("Lo que le cuesta el producto al proveedor. Nunca cero."),
|
|
40896
|
+
shippingCost: z156.number().finite().nonnegative(),
|
|
40897
|
+
returnRate: z156.number().finite().min(0).lt(1).describe("Proporci\xF3n 0\u20131, no porcentaje: 0.15 es quince por ciento."),
|
|
40898
|
+
cancellationRate: z156.number().finite().min(0).lt(1)
|
|
40899
|
+
}).strict();
|
|
40900
|
+
var Count = z156.number().int().min(0).max(1e5).nullable().optional();
|
|
40901
|
+
var ReportProductCountsInput = z156.object({
|
|
40902
|
+
productControlId: z156.string().uuid().describe("El identificador que devuelve get_product_control."),
|
|
40568
40903
|
date: DateOnly2,
|
|
40569
40904
|
orders: Count.describe("Pedidos que entraron ese d\xEDa."),
|
|
40570
40905
|
cancelledOrders: Count,
|
|
@@ -40574,64 +40909,64 @@ var ReportProductCountsInput = z155.object({
|
|
|
40574
40909
|
}).strict();
|
|
40575
40910
|
var MAX_ORDERS_FILE_BYTES = 8 * 1024 * 1024;
|
|
40576
40911
|
var SyncShopifyOrdersInput = SyncShopifyOrdersSchema;
|
|
40577
|
-
var ImportOrdersFileInput =
|
|
40912
|
+
var ImportOrdersFileInput = z156.object({
|
|
40578
40913
|
/** Ruta ABSOLUTA en la máquina de quien usa el MCP. */
|
|
40579
|
-
path:
|
|
40580
|
-
operationId:
|
|
40914
|
+
path: z156.string().min(1),
|
|
40915
|
+
operationId: z156.string().uuid().optional(),
|
|
40581
40916
|
/** Solo mirar: devuelve el resumen y no escribe nada. */
|
|
40582
|
-
dryRun:
|
|
40917
|
+
dryRun: z156.boolean().optional()
|
|
40583
40918
|
}).strict();
|
|
40584
|
-
var CreateOperationInput =
|
|
40585
|
-
countryCode:
|
|
40586
|
-
label:
|
|
40919
|
+
var CreateOperationInput = z156.object({
|
|
40920
|
+
countryCode: z156.string().length(2),
|
|
40921
|
+
label: z156.string().trim().min(1).max(60).optional()
|
|
40587
40922
|
}).strict();
|
|
40588
|
-
var MergeProductsInput =
|
|
40589
|
-
sourceId:
|
|
40590
|
-
targetId:
|
|
40923
|
+
var MergeProductsInput = z156.object({
|
|
40924
|
+
sourceId: z156.string().uuid(),
|
|
40925
|
+
targetId: z156.string().uuid()
|
|
40591
40926
|
}).strict();
|
|
40592
|
-
var SetMonthlyExpenseInput =
|
|
40593
|
-
month:
|
|
40927
|
+
var SetMonthlyExpenseInput = z156.object({
|
|
40928
|
+
month: z156.string().regex(/^\d{4}-(0[1-9]|1[0-2])$/).optional(),
|
|
40594
40929
|
label: SaveMonthlyExpenseSchema.shape.label,
|
|
40595
|
-
amount:
|
|
40596
|
-
operationId:
|
|
40930
|
+
amount: z156.number().finite().min(0),
|
|
40931
|
+
operationId: z156.string().uuid().optional()
|
|
40597
40932
|
}).strict();
|
|
40598
|
-
var SetAdSpendInput =
|
|
40599
|
-
month:
|
|
40600
|
-
operationId:
|
|
40933
|
+
var SetAdSpendInput = z156.object({
|
|
40934
|
+
month: z156.string().regex(/^\d{4}-(0[1-9]|1[0-2])$/).optional(),
|
|
40935
|
+
operationId: z156.string().uuid().optional(),
|
|
40601
40936
|
/** null RETIRA la corrección; cero afirma que no pautó. */
|
|
40602
|
-
amount:
|
|
40937
|
+
amount: z156.number().finite().min(0).nullable()
|
|
40603
40938
|
}).strict();
|
|
40604
|
-
var GetFinancesInput =
|
|
40605
|
-
month:
|
|
40606
|
-
operationId:
|
|
40939
|
+
var GetFinancesInput = z156.object({
|
|
40940
|
+
month: z156.string().regex(/^\d{4}-(0[1-9]|1[0-2])$/).optional().describe("Mes en YYYY-MM. Si se omite, el mes en curso. Solo mes calendario."),
|
|
40941
|
+
operationId: z156.string().uuid().optional().describe("El negocio, si tiene mas de uno. Con uno solo no hace falta decirlo.")
|
|
40607
40942
|
}).strict();
|
|
40608
40943
|
var GetDropiStatusInput = DropiCountryQuerySchema.extend({
|
|
40609
40944
|
countryCode: DropiCountrySchema.optional().describe("Pa\xEDs de la cuenta (ISO-2). Si se omite, el servidor responde por Colombia (CO)."),
|
|
40610
|
-
connectionId:
|
|
40945
|
+
connectionId: z156.string().uuid().optional().describe("Una cuenta concreta cuando el pa\xEDs tiene varias. Sale de la lista de conexiones; nunca se inventa.")
|
|
40611
40946
|
});
|
|
40612
40947
|
var SyncDropiOrdersInput = SyncDropiFinancialSchema;
|
|
40613
40948
|
var CreateManualProductInput = CreateManualProductControlSchema.innerType().omit({
|
|
40614
40949
|
requestId: true
|
|
40615
40950
|
});
|
|
40616
|
-
var ListUnmatchedProductsInput =
|
|
40617
|
-
operationId:
|
|
40951
|
+
var ListUnmatchedProductsInput = z156.object({
|
|
40952
|
+
operationId: z156.string().uuid().optional().describe("El negocio, si tiene mas de uno. Con uno solo no hace falta decirlo.")
|
|
40618
40953
|
}).strict();
|
|
40619
40954
|
var ResolveUnmatchedProductInput = ResolveUnmatchedProductSchema;
|
|
40620
|
-
var ListLinkableCampaignsInput =
|
|
40621
|
-
var LinkProductCampaignInput =
|
|
40622
|
-
provider:
|
|
40955
|
+
var ListLinkableCampaignsInput = z156.object({}).strict();
|
|
40956
|
+
var LinkProductCampaignInput = z156.object({
|
|
40957
|
+
provider: z156.enum([
|
|
40623
40958
|
"meta",
|
|
40624
40959
|
"tiktok"
|
|
40625
40960
|
]).describe("La red publicitaria de la campa\xF1a."),
|
|
40626
|
-
accountId:
|
|
40627
|
-
campaignId:
|
|
40628
|
-
productControlId:
|
|
40629
|
-
shopDomain:
|
|
40630
|
-
shopifyProductId:
|
|
40961
|
+
accountId: z156.string().min(1).max(64).regex(/^[A-Za-z0-9_-]+$/).describe("La cuenta publicitaria. Sale de list_linkable_campaigns; nunca se inventa."),
|
|
40962
|
+
campaignId: z156.string().min(1).max(64).regex(/^[A-Za-z0-9_-]+$/).describe("La campa\xF1a. Sale de list_linkable_campaigns; nunca se inventa."),
|
|
40963
|
+
productControlId: z156.string().uuid().optional().describe("El id del producto en su control financiero."),
|
|
40964
|
+
shopDomain: z156.string().min(1).optional().describe("La tienda del producto; con shopifyProductId es la otra forma de decir cu\xE1l es."),
|
|
40965
|
+
shopifyProductId: z156.string().min(1).optional().describe("El id del producto en su tienda. Va junto a shopDomain.")
|
|
40631
40966
|
}).strict();
|
|
40632
40967
|
var DeleteMonthlyExpenseInput = DeleteMonthlyExpenseSchema;
|
|
40633
|
-
var RemoveProductControlInput =
|
|
40634
|
-
productControlId:
|
|
40968
|
+
var RemoveProductControlInput = z156.object({
|
|
40969
|
+
productControlId: z156.string().uuid().describe("El id del producto en su control. Sale del listado; nunca se inventa.")
|
|
40635
40970
|
}).strict();
|
|
40636
40971
|
function manualProductRequestId(input) {
|
|
40637
40972
|
const digest3 = createHash5("sha256").update(JSON.stringify(input, [
|
|
@@ -40972,28 +41307,28 @@ function registerProductControlTools(server, api, { transport = "http" } = {}) {
|
|
|
40972
41307
|
__name(registerProductControlTools, "registerProductControlTools");
|
|
40973
41308
|
|
|
40974
41309
|
// src/tools/market-study-estimate.ts
|
|
40975
|
-
import { z as
|
|
41310
|
+
import { z as z157 } from "zod";
|
|
40976
41311
|
var estimateMarketStudyGuidance = getProductMakerCapabilityModelGuidance("estimate_market_study_cost");
|
|
40977
|
-
var ProductTaskOutputEnum =
|
|
41312
|
+
var ProductTaskOutputEnum = z157.enum([
|
|
40978
41313
|
"landing",
|
|
40979
41314
|
"video",
|
|
40980
41315
|
"image"
|
|
40981
41316
|
]);
|
|
40982
|
-
var EstimateMarketStudyInput =
|
|
40983
|
-
kind:
|
|
41317
|
+
var EstimateMarketStudyInput = z157.object({
|
|
41318
|
+
kind: z157.enum([
|
|
40984
41319
|
"video",
|
|
40985
41320
|
"product_task"
|
|
40986
41321
|
]).describe('"video" para generate_video_creative; "product_task" para create_product_task.'),
|
|
40987
|
-
videoAngleCount:
|
|
40988
|
-
durationSeconds:
|
|
41322
|
+
videoAngleCount: z157.number().int().min(1).max(3).describe("Los mismos 1, 2 o 3 videos que vas a pedir al crear."),
|
|
41323
|
+
durationSeconds: z157.number().int().min(4).max(30).default(15).describe("La misma duraci\xF3n que enviar\xE1s al crear."),
|
|
40989
41324
|
videoStructure: VideoStructureEnum.default("single_take"),
|
|
40990
41325
|
videoQuality: VideoQualityEnum.default("economy"),
|
|
40991
41326
|
videoResolution: VideoResolutionEnum.default("480p"),
|
|
40992
|
-
productImageCount:
|
|
41327
|
+
productImageCount: z157.number().int().min(1).max(5).optional().describe("Cantidad de fotos del producto que adjuntar\xE1s (1-5)."),
|
|
40993
41328
|
outputs: ProductTaskOutputEnum.array().min(1).max(3).optional().describe('Solo para kind="product_task": los mismos outputs de create_product_task. Default: todos.'),
|
|
40994
|
-
generateAiMusic:
|
|
41329
|
+
generateAiMusic: z157.boolean().optional(),
|
|
40995
41330
|
generateAiEdit: AiEditRequestSchema.optional(),
|
|
40996
|
-
replicateWinnerWhenFound:
|
|
41331
|
+
replicateWinnerWhenFound: z157.boolean().optional().describe("La misma preferencia que enviar\xE1s al crear. false excluye la reserva de r\xE9plica del ganador de la cotizaci\xF3n con estudio.")
|
|
40997
41332
|
}).strict();
|
|
40998
41333
|
function buildMarketStudyEstimateRequests(input) {
|
|
40999
41334
|
const quotesVideo = input.kind === "video" || (input.outputs ?? [
|
|
@@ -41131,7 +41466,7 @@ function createServer(opts) {
|
|
|
41131
41466
|
signal: opts.abortSignal
|
|
41132
41467
|
});
|
|
41133
41468
|
registerProductMakerPrompts(server);
|
|
41134
|
-
registerMediaUploadAppResource(server, api.publicUrl("/v1/uploaded-assets/sessions/resolve"));
|
|
41469
|
+
registerMediaUploadAppResource(server, api.publicUrl("/v1/uploaded-assets/sessions/resolve"), opts.mediaUploadStorageOrigins);
|
|
41135
41470
|
registerTaskResultsAppResource(server, proxyBaseUrl);
|
|
41136
41471
|
registerProductCostingTools(server);
|
|
41137
41472
|
registerProductResearchTools(server, api);
|
|
@@ -41145,6 +41480,7 @@ function createServer(opts) {
|
|
|
41145
41480
|
registerServiceImageTools(server, api);
|
|
41146
41481
|
registerAnimatedVideoTools(server, api);
|
|
41147
41482
|
registerCreativeTools(server, api, {
|
|
41483
|
+
proxyBaseUrl,
|
|
41148
41484
|
transport
|
|
41149
41485
|
});
|
|
41150
41486
|
registerCreativeVariantTools(server, api);
|
|
@@ -41165,26 +41501,6 @@ function createServer(opts) {
|
|
|
41165
41501
|
}
|
|
41166
41502
|
__name(createServer, "createServer");
|
|
41167
41503
|
|
|
41168
|
-
// src/validate-api-url.ts
|
|
41169
|
-
function assertSafePublicBaseUrl(envName, url2) {
|
|
41170
|
-
let u;
|
|
41171
|
-
try {
|
|
41172
|
-
u = new URL(url2);
|
|
41173
|
-
} catch {
|
|
41174
|
-
throw new Error(`${envName} is not a valid URL: ${url2}`);
|
|
41175
|
-
}
|
|
41176
|
-
if (u.protocol !== "https:") {
|
|
41177
|
-
throw new Error(`${envName} must use https (got ${u.protocol})`);
|
|
41178
|
-
}
|
|
41179
|
-
if (u.username || u.password || u.search || u.hash) {
|
|
41180
|
-
throw new Error(`${envName} must not contain credentials, query parameters, or fragments`);
|
|
41181
|
-
}
|
|
41182
|
-
if (isPrivateHost(u.hostname)) {
|
|
41183
|
-
throw new Error(`${envName} must not point to a private/loopback host: ${u.hostname}`);
|
|
41184
|
-
}
|
|
41185
|
-
}
|
|
41186
|
-
__name(assertSafePublicBaseUrl, "assertSafePublicBaseUrl");
|
|
41187
|
-
|
|
41188
41504
|
// src/http.ts
|
|
41189
41505
|
var ALLOWED_ORIGINS = /* @__PURE__ */ new Set([
|
|
41190
41506
|
"https://chat.openai.com",
|
|
@@ -41391,6 +41707,7 @@ function createAuthenticatedMcpHandler(opts, oauthAnalytics) {
|
|
|
41391
41707
|
credential,
|
|
41392
41708
|
apiBaseUrl: opts.apiBaseUrl,
|
|
41393
41709
|
proxyBaseUrl: opts.proxyBaseUrl,
|
|
41710
|
+
mediaUploadStorageOrigins: opts.mediaUploadStorageOrigins,
|
|
41394
41711
|
transport: "http",
|
|
41395
41712
|
instructions: buildServerInstructions(legacySignal),
|
|
41396
41713
|
abortSignal: abortController.signal
|
|
@@ -41540,6 +41857,7 @@ async function startHttpServer(env = process.env) {
|
|
|
41540
41857
|
const proxyBaseUrl = env.MCP_PROXY_BASE_URL ?? "https://mcp.productmaker.app";
|
|
41541
41858
|
assertSafePublicBaseUrl("PM_API_URL", apiBaseUrl);
|
|
41542
41859
|
assertSafePublicBaseUrl("MCP_PROXY_BASE_URL", proxyBaseUrl);
|
|
41860
|
+
const mediaUploadStorageOrigins = parseMediaUploadStorageOrigins(env.PM_MEDIA_UPLOAD_STORAGE_ORIGINS);
|
|
41543
41861
|
const oauthConfig = loadMcpOAuthConfig(env);
|
|
41544
41862
|
const auth0Metadata = await loadAuth0Metadata(oauthConfig).catch((error) => {
|
|
41545
41863
|
oauthAnalytics.capture(MCP_OAUTH_ANALYTICS_EVENTS.FAILED, null, {
|
|
@@ -41552,6 +41870,7 @@ async function startHttpServer(env = process.env) {
|
|
|
41552
41870
|
const app = createApp({
|
|
41553
41871
|
apiBaseUrl,
|
|
41554
41872
|
proxyBaseUrl,
|
|
41873
|
+
mediaUploadStorageOrigins,
|
|
41555
41874
|
oauth,
|
|
41556
41875
|
appsChallengeToken: env.OPENAI_APPS_CHALLENGE_TOKEN ?? null
|
|
41557
41876
|
});
|