@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/stdio.js
CHANGED
|
@@ -2796,7 +2796,7 @@ var LlmPricingSnapshotSchema = z3.discriminatedUnion("source", [
|
|
|
2796
2796
|
});
|
|
2797
2797
|
function nativeOutputTokenCeiling(target) {
|
|
2798
2798
|
if (target.nativeOwnedOutputPolicy) return target.nativeOwnedOutputPolicy.verifiedMaxOutputTokens;
|
|
2799
|
-
return target.transport === "google" || target.id === "gemini-3.1-pro-openrouter" ? 65536 : 1e5;
|
|
2799
|
+
return target.transport === "google" || target.id === "gemini-flash-runware" || target.id === "gemini-3.1-pro-openrouter" ? 65536 : 1e5;
|
|
2800
2800
|
}
|
|
2801
2801
|
__name(nativeOutputTokenCeiling, "nativeOutputTokenCeiling");
|
|
2802
2802
|
var LlmRouteTargetSchema = z3.object({
|
|
@@ -3612,7 +3612,19 @@ var identity = {
|
|
|
3612
3612
|
requestDigest: digest,
|
|
3613
3613
|
nativeMessagesBudget: NativeMessagesBudgetSchema.optional()
|
|
3614
3614
|
};
|
|
3615
|
-
var ModelRouteAttemptRequestSchema = z4.object(
|
|
3615
|
+
var ModelRouteAttemptRequestSchema = z4.object({
|
|
3616
|
+
...identity,
|
|
3617
|
+
/** Trusted proxy evidence of a pure reasoning cap. Recovery only: this never
|
|
3618
|
+
* grants a changed body permission to reuse an older paid execution. Absent
|
|
3619
|
+
* on historical entries; retained on new entries to identify normalized work.
|
|
3620
|
+
*/
|
|
3621
|
+
reasoningNormalization: z4.object({
|
|
3622
|
+
version: z4.literal(1),
|
|
3623
|
+
originalCallId: id,
|
|
3624
|
+
originalRequestDigest: digest,
|
|
3625
|
+
originalNativeMessagesBudget: NativeMessagesBudgetSchema.optional()
|
|
3626
|
+
}).strict().optional()
|
|
3627
|
+
}).strict();
|
|
3616
3628
|
var ModelRouteCostSchema = z4.discriminatedUnion("source", [
|
|
3617
3629
|
z4.object({
|
|
3618
3630
|
source: z4.literal("unknown")
|
|
@@ -8051,6 +8063,12 @@ var CodeLandingAuthorEnvelopeSchema = z23.object({
|
|
|
8051
8063
|
*/
|
|
8052
8064
|
variantFacts: z23.array(LandingVariantFactSchema).max(16).optional()
|
|
8053
8065
|
});
|
|
8066
|
+
var CODE_LANDING_DEFERRED_AUTHOR_PROVIDER_RESULT_KIND = "code_landing_deferred_author_provider_v1";
|
|
8067
|
+
var CodeLandingDeferredAuthorProviderResultSchema = z23.object({
|
|
8068
|
+
kind: z23.literal(CODE_LANDING_DEFERRED_AUTHOR_PROVIDER_RESULT_KIND),
|
|
8069
|
+
stepKey: z23.string().min(1),
|
|
8070
|
+
providerResult: z23.record(z23.string(), z23.unknown())
|
|
8071
|
+
});
|
|
8054
8072
|
var CodeLandingCommerceChangeSchema = z23.object({
|
|
8055
8073
|
price: z23.string().trim().min(1).max(40).optional(),
|
|
8056
8074
|
compareAtPrice: z23.string().trim().min(1).max(40).nullable().optional(),
|
|
@@ -10581,6 +10599,8 @@ var role = z38.object({
|
|
|
10581
10599
|
provider: id2,
|
|
10582
10600
|
model: id2,
|
|
10583
10601
|
bindingId: id2,
|
|
10602
|
+
/** Immutable shared-router chain for fresh recipes; absent preserves legacy custody. */
|
|
10603
|
+
routeSnapshot: LlmRouteSnapshotSchema.optional(),
|
|
10584
10604
|
/** null explicitly omits effort on OpenRouter; undefined is a historical recipe. */
|
|
10585
10605
|
reasoning: CreativeEngineRoleReasoningSchema.nullable().optional(),
|
|
10586
10606
|
/** Server-owned output ceiling; omitted historical recipes keep their old stage cap. */
|
|
@@ -10600,7 +10620,13 @@ var CreativeEngineRecipeSchema = z38.object({
|
|
|
10600
10620
|
roles: z38.object({
|
|
10601
10621
|
director: role,
|
|
10602
10622
|
reviewer: role,
|
|
10603
|
-
executor: role.nullable()
|
|
10623
|
+
executor: role.nullable(),
|
|
10624
|
+
/**
|
|
10625
|
+
* Independent reviewer of the assembled final video only (owner
|
|
10626
|
+
* decision 2026-10-26). Absent on historical recipes: their final
|
|
10627
|
+
* review keeps the frozen `reviewer`, unchanged hash and identity.
|
|
10628
|
+
*/
|
|
10629
|
+
finalReviewer: role.optional()
|
|
10604
10630
|
}).strict(),
|
|
10605
10631
|
capabilities: z38.object({
|
|
10606
10632
|
videoModels: z38.array(id2).min(1),
|
|
@@ -10619,9 +10645,14 @@ var CreativeEngineRecipeSchema = z38.object({
|
|
|
10619
10645
|
/** New recipes reserve an explicit native failover; historical calls stay single-route. */
|
|
10620
10646
|
modelFallbackPolicy: z38.literal("anthropic_conclusive_v1").optional(),
|
|
10621
10647
|
/** New reviewer/executor calls use the shared same-model native provider router. */
|
|
10622
|
-
centralModelRouting: z38.
|
|
10648
|
+
centralModelRouting: z38.enum([
|
|
10649
|
+
"native_owned_same_model_v1",
|
|
10650
|
+
"shared_role_chain_v1"
|
|
10651
|
+
]).optional(),
|
|
10623
10652
|
/** Skip only a redundant global review of a deterministically frozen scene repair. */
|
|
10624
10653
|
repairPlanReviewPolicy: z38.literal("frozen_contract_v1").optional(),
|
|
10654
|
+
/** Root-only visual patch wire contract; absent preserves historic full-plan recovery. */
|
|
10655
|
+
repairAuthoring: z38.literal("scene_patch_v1").optional(),
|
|
10625
10656
|
/** New requests maximize output inside model and measured input headroom. */
|
|
10626
10657
|
outputTokenPolicy: z38.literal("model_max_v1").optional(),
|
|
10627
10658
|
/**
|
|
@@ -11082,6 +11113,7 @@ var CreativeEngineCandidateSchema = z38.object({
|
|
|
11082
11113
|
qualityScore: z38.number().min(0).max(100)
|
|
11083
11114
|
}).strict();
|
|
11084
11115
|
var CreativeEngineCostBasisSchema = z38.enum([
|
|
11116
|
+
"reused",
|
|
11085
11117
|
"provider_reported",
|
|
11086
11118
|
"provider_credits_conversion",
|
|
11087
11119
|
"catalogue_estimate"
|
|
@@ -11382,6 +11414,15 @@ var CreativeEngineEventSchema = z38.discriminatedUnion("type", [
|
|
|
11382
11414
|
}).strict(),
|
|
11383
11415
|
plan: CreativeEnginePlanSchema
|
|
11384
11416
|
}).strict(),
|
|
11417
|
+
z38.object({
|
|
11418
|
+
type: z38.literal("accepted_author_output_revalidated"),
|
|
11419
|
+
operationId: id2,
|
|
11420
|
+
acceptedId: id2,
|
|
11421
|
+
context: CreativeEngineContextBindingSchema,
|
|
11422
|
+
outputSha256: hash,
|
|
11423
|
+
validationRevision: z38.string().regex(/^[a-z][a-z0-9_]{0,63}$/),
|
|
11424
|
+
plan: CreativeEnginePlanSchema
|
|
11425
|
+
}).strict(),
|
|
11385
11426
|
z38.object({
|
|
11386
11427
|
type: z38.literal("plan_recorded"),
|
|
11387
11428
|
plan: CreativeEnginePlanSchema
|
|
@@ -12873,7 +12914,9 @@ var SERVICE_MODES = [
|
|
|
12873
12914
|
];
|
|
12874
12915
|
var ServiceModeSchema = z46.enum(SERVICE_MODES);
|
|
12875
12916
|
var AppFeaturesSchema = z46.object({
|
|
12876
|
-
serviceMotionGraphics: z46.boolean()
|
|
12917
|
+
serviceMotionGraphics: z46.boolean(),
|
|
12918
|
+
/** GIF authoring controls; absent on older APIs means hidden. */
|
|
12919
|
+
landingGif: z46.boolean().default(false)
|
|
12877
12920
|
});
|
|
12878
12921
|
var MOTION_GRAPHICS_FORMATS = [
|
|
12879
12922
|
"vertical",
|
|
@@ -20680,7 +20723,7 @@ var CAPABILITY_MODEL_GUIDANCE = {
|
|
|
20680
20723
|
]
|
|
20681
20724
|
},
|
|
20682
20725
|
generate_free_video: {
|
|
20683
|
-
summary: "
|
|
20726
|
+
summary: "Creates custom-script video using paid credits; legacy 'free' means free-form.",
|
|
20684
20727
|
useWhen: [
|
|
20685
20728
|
"genera un video libre creativo",
|
|
20686
20729
|
"la persona pide un video libre con un prompt o guion espec\xEDfico"
|
|
@@ -29188,197 +29231,271 @@ var ApiClient = class {
|
|
|
29188
29231
|
};
|
|
29189
29232
|
|
|
29190
29233
|
// src/index.ts
|
|
29191
|
-
var MCP_VERSION = "12.2.
|
|
29234
|
+
var MCP_VERSION = "12.2.8";
|
|
29192
29235
|
|
|
29193
29236
|
// src/meta-upload-app.ts
|
|
29194
29237
|
import { readFileSync } from "fs";
|
|
29238
|
+
import { z as z131 } from "zod";
|
|
29195
29239
|
import { registerAppResource, RESOURCE_MIME_TYPE } from "@modelcontextprotocol/ext-apps/server";
|
|
29196
|
-
|
|
29197
|
-
|
|
29198
|
-
|
|
29199
|
-
|
|
29200
|
-
|
|
29201
|
-
|
|
29202
|
-
|
|
29203
|
-
|
|
29204
|
-
|
|
29205
|
-
|
|
29206
|
-
|
|
29207
|
-
|
|
29208
|
-
|
|
29209
|
-
|
|
29210
|
-
|
|
29211
|
-
|
|
29212
|
-
|
|
29213
|
-
|
|
29214
|
-
|
|
29215
|
-
|
|
29216
|
-
|
|
29217
|
-
|
|
29218
|
-
|
|
29219
|
-
|
|
29220
|
-
|
|
29240
|
+
|
|
29241
|
+
// src/public-https.ts
|
|
29242
|
+
import { lookup } from "dns/promises";
|
|
29243
|
+
import https from "https";
|
|
29244
|
+
import { isIP } from "net";
|
|
29245
|
+
import { createGunzip, createInflate, createBrotliDecompress } from "zlib";
|
|
29246
|
+
|
|
29247
|
+
// ../../packages/utils/src/ip-classification.ts
|
|
29248
|
+
import net from "net";
|
|
29249
|
+
function parseIPv4(ip) {
|
|
29250
|
+
const parts = ip.split(".");
|
|
29251
|
+
if (parts.length !== 4) return null;
|
|
29252
|
+
const nums = [
|
|
29253
|
+
Number(parts[0]),
|
|
29254
|
+
Number(parts[1]),
|
|
29255
|
+
Number(parts[2]),
|
|
29256
|
+
Number(parts[3])
|
|
29257
|
+
];
|
|
29258
|
+
if (nums.some((n) => !Number.isInteger(n) || n < 0 || n > 255)) return null;
|
|
29259
|
+
return nums;
|
|
29260
|
+
}
|
|
29261
|
+
__name(parseIPv4, "parseIPv4");
|
|
29262
|
+
function classifyPrivateIPv4(ip, includeAdditionalRanges) {
|
|
29263
|
+
const p = parseIPv4(ip);
|
|
29264
|
+
if (!p) return false;
|
|
29265
|
+
const [a, b] = p;
|
|
29266
|
+
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);
|
|
29267
|
+
}
|
|
29268
|
+
__name(classifyPrivateIPv4, "classifyPrivateIPv4");
|
|
29269
|
+
function isPrivateIPv4(ip) {
|
|
29270
|
+
return classifyPrivateIPv4(ip, true);
|
|
29271
|
+
}
|
|
29272
|
+
__name(isPrivateIPv4, "isPrivateIPv4");
|
|
29273
|
+
function is6to4PrivateIPv4(ipv6) {
|
|
29274
|
+
const groups = ipv6.split(":");
|
|
29275
|
+
if (groups.length < 3) return false;
|
|
29276
|
+
return isPrivateIPv4Words(groups[1] || "0", groups[2] || "0");
|
|
29277
|
+
}
|
|
29278
|
+
__name(is6to4PrivateIPv4, "is6to4PrivateIPv4");
|
|
29279
|
+
function isPrivateIPv4Words(high, low) {
|
|
29280
|
+
const hi = parseInt(high, 16);
|
|
29281
|
+
const lo = parseInt(low, 16);
|
|
29282
|
+
if (Number.isNaN(hi) || Number.isNaN(lo)) return false;
|
|
29283
|
+
const a = hi >> 8 & 255;
|
|
29284
|
+
const b = hi & 255;
|
|
29285
|
+
const c = lo >> 8 & 255;
|
|
29286
|
+
const d = lo & 255;
|
|
29287
|
+
return isPrivateIPv4(`${a}.${b}.${c}.${d}`);
|
|
29288
|
+
}
|
|
29289
|
+
__name(isPrivateIPv4Words, "isPrivateIPv4Words");
|
|
29290
|
+
function isPrivateIPv6(ip) {
|
|
29291
|
+
let normalized;
|
|
29221
29292
|
try {
|
|
29222
|
-
|
|
29223
|
-
} catch
|
|
29224
|
-
|
|
29225
|
-
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}`);
|
|
29293
|
+
normalized = new URL(`http://[${ip.split("%", 1)[0]}]/`).hostname.slice(1, -1).toLowerCase();
|
|
29294
|
+
} catch {
|
|
29295
|
+
return false;
|
|
29226
29296
|
}
|
|
29297
|
+
if (normalized === "::" || /^::[0-9a-f]+$/.test(normalized)) return true;
|
|
29298
|
+
if (normalized.startsWith("64:ff9b:")) return true;
|
|
29299
|
+
if (/^fe[89a-f][0-9a-f]:/.test(normalized)) return true;
|
|
29300
|
+
if (/^f[cd][0-9a-f]{2}:/.test(normalized)) return true;
|
|
29301
|
+
if (normalized.startsWith("ff")) return true;
|
|
29302
|
+
if (/^2001:0*:/.test(normalized)) return true;
|
|
29303
|
+
if (normalized.startsWith("2001:db8:")) return true;
|
|
29304
|
+
if (normalized.startsWith("2002:") && is6to4PrivateIPv4(normalized)) return true;
|
|
29305
|
+
const embedded = /^::(?:ffff:(?:0:)?)?([0-9a-f]+):([0-9a-f]+)$/.exec(normalized);
|
|
29306
|
+
if (embedded?.[1] && embedded[2]) return isPrivateIPv4Words(embedded[1], embedded[2]);
|
|
29307
|
+
return false;
|
|
29227
29308
|
}
|
|
29228
|
-
__name(
|
|
29229
|
-
function
|
|
29230
|
-
const
|
|
29231
|
-
|
|
29232
|
-
|
|
29233
|
-
|
|
29234
|
-
function registerMediaUploadAppResource(server, resolveEndpoint) {
|
|
29235
|
-
const html = renderMediaUploadAppHtml(resolveEndpoint);
|
|
29236
|
-
const csp = buildMediaUploadAppCsp(resolveEndpoint);
|
|
29237
|
-
registerAppResource(server, MEDIA_UPLOAD_APP_NAME, MEDIA_UPLOAD_APP_RESOURCE_URI, {
|
|
29238
|
-
title: "Upload media",
|
|
29239
|
-
mimeType: RESOURCE_MIME_TYPE,
|
|
29240
|
-
_meta: {
|
|
29241
|
-
ui: {
|
|
29242
|
-
csp
|
|
29243
|
-
}
|
|
29244
|
-
}
|
|
29245
|
-
}, async () => ({
|
|
29246
|
-
contents: [
|
|
29247
|
-
{
|
|
29248
|
-
uri: MEDIA_UPLOAD_APP_RESOURCE_URI,
|
|
29249
|
-
mimeType: RESOURCE_MIME_TYPE,
|
|
29250
|
-
text: html,
|
|
29251
|
-
_meta: {
|
|
29252
|
-
ui: {
|
|
29253
|
-
csp
|
|
29254
|
-
}
|
|
29255
|
-
}
|
|
29256
|
-
}
|
|
29257
|
-
]
|
|
29258
|
-
}));
|
|
29309
|
+
__name(isPrivateIPv6, "isPrivateIPv6");
|
|
29310
|
+
function isPrivateIp(ip) {
|
|
29311
|
+
const kind2 = net.isIP(ip);
|
|
29312
|
+
if (kind2 === 4) return isPrivateIPv4(ip);
|
|
29313
|
+
if (kind2 === 6) return isPrivateIPv6(ip);
|
|
29314
|
+
return false;
|
|
29259
29315
|
}
|
|
29260
|
-
__name(
|
|
29261
|
-
|
|
29262
|
-
|
|
29263
|
-
|
|
29264
|
-
|
|
29265
|
-
|
|
29266
|
-
|
|
29267
|
-
|
|
29268
|
-
|
|
29269
|
-
|
|
29270
|
-
|
|
29271
|
-
|
|
29272
|
-
|
|
29273
|
-
return null;
|
|
29316
|
+
__name(isPrivateIp, "isPrivateIp");
|
|
29317
|
+
function looksLikeNonCanonicalIp(host) {
|
|
29318
|
+
if (net.isIP(host) !== 0) return false;
|
|
29319
|
+
if (/^\d+$/.test(host)) return true;
|
|
29320
|
+
if (/^0x[0-9a-f]+$/i.test(host)) return true;
|
|
29321
|
+
if (host.includes(".")) {
|
|
29322
|
+
const labels = host.split(".");
|
|
29323
|
+
const allNumeric = labels.every((label) => {
|
|
29324
|
+
if (label.length === 0) return false;
|
|
29325
|
+
return /^\d+$/.test(label) || // decimal / octal-with-leading-zero
|
|
29326
|
+
/^0x[0-9a-f]+$/i.test(label);
|
|
29327
|
+
});
|
|
29328
|
+
if (allNumeric) return true;
|
|
29274
29329
|
}
|
|
29330
|
+
return false;
|
|
29275
29331
|
}
|
|
29276
|
-
__name(
|
|
29277
|
-
|
|
29278
|
-
|
|
29279
|
-
|
|
29280
|
-
|
|
29332
|
+
__name(looksLikeNonCanonicalIp, "looksLikeNonCanonicalIp");
|
|
29333
|
+
|
|
29334
|
+
// src/public-host.ts
|
|
29335
|
+
function isPrivateHost(value) {
|
|
29336
|
+
let host = value.toLowerCase().replace(/^\[|\]$/g, "").replace(/\.$/, "");
|
|
29337
|
+
if (host === "localhost" || host === "metadata.google.internal") return true;
|
|
29338
|
+
if (host.includes(":")) {
|
|
29281
29339
|
try {
|
|
29282
|
-
|
|
29283
|
-
return url2.protocol === "https:" && url2.origin === origin && url2.pathname.startsWith("/assets/") && url2.pathname.length > "/assets/".length && !url2.username && !url2.password ? url2.href : null;
|
|
29340
|
+
host = new URL(`https://[${host}]/`).hostname.slice(1, -1);
|
|
29284
29341
|
} catch {
|
|
29285
|
-
return
|
|
29342
|
+
return true;
|
|
29286
29343
|
}
|
|
29287
|
-
}
|
|
29288
|
-
|
|
29289
|
-
|
|
29290
|
-
|
|
29291
|
-
|
|
29292
|
-
|
|
29293
|
-
|
|
29294
|
-
|
|
29295
|
-
|
|
29296
|
-
}
|
|
29297
|
-
|
|
29298
|
-
|
|
29299
|
-
|
|
29300
|
-
|
|
29301
|
-
|
|
29302
|
-
|
|
29303
|
-
|
|
29304
|
-
|
|
29305
|
-
|
|
29306
|
-
|
|
29307
|
-
|
|
29308
|
-
|
|
29309
|
-
] : [];
|
|
29310
|
-
});
|
|
29311
|
-
const links = media.links.flatMap((link) => {
|
|
29312
|
-
const url2 = safeUrl(link.url);
|
|
29313
|
-
return url2 ? [
|
|
29314
|
-
{
|
|
29315
|
-
url: url2,
|
|
29316
|
-
label: link.label?.slice(0, 120) ?? "Open result"
|
|
29317
|
-
}
|
|
29318
|
-
] : [];
|
|
29319
|
-
});
|
|
29320
|
-
return {
|
|
29321
|
-
state: images.length || videos.length || links.length ? "ready" : "empty",
|
|
29322
|
-
images,
|
|
29323
|
-
videos,
|
|
29324
|
-
links
|
|
29344
|
+
}
|
|
29345
|
+
return looksLikeNonCanonicalIp(host) || isPrivateIp(host);
|
|
29346
|
+
}
|
|
29347
|
+
__name(isPrivateHost, "isPrivateHost");
|
|
29348
|
+
|
|
29349
|
+
// src/public-https.ts
|
|
29350
|
+
var PublicHttpsError = class extends Error {
|
|
29351
|
+
static {
|
|
29352
|
+
__name(this, "PublicHttpsError");
|
|
29353
|
+
}
|
|
29354
|
+
code;
|
|
29355
|
+
constructor(code, message) {
|
|
29356
|
+
super(message), this.code = code;
|
|
29357
|
+
this.name = "PublicHttpsError";
|
|
29358
|
+
}
|
|
29359
|
+
};
|
|
29360
|
+
function pinnedLookup(pin) {
|
|
29361
|
+
return (_hostname, options, callback) => {
|
|
29362
|
+
if (options.all) callback(null, [
|
|
29363
|
+
pin
|
|
29364
|
+
]);
|
|
29365
|
+
else callback(null, pin.address, pin.family);
|
|
29325
29366
|
};
|
|
29326
29367
|
}
|
|
29327
|
-
__name(
|
|
29328
|
-
function
|
|
29368
|
+
__name(pinnedLookup, "pinnedLookup");
|
|
29369
|
+
async function resolvePublicAddress(url2, signal) {
|
|
29370
|
+
const hostname = url2.hostname.replace(/^\[|\]$/g, "");
|
|
29371
|
+
if (isPrivateHost(hostname)) throw new PublicHttpsError("INVALID_INPUT", "media URL host is private");
|
|
29372
|
+
const literalFamily = isIP(hostname);
|
|
29373
|
+
const records = literalFamily ? [
|
|
29374
|
+
{
|
|
29375
|
+
address: hostname,
|
|
29376
|
+
family: literalFamily
|
|
29377
|
+
}
|
|
29378
|
+
] : await new Promise((resolve4, reject) => {
|
|
29379
|
+
const abort = /* @__PURE__ */ __name(() => reject(signal.reason), "abort");
|
|
29380
|
+
if (signal.aborted) return abort();
|
|
29381
|
+
signal.addEventListener("abort", abort, {
|
|
29382
|
+
once: true
|
|
29383
|
+
});
|
|
29384
|
+
lookup(hostname, {
|
|
29385
|
+
all: true
|
|
29386
|
+
}).then(resolve4, reject).finally(() => {
|
|
29387
|
+
signal.removeEventListener("abort", abort);
|
|
29388
|
+
});
|
|
29389
|
+
});
|
|
29390
|
+
if (!Array.isArray(records) || !records.length || records.some((entry) => !isIP(entry.address) || isIP(entry.address) !== entry.family || isPrivateHost(entry.address))) {
|
|
29391
|
+
throw new PublicHttpsError("INVALID_INPUT", "media URL DNS answers are not exclusively public IP addresses");
|
|
29392
|
+
}
|
|
29393
|
+
const first = records[0];
|
|
29394
|
+
if (!first || first.family !== 4 && first.family !== 6) throw new PublicHttpsError("INVALID_INPUT", "media URL has no usable DNS address");
|
|
29329
29395
|
return {
|
|
29330
|
-
|
|
29331
|
-
|
|
29332
|
-
},
|
|
29333
|
-
"openai/outputTemplate": TASK_RESULTS_APP_RESOURCE_URI
|
|
29396
|
+
address: first.address,
|
|
29397
|
+
family: first.family
|
|
29334
29398
|
};
|
|
29335
29399
|
}
|
|
29336
|
-
__name(
|
|
29337
|
-
function
|
|
29338
|
-
|
|
29400
|
+
__name(resolvePublicAddress, "resolvePublicAddress");
|
|
29401
|
+
async function withPinnedHttpsResponse(url2, pin, signal, consume, tls = {}) {
|
|
29402
|
+
const agent = new https.Agent({
|
|
29403
|
+
keepAlive: false,
|
|
29404
|
+
lookup: pinnedLookup(pin),
|
|
29405
|
+
...tls
|
|
29406
|
+
});
|
|
29407
|
+
let body;
|
|
29408
|
+
let incoming;
|
|
29409
|
+
let req;
|
|
29410
|
+
try {
|
|
29411
|
+
const response = await new Promise((resolve4, reject) => {
|
|
29412
|
+
req = https.request(url2, {
|
|
29413
|
+
agent,
|
|
29414
|
+
signal,
|
|
29415
|
+
headers: {
|
|
29416
|
+
"accept-encoding": "gzip, deflate, br"
|
|
29417
|
+
}
|
|
29418
|
+
}, (res) => {
|
|
29419
|
+
try {
|
|
29420
|
+
incoming = res;
|
|
29421
|
+
const encoding = res.headers["content-encoding"]?.toLowerCase();
|
|
29422
|
+
const decoder = encoding === "gzip" ? createGunzip() : encoding === "deflate" ? createInflate() : encoding === "br" ? createBrotliDecompress() : void 0;
|
|
29423
|
+
body = decoder ? res.pipe(decoder) : res;
|
|
29424
|
+
if (decoder) res.on("error", (error) => decoder.destroy(error));
|
|
29425
|
+
const iterator = body[Symbol.asyncIterator]();
|
|
29426
|
+
const stream = new ReadableStream({
|
|
29427
|
+
async pull(controller) {
|
|
29428
|
+
try {
|
|
29429
|
+
const next = await iterator.next();
|
|
29430
|
+
if (next.done) controller.close();
|
|
29431
|
+
else controller.enqueue(new Uint8Array(next.value));
|
|
29432
|
+
} catch (error) {
|
|
29433
|
+
controller.error(error);
|
|
29434
|
+
}
|
|
29435
|
+
},
|
|
29436
|
+
cancel() {
|
|
29437
|
+
body?.destroy();
|
|
29438
|
+
res.destroy();
|
|
29439
|
+
}
|
|
29440
|
+
});
|
|
29441
|
+
const headers = new Headers();
|
|
29442
|
+
for (const [key2, value] of Object.entries(res.headers)) {
|
|
29443
|
+
if (value !== void 0) headers.set(key2, Array.isArray(value) ? value.join(", ") : value);
|
|
29444
|
+
}
|
|
29445
|
+
const status = res.statusCode ?? 502;
|
|
29446
|
+
resolve4(new Response([
|
|
29447
|
+
204,
|
|
29448
|
+
205,
|
|
29449
|
+
304
|
|
29450
|
+
].includes(status) ? null : stream, {
|
|
29451
|
+
status,
|
|
29452
|
+
headers
|
|
29453
|
+
}));
|
|
29454
|
+
} catch (error) {
|
|
29455
|
+
res.destroy();
|
|
29456
|
+
reject(error);
|
|
29457
|
+
}
|
|
29458
|
+
});
|
|
29459
|
+
req.on("error", reject);
|
|
29460
|
+
req.end();
|
|
29461
|
+
});
|
|
29462
|
+
if (response.status >= 300 && response.status < 400) throw new PublicHttpsError("DOWNLOAD_FAILED", "media URL redirects are not allowed");
|
|
29463
|
+
return await consume(response, url2);
|
|
29464
|
+
} finally {
|
|
29465
|
+
body?.destroy();
|
|
29466
|
+
incoming?.destroy();
|
|
29467
|
+
req?.destroy();
|
|
29468
|
+
agent.destroy();
|
|
29469
|
+
}
|
|
29339
29470
|
}
|
|
29340
|
-
__name(
|
|
29341
|
-
function
|
|
29342
|
-
|
|
29343
|
-
|
|
29344
|
-
|
|
29345
|
-
|
|
29346
|
-
|
|
29347
|
-
|
|
29348
|
-
|
|
29349
|
-
|
|
29350
|
-
|
|
29351
|
-
|
|
29352
|
-
|
|
29353
|
-
|
|
29354
|
-
|
|
29355
|
-
|
|
29356
|
-
|
|
29357
|
-
|
|
29358
|
-
|
|
29359
|
-
|
|
29360
|
-
|
|
29361
|
-
|
|
29362
|
-
|
|
29363
|
-
|
|
29364
|
-
}, async () => ({
|
|
29365
|
-
contents: [
|
|
29366
|
-
{
|
|
29367
|
-
uri: TASK_RESULTS_APP_RESOURCE_URI,
|
|
29368
|
-
mimeType: RESOURCE_MIME_TYPE2,
|
|
29369
|
-
text: html,
|
|
29370
|
-
_meta: meta
|
|
29371
|
-
}
|
|
29372
|
-
]
|
|
29373
|
-
}));
|
|
29471
|
+
__name(withPinnedHttpsResponse, "withPinnedHttpsResponse");
|
|
29472
|
+
async function withPublicHttpsResponse(rawUrl, timeoutMs, consume) {
|
|
29473
|
+
let url2;
|
|
29474
|
+
try {
|
|
29475
|
+
url2 = new URL(rawUrl);
|
|
29476
|
+
} catch {
|
|
29477
|
+
throw new PublicHttpsError("INVALID_INPUT", "media URL is not valid");
|
|
29478
|
+
}
|
|
29479
|
+
if (url2.protocol !== "https:" || url2.username || url2.password) throw new PublicHttpsError("INVALID_INPUT", "media URL must be HTTPS without embedded credentials");
|
|
29480
|
+
const controller = new AbortController();
|
|
29481
|
+
const timer = setTimeout(() => controller.abort(new Error("media download timed out")), timeoutMs);
|
|
29482
|
+
timer.unref();
|
|
29483
|
+
try {
|
|
29484
|
+
let pin;
|
|
29485
|
+
try {
|
|
29486
|
+
pin = await resolvePublicAddress(url2, controller.signal);
|
|
29487
|
+
} catch (error) {
|
|
29488
|
+
if (error instanceof PublicHttpsError || controller.signal.aborted) throw error;
|
|
29489
|
+
throw new PublicHttpsError("INVALID_INPUT", "media URL DNS lookup failed");
|
|
29490
|
+
}
|
|
29491
|
+
return await withPinnedHttpsResponse(url2, pin, controller.signal, consume);
|
|
29492
|
+
} finally {
|
|
29493
|
+
clearTimeout(timer);
|
|
29494
|
+
}
|
|
29374
29495
|
}
|
|
29375
|
-
__name(
|
|
29376
|
-
|
|
29377
|
-
// src/tools/tasks.ts
|
|
29378
|
-
import { z as z136 } from "zod";
|
|
29496
|
+
__name(withPublicHttpsResponse, "withPublicHttpsResponse");
|
|
29379
29497
|
|
|
29380
29498
|
// src/resolve-image.ts
|
|
29381
|
-
import { lookup as dnsLookup } from "dns/promises";
|
|
29382
29499
|
import { promises as fs, createReadStream } from "fs";
|
|
29383
29500
|
import * as path from "path";
|
|
29384
29501
|
import * as os from "os";
|
|
@@ -29425,53 +29542,6 @@ function sniffMime(buf) {
|
|
|
29425
29542
|
return null;
|
|
29426
29543
|
}
|
|
29427
29544
|
__name(sniffMime, "sniffMime");
|
|
29428
|
-
function isPrivateHost(host) {
|
|
29429
|
-
if (host === "localhost" || host === "0.0.0.0") return true;
|
|
29430
|
-
if (host === "metadata.google.internal" || host === "169.254.169.254") return true;
|
|
29431
|
-
if (/^127\./.test(host)) return true;
|
|
29432
|
-
if (/^10\./.test(host)) return true;
|
|
29433
|
-
if (/^192\.168\./.test(host)) return true;
|
|
29434
|
-
if (/^172\.(1[6-9]|2\d|3[0-1])\./.test(host)) return true;
|
|
29435
|
-
if (host.startsWith("169.254.")) return true;
|
|
29436
|
-
if (host === "[::1]" || host === "::1") return true;
|
|
29437
|
-
if (/^\[?0*:(?:0*:){6}0*1\]?$/i.test(host)) return true;
|
|
29438
|
-
const ipv4Mapped = host.match(/^\[?::ffff:([0-9a-f.:]+)\]?$/i);
|
|
29439
|
-
if (ipv4Mapped && ipv4Mapped[1]) {
|
|
29440
|
-
const inner = ipv4Mapped[1];
|
|
29441
|
-
if (inner.includes(".")) return isPrivateHost(inner);
|
|
29442
|
-
const parts = inner.split(":").map((h) => parseInt(h, 16));
|
|
29443
|
-
if (parts.length === 2 && parts.every((n) => !Number.isNaN(n))) {
|
|
29444
|
-
const bytes = [
|
|
29445
|
-
parts[0] >> 8,
|
|
29446
|
-
parts[0] & 255,
|
|
29447
|
-
parts[1] >> 8,
|
|
29448
|
-
parts[1] & 255
|
|
29449
|
-
];
|
|
29450
|
-
return isPrivateHost(bytes.join("."));
|
|
29451
|
-
}
|
|
29452
|
-
}
|
|
29453
|
-
if (/^\[?f[cd][0-9a-f]{2}:/i.test(host)) return true;
|
|
29454
|
-
if (/^\[?fe[89ab][0-9a-f]:/i.test(host)) return true;
|
|
29455
|
-
return false;
|
|
29456
|
-
}
|
|
29457
|
-
__name(isPrivateHost, "isPrivateHost");
|
|
29458
|
-
async function assertHostResolvesPublic(hostname) {
|
|
29459
|
-
let records;
|
|
29460
|
-
try {
|
|
29461
|
-
records = await dnsLookup(hostname, {
|
|
29462
|
-
all: true
|
|
29463
|
-
});
|
|
29464
|
-
} catch {
|
|
29465
|
-
if (process.env.NODE_ENV === "test" || process.env.VITEST) return;
|
|
29466
|
-
throw new ResolveImageError("INVALID_INPUT", `imageUrl DNS lookup failed for ${hostname}`);
|
|
29467
|
-
}
|
|
29468
|
-
for (const { address } of records) {
|
|
29469
|
-
if (isPrivateHost(address)) {
|
|
29470
|
-
throw new ResolveImageError("INVALID_INPUT", `imageUrl host resolves to a private address (${address})`);
|
|
29471
|
-
}
|
|
29472
|
-
}
|
|
29473
|
-
}
|
|
29474
|
-
__name(assertHostResolvesPublic, "assertHostResolvesPublic");
|
|
29475
29545
|
function expandUserPath(raw) {
|
|
29476
29546
|
let s = raw.trim();
|
|
29477
29547
|
if (s === "~") return os.homedir();
|
|
@@ -29545,48 +29615,37 @@ async function resolvePath(rawPath) {
|
|
|
29545
29615
|
}
|
|
29546
29616
|
__name(resolvePath, "resolvePath");
|
|
29547
29617
|
async function resolveImageUrl(rawUrl, maxBytes) {
|
|
29548
|
-
let u;
|
|
29549
|
-
try {
|
|
29550
|
-
u = new URL(rawUrl);
|
|
29551
|
-
} catch {
|
|
29552
|
-
throw new ResolveImageError("INVALID_INPUT", "imageUrl is not a valid URL");
|
|
29553
|
-
}
|
|
29554
|
-
if (u.protocol !== "https:") throw new ResolveImageError("INVALID_INPUT", "imageUrl must be HTTPS");
|
|
29555
|
-
if (isPrivateHost(u.hostname)) throw new ResolveImageError("INVALID_INPUT", "imageUrl host not allowed");
|
|
29556
|
-
await assertHostResolvesPublic(u.hostname);
|
|
29557
|
-
let res;
|
|
29558
29618
|
try {
|
|
29559
|
-
|
|
29560
|
-
|
|
29561
|
-
|
|
29619
|
+
return await withPublicHttpsResponse(rawUrl, 3e4, async (res) => {
|
|
29620
|
+
if (!res.ok) throw new ResolveImageError("IMAGE_DOWNLOAD_FAILED", `HTTP ${res.status}`);
|
|
29621
|
+
const cl = res.headers.get("content-length");
|
|
29622
|
+
if (cl && Number(cl) > maxBytes) {
|
|
29623
|
+
throw new ResolveImageError("IMAGE_TOO_LARGE", `content-length ${cl}`);
|
|
29624
|
+
}
|
|
29625
|
+
const ct = (res.headers.get("content-type") ?? "").split(";")[0]?.trim() ?? "";
|
|
29626
|
+
if (!ALLOWED_MIMES.has(ct)) throw new ResolveImageError("IMAGE_TYPE_UNSUPPORTED", `content-type ${ct}`);
|
|
29627
|
+
let buf;
|
|
29628
|
+
try {
|
|
29629
|
+
buf = Buffer.from(await readResponseBytes(res, maxBytes));
|
|
29630
|
+
} catch (error) {
|
|
29631
|
+
if (error instanceof ResponseBodyTooLargeError) {
|
|
29632
|
+
throw new ResolveImageError("IMAGE_TOO_LARGE", `image URL exceeds ${maxBytes} bytes`);
|
|
29633
|
+
}
|
|
29634
|
+
throw new ResolveImageError("IMAGE_DOWNLOAD_FAILED", "image response could not be read");
|
|
29635
|
+
}
|
|
29636
|
+
const sniffed = sniffMime(buf);
|
|
29637
|
+
if (!sniffed || sniffed !== ct) {
|
|
29638
|
+
throw new ResolveImageError("IMAGE_TYPE_UNSUPPORTED", `magic bytes (${sniffed ?? "unknown"}) do not match content-type (${ct})`);
|
|
29639
|
+
}
|
|
29640
|
+
return {
|
|
29641
|
+
buffer: buf,
|
|
29642
|
+
mimetype: sniffed
|
|
29643
|
+
};
|
|
29562
29644
|
});
|
|
29563
|
-
} catch (e) {
|
|
29564
|
-
throw new ResolveImageError("IMAGE_DOWNLOAD_FAILED", e.message);
|
|
29565
|
-
}
|
|
29566
|
-
if (!res.ok) throw new ResolveImageError("IMAGE_DOWNLOAD_FAILED", `HTTP ${res.status}`);
|
|
29567
|
-
const cl = res.headers.get("content-length");
|
|
29568
|
-
if (cl && Number(cl) > maxBytes) {
|
|
29569
|
-
throw new ResolveImageError("IMAGE_TOO_LARGE", `content-length ${cl}`);
|
|
29570
|
-
}
|
|
29571
|
-
const ct = (res.headers.get("content-type") ?? "").split(";")[0]?.trim() ?? "";
|
|
29572
|
-
if (!ALLOWED_MIMES.has(ct)) throw new ResolveImageError("IMAGE_TYPE_UNSUPPORTED", `content-type ${ct}`);
|
|
29573
|
-
let buf;
|
|
29574
|
-
try {
|
|
29575
|
-
buf = Buffer.from(await readResponseBytes(res, maxBytes));
|
|
29576
29645
|
} catch (error) {
|
|
29577
|
-
if (error instanceof
|
|
29578
|
-
|
|
29579
|
-
}
|
|
29580
|
-
throw new ResolveImageError("IMAGE_DOWNLOAD_FAILED", "image response could not be read");
|
|
29581
|
-
}
|
|
29582
|
-
const sniffed = sniffMime(buf);
|
|
29583
|
-
if (!sniffed || sniffed !== ct) {
|
|
29584
|
-
throw new ResolveImageError("IMAGE_TYPE_UNSUPPORTED", `magic bytes (${sniffed ?? "unknown"}) do not match content-type (${ct})`);
|
|
29646
|
+
if (error instanceof ResolveImageError) throw error;
|
|
29647
|
+
throw new ResolveImageError(error instanceof PublicHttpsError && error.code === "INVALID_INPUT" ? "INVALID_INPUT" : "IMAGE_DOWNLOAD_FAILED", error instanceof Error ? error.message : "image download failed");
|
|
29585
29648
|
}
|
|
29586
|
-
return {
|
|
29587
|
-
buffer: buf,
|
|
29588
|
-
mimetype: sniffed
|
|
29589
|
-
};
|
|
29590
29649
|
}
|
|
29591
29650
|
__name(resolveImageUrl, "resolveImageUrl");
|
|
29592
29651
|
async function resolveImages(images, opts = {
|
|
@@ -29617,6 +29676,262 @@ async function resolveOne(ref, transport, maxBytes) {
|
|
|
29617
29676
|
}
|
|
29618
29677
|
__name(resolveOne, "resolveOne");
|
|
29619
29678
|
|
|
29679
|
+
// src/validate-api-url.ts
|
|
29680
|
+
function assertSafePublicBaseUrl(envName, url2) {
|
|
29681
|
+
let u;
|
|
29682
|
+
try {
|
|
29683
|
+
u = new URL(url2);
|
|
29684
|
+
} catch {
|
|
29685
|
+
throw new Error(`${envName} is not a valid URL: ${url2}`);
|
|
29686
|
+
}
|
|
29687
|
+
if (u.protocol !== "https:") {
|
|
29688
|
+
throw new Error(`${envName} must use https (got ${u.protocol})`);
|
|
29689
|
+
}
|
|
29690
|
+
if (u.username || u.password || u.search || u.hash) {
|
|
29691
|
+
throw new Error(`${envName} must not contain credentials, query parameters, or fragments`);
|
|
29692
|
+
}
|
|
29693
|
+
if (isPrivateHost(u.hostname)) {
|
|
29694
|
+
throw new Error(`${envName} must not point to a private/loopback host: ${u.hostname}`);
|
|
29695
|
+
}
|
|
29696
|
+
}
|
|
29697
|
+
__name(assertSafePublicBaseUrl, "assertSafePublicBaseUrl");
|
|
29698
|
+
function assertSafeApiBaseUrl(url2) {
|
|
29699
|
+
assertSafePublicBaseUrl("PM_API_URL", url2);
|
|
29700
|
+
}
|
|
29701
|
+
__name(assertSafeApiBaseUrl, "assertSafeApiBaseUrl");
|
|
29702
|
+
|
|
29703
|
+
// src/app-origin.ts
|
|
29704
|
+
var PRODUCTMAKER_APP_ORIGIN = "https://productmaker.app";
|
|
29705
|
+
|
|
29706
|
+
// src/meta-upload-app.ts
|
|
29707
|
+
var MEDIA_UPLOAD_APP_RESOURCE_URI = "ui://media-upload/v1.html";
|
|
29708
|
+
var MEDIA_UPLOAD_APP_NAME = "ProductMaker media upload";
|
|
29709
|
+
var MEDIA_UPLOAD_APP_HTML_URL = new URL("./ui/meta-upload-app.html", import.meta.url);
|
|
29710
|
+
var PRODUCTMAKER_UPLOAD_STORAGE_ORIGIN = "https://825805a8113ddb98b68b9b0ddbb5d72a.r2.cloudflarestorage.com";
|
|
29711
|
+
var MEDIA_UPLOAD_CONNECT_DOMAINS = [
|
|
29712
|
+
"https://api.productmaker.app",
|
|
29713
|
+
PRODUCTMAKER_APP_ORIGIN,
|
|
29714
|
+
"https://www.productmaker.app",
|
|
29715
|
+
// The file bridge supplies temporary URLs. Keep this host-owned scope until
|
|
29716
|
+
// a real ChatGPT/library upload establishes every required exact origin.
|
|
29717
|
+
"https://*.oaiusercontent.com"
|
|
29718
|
+
];
|
|
29719
|
+
var STORAGE_ORIGINS_ERROR = "PM_MEDIA_UPLOAD_STORAGE_ORIGINS must be a JSON array of exact public HTTPS origins";
|
|
29720
|
+
function normalizeStorageOrigins(value) {
|
|
29721
|
+
try {
|
|
29722
|
+
const origins = z131.array(z131.string().trim().min(1)).max(16).parse(value);
|
|
29723
|
+
return [
|
|
29724
|
+
...new Set(origins.map((origin) => {
|
|
29725
|
+
assertSafePublicBaseUrl("PM_MEDIA_UPLOAD_STORAGE_ORIGINS", origin);
|
|
29726
|
+
const parsed = new URL(origin);
|
|
29727
|
+
if (parsed.pathname !== "/" || parsed.hostname.includes("*")) throw new Error(STORAGE_ORIGINS_ERROR);
|
|
29728
|
+
return parsed.origin;
|
|
29729
|
+
}))
|
|
29730
|
+
];
|
|
29731
|
+
} catch {
|
|
29732
|
+
throw new Error(STORAGE_ORIGINS_ERROR);
|
|
29733
|
+
}
|
|
29734
|
+
}
|
|
29735
|
+
__name(normalizeStorageOrigins, "normalizeStorageOrigins");
|
|
29736
|
+
function parseMediaUploadStorageOrigins(value) {
|
|
29737
|
+
if (value === void 0) return void 0;
|
|
29738
|
+
try {
|
|
29739
|
+
return normalizeStorageOrigins(JSON.parse(value));
|
|
29740
|
+
} catch {
|
|
29741
|
+
throw new Error(STORAGE_ORIGINS_ERROR);
|
|
29742
|
+
}
|
|
29743
|
+
}
|
|
29744
|
+
__name(parseMediaUploadStorageOrigins, "parseMediaUploadStorageOrigins");
|
|
29745
|
+
function buildMediaUploadAppCsp(resolveEndpoint, storageOrigins) {
|
|
29746
|
+
const resolveOrigin = new URL(resolveEndpoint).origin;
|
|
29747
|
+
const approvedStorage = storageOrigins === void 0 ? resolveOrigin === "https://api.productmaker.app" ? [
|
|
29748
|
+
PRODUCTMAKER_UPLOAD_STORAGE_ORIGIN
|
|
29749
|
+
] : [] : normalizeStorageOrigins(storageOrigins);
|
|
29750
|
+
return {
|
|
29751
|
+
connectDomains: [
|
|
29752
|
+
.../* @__PURE__ */ new Set([
|
|
29753
|
+
...MEDIA_UPLOAD_CONNECT_DOMAINS,
|
|
29754
|
+
...approvedStorage,
|
|
29755
|
+
resolveOrigin
|
|
29756
|
+
])
|
|
29757
|
+
],
|
|
29758
|
+
// This standalone HTML has no remote static assets. Local image previews
|
|
29759
|
+
// use File/createImageBitmap and data: URLs; host-library bytes use fetch.
|
|
29760
|
+
resourceDomains: []
|
|
29761
|
+
};
|
|
29762
|
+
}
|
|
29763
|
+
__name(buildMediaUploadAppCsp, "buildMediaUploadAppCsp");
|
|
29764
|
+
var MEDIA_UPLOAD_APP_CSP = buildMediaUploadAppCsp("https://api.productmaker.app/v1/uploaded-assets/sessions/resolve");
|
|
29765
|
+
function readMediaUploadAppHtml(htmlUrl = MEDIA_UPLOAD_APP_HTML_URL) {
|
|
29766
|
+
try {
|
|
29767
|
+
return readFileSync(htmlUrl, "utf8");
|
|
29768
|
+
} catch (error) {
|
|
29769
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
29770
|
+
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}`);
|
|
29771
|
+
}
|
|
29772
|
+
}
|
|
29773
|
+
__name(readMediaUploadAppHtml, "readMediaUploadAppHtml");
|
|
29774
|
+
function renderMediaUploadAppHtml(resolveEndpoint) {
|
|
29775
|
+
const encodedEndpoint = JSON.stringify(resolveEndpoint).slice(1, -1).replaceAll("<", "\\u003c");
|
|
29776
|
+
return readMediaUploadAppHtml().replace("__PRODUCTMAKER_RESOLVE_ENDPOINT__", encodedEndpoint);
|
|
29777
|
+
}
|
|
29778
|
+
__name(renderMediaUploadAppHtml, "renderMediaUploadAppHtml");
|
|
29779
|
+
function registerMediaUploadAppResource(server, resolveEndpoint, storageOrigins) {
|
|
29780
|
+
const html = renderMediaUploadAppHtml(resolveEndpoint);
|
|
29781
|
+
const csp = buildMediaUploadAppCsp(resolveEndpoint, storageOrigins);
|
|
29782
|
+
const meta = {
|
|
29783
|
+
ui: {
|
|
29784
|
+
csp
|
|
29785
|
+
},
|
|
29786
|
+
// OpenAI requires a dedicated per-plugin UI origin. Keep its compatibility
|
|
29787
|
+
// alias separate from the host-derived standard domain used by Claude.
|
|
29788
|
+
"openai/widgetDomain": PRODUCTMAKER_APP_ORIGIN,
|
|
29789
|
+
"openai/widgetCSP": {
|
|
29790
|
+
connect_domains: csp.connectDomains,
|
|
29791
|
+
resource_domains: csp.resourceDomains,
|
|
29792
|
+
// Trust only the existing secure-upload fallback for OpenAI external opens.
|
|
29793
|
+
// The current HTML uses an ordinary anchor; no navigation behavior changes.
|
|
29794
|
+
redirect_domains: [
|
|
29795
|
+
PRODUCTMAKER_APP_ORIGIN
|
|
29796
|
+
]
|
|
29797
|
+
}
|
|
29798
|
+
};
|
|
29799
|
+
registerAppResource(server, MEDIA_UPLOAD_APP_NAME, MEDIA_UPLOAD_APP_RESOURCE_URI, {
|
|
29800
|
+
title: "Upload media",
|
|
29801
|
+
mimeType: RESOURCE_MIME_TYPE,
|
|
29802
|
+
_meta: meta
|
|
29803
|
+
}, async () => ({
|
|
29804
|
+
contents: [
|
|
29805
|
+
{
|
|
29806
|
+
uri: MEDIA_UPLOAD_APP_RESOURCE_URI,
|
|
29807
|
+
mimeType: RESOURCE_MIME_TYPE,
|
|
29808
|
+
text: html,
|
|
29809
|
+
_meta: meta
|
|
29810
|
+
}
|
|
29811
|
+
]
|
|
29812
|
+
}));
|
|
29813
|
+
}
|
|
29814
|
+
__name(registerMediaUploadAppResource, "registerMediaUploadAppResource");
|
|
29815
|
+
|
|
29816
|
+
// src/task-results-app.ts
|
|
29817
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
29818
|
+
import { registerAppResource as registerAppResource2, RESOURCE_MIME_TYPE as RESOURCE_MIME_TYPE2 } from "@modelcontextprotocol/ext-apps/server";
|
|
29819
|
+
var TASK_RESULTS_APP_RESOURCE_URI = "ui://task-results/v1.html";
|
|
29820
|
+
var HTML_URL = new URL("./ui/task-results-app.html", import.meta.url);
|
|
29821
|
+
function assetOrigin(value) {
|
|
29822
|
+
if (!value) return null;
|
|
29823
|
+
try {
|
|
29824
|
+
const url2 = new URL(value);
|
|
29825
|
+
return url2.protocol === "https:" && !url2.username && !url2.password ? url2.origin : null;
|
|
29826
|
+
} catch {
|
|
29827
|
+
return null;
|
|
29828
|
+
}
|
|
29829
|
+
}
|
|
29830
|
+
__name(assetOrigin, "assetOrigin");
|
|
29831
|
+
function buildTaskResultsView(media, proxyBaseUrl) {
|
|
29832
|
+
const origin = assetOrigin(proxyBaseUrl);
|
|
29833
|
+
const safeUrl = /* @__PURE__ */ __name((value) => {
|
|
29834
|
+
if (!origin) return null;
|
|
29835
|
+
try {
|
|
29836
|
+
const url2 = new URL(value);
|
|
29837
|
+
return url2.protocol === "https:" && url2.origin === origin && url2.pathname.startsWith("/assets/") && url2.pathname.length > "/assets/".length && !url2.username && !url2.password ? url2.href : null;
|
|
29838
|
+
} catch {
|
|
29839
|
+
return null;
|
|
29840
|
+
}
|
|
29841
|
+
}, "safeUrl");
|
|
29842
|
+
const images = media.images.flatMap((image2, index) => {
|
|
29843
|
+
const url2 = safeUrl(image2.url);
|
|
29844
|
+
return url2 ? [
|
|
29845
|
+
{
|
|
29846
|
+
url: url2,
|
|
29847
|
+
alt: image2.alt?.slice(0, 160) ?? `ProductMaker image ${index + 1}`
|
|
29848
|
+
}
|
|
29849
|
+
] : [];
|
|
29850
|
+
});
|
|
29851
|
+
const videos = media.videos.flatMap((video) => {
|
|
29852
|
+
const url2 = safeUrl(video.url);
|
|
29853
|
+
return url2 ? [
|
|
29854
|
+
{
|
|
29855
|
+
url: url2,
|
|
29856
|
+
...video.label ? {
|
|
29857
|
+
label: video.label.slice(0, 120)
|
|
29858
|
+
} : {},
|
|
29859
|
+
...video.scriptRewritten === true ? {
|
|
29860
|
+
scriptRewritten: true
|
|
29861
|
+
} : {}
|
|
29862
|
+
}
|
|
29863
|
+
] : [];
|
|
29864
|
+
});
|
|
29865
|
+
const links = media.links.flatMap((link) => {
|
|
29866
|
+
const url2 = safeUrl(link.url);
|
|
29867
|
+
return url2 ? [
|
|
29868
|
+
{
|
|
29869
|
+
url: url2,
|
|
29870
|
+
label: link.label?.slice(0, 120) ?? "Open result"
|
|
29871
|
+
}
|
|
29872
|
+
] : [];
|
|
29873
|
+
});
|
|
29874
|
+
return {
|
|
29875
|
+
state: images.length || videos.length || links.length ? "ready" : "empty",
|
|
29876
|
+
images,
|
|
29877
|
+
videos,
|
|
29878
|
+
links
|
|
29879
|
+
};
|
|
29880
|
+
}
|
|
29881
|
+
__name(buildTaskResultsView, "buildTaskResultsView");
|
|
29882
|
+
function taskResultsToolMeta() {
|
|
29883
|
+
return {
|
|
29884
|
+
ui: {
|
|
29885
|
+
resourceUri: TASK_RESULTS_APP_RESOURCE_URI
|
|
29886
|
+
},
|
|
29887
|
+
"openai/outputTemplate": TASK_RESULTS_APP_RESOURCE_URI
|
|
29888
|
+
};
|
|
29889
|
+
}
|
|
29890
|
+
__name(taskResultsToolMeta, "taskResultsToolMeta");
|
|
29891
|
+
function renderTaskResultsAppHtml(proxyBaseUrl) {
|
|
29892
|
+
return readFileSync2(HTML_URL, "utf8").replace("__PRODUCTMAKER_ASSET_ORIGIN__", JSON.stringify(assetOrigin(proxyBaseUrl)).replaceAll("<", "\\u003c"));
|
|
29893
|
+
}
|
|
29894
|
+
__name(renderTaskResultsAppHtml, "renderTaskResultsAppHtml");
|
|
29895
|
+
function registerTaskResultsAppResource(server, proxyBaseUrl) {
|
|
29896
|
+
const origin = assetOrigin(proxyBaseUrl);
|
|
29897
|
+
const resourceDomains = origin ? [
|
|
29898
|
+
origin
|
|
29899
|
+
] : [];
|
|
29900
|
+
const html = renderTaskResultsAppHtml(proxyBaseUrl);
|
|
29901
|
+
const meta = {
|
|
29902
|
+
ui: {
|
|
29903
|
+
csp: {
|
|
29904
|
+
resourceDomains,
|
|
29905
|
+
connectDomains: []
|
|
29906
|
+
}
|
|
29907
|
+
},
|
|
29908
|
+
"openai/widgetDomain": PRODUCTMAKER_APP_ORIGIN,
|
|
29909
|
+
"openai/widgetCSP": {
|
|
29910
|
+
resource_domains: resourceDomains,
|
|
29911
|
+
connect_domains: []
|
|
29912
|
+
},
|
|
29913
|
+
"openai/widgetDescription": "Displays the actual ProductMaker images and videos with playback controls and download links."
|
|
29914
|
+
};
|
|
29915
|
+
registerAppResource2(server, "ProductMaker task results", TASK_RESULTS_APP_RESOURCE_URI, {
|
|
29916
|
+
title: "Your creative",
|
|
29917
|
+
mimeType: RESOURCE_MIME_TYPE2,
|
|
29918
|
+
_meta: meta
|
|
29919
|
+
}, async () => ({
|
|
29920
|
+
contents: [
|
|
29921
|
+
{
|
|
29922
|
+
uri: TASK_RESULTS_APP_RESOURCE_URI,
|
|
29923
|
+
mimeType: RESOURCE_MIME_TYPE2,
|
|
29924
|
+
text: html,
|
|
29925
|
+
_meta: meta
|
|
29926
|
+
}
|
|
29927
|
+
]
|
|
29928
|
+
}));
|
|
29929
|
+
}
|
|
29930
|
+
__name(registerTaskResultsAppResource, "registerTaskResultsAppResource");
|
|
29931
|
+
|
|
29932
|
+
// src/tools/tasks.ts
|
|
29933
|
+
import { z as z137 } from "zod";
|
|
29934
|
+
|
|
29620
29935
|
// src/status-poller.ts
|
|
29621
29936
|
var TERMINAL = /* @__PURE__ */ new Set([
|
|
29622
29937
|
"done",
|
|
@@ -29693,7 +30008,6 @@ __name(buildPollingContinuation, "buildPollingContinuation");
|
|
|
29693
30008
|
import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js";
|
|
29694
30009
|
|
|
29695
30010
|
// src/resolve-media.ts
|
|
29696
|
-
import { lookup as dnsLookup2 } from "dns/promises";
|
|
29697
30011
|
import { promises as fs2, createReadStream as createReadStream2 } from "fs";
|
|
29698
30012
|
import * as os2 from "os";
|
|
29699
30013
|
import * as path2 from "path";
|
|
@@ -29830,32 +30144,6 @@ async function readFileWithCap2(absPath) {
|
|
|
29830
30144
|
return Buffer.concat(chunks, total);
|
|
29831
30145
|
}
|
|
29832
30146
|
__name(readFileWithCap2, "readFileWithCap");
|
|
29833
|
-
async function assertPublicUrl(rawUrl) {
|
|
29834
|
-
let url2;
|
|
29835
|
-
try {
|
|
29836
|
-
url2 = new URL(rawUrl);
|
|
29837
|
-
} catch {
|
|
29838
|
-
throw new ResolveMediaError("INVALID_INPUT", "media URL is not valid");
|
|
29839
|
-
}
|
|
29840
|
-
if (url2.protocol !== "https:") throw new ResolveMediaError("INVALID_INPUT", "media URL must be HTTPS");
|
|
29841
|
-
if (url2.username || url2.password) throw new ResolveMediaError("INVALID_INPUT", "media URL must not contain embedded credentials");
|
|
29842
|
-
if (isPrivateHost(url2.hostname)) throw new ResolveMediaError("INVALID_INPUT", "media URL host is private");
|
|
29843
|
-
try {
|
|
29844
|
-
const records = await dnsLookup2(url2.hostname, {
|
|
29845
|
-
all: true
|
|
29846
|
-
});
|
|
29847
|
-
if (records.some((record) => isPrivateHost(record.address))) {
|
|
29848
|
-
throw new ResolveMediaError("INVALID_INPUT", "media URL resolves to a private address");
|
|
29849
|
-
}
|
|
29850
|
-
} catch (error) {
|
|
29851
|
-
if (error instanceof ResolveMediaError) throw error;
|
|
29852
|
-
if (process.env.NODE_ENV !== "test" && !process.env.VITEST) {
|
|
29853
|
-
throw new ResolveMediaError("INVALID_INPUT", "media URL DNS lookup failed");
|
|
29854
|
-
}
|
|
29855
|
-
}
|
|
29856
|
-
return url2;
|
|
29857
|
-
}
|
|
29858
|
-
__name(assertPublicUrl, "assertPublicUrl");
|
|
29859
30147
|
async function resolvePath2(rawPath, expectedKind) {
|
|
29860
30148
|
const absolutePath = path2.resolve(expandUserPath2(rawPath));
|
|
29861
30149
|
const extension = path2.extname(absolutePath).toLowerCase();
|
|
@@ -29899,46 +30187,48 @@ async function resolveMediaRef(ref, opts) {
|
|
|
29899
30187
|
return resolvePath2(ref.path, opts.expectedKind);
|
|
29900
30188
|
}
|
|
29901
30189
|
if (!ref.url) throw new ResolveMediaError("INVALID_INPUT", "media URL is empty");
|
|
29902
|
-
|
|
29903
|
-
|
|
29904
|
-
|
|
29905
|
-
|
|
29906
|
-
|
|
29907
|
-
|
|
29908
|
-
|
|
29909
|
-
|
|
29910
|
-
|
|
29911
|
-
|
|
29912
|
-
|
|
29913
|
-
|
|
29914
|
-
|
|
29915
|
-
|
|
29916
|
-
|
|
29917
|
-
|
|
29918
|
-
|
|
29919
|
-
|
|
29920
|
-
|
|
29921
|
-
|
|
29922
|
-
|
|
29923
|
-
|
|
29924
|
-
|
|
29925
|
-
|
|
29926
|
-
|
|
30190
|
+
try {
|
|
30191
|
+
return await withPublicHttpsResponse(ref.url, 12e4, async (response, url2) => {
|
|
30192
|
+
const extension = path2.extname(url2.pathname).toLowerCase();
|
|
30193
|
+
const format = MEDIA_FORMATS.find((entry) => entry.exts.some((candidate) => candidate === extension));
|
|
30194
|
+
if (format && opts.expectedKind && format.kind !== opts.expectedKind) {
|
|
30195
|
+
throw new ResolveMediaError("MEDIA_TYPE_UNSUPPORTED", "media URL extension does not match the requested type");
|
|
30196
|
+
}
|
|
30197
|
+
if (!response.ok) throw new ResolveMediaError("MEDIA_URL_UNAVAILABLE", `media URL returned HTTP ${response.status}`);
|
|
30198
|
+
const contentLength = Number(response.headers.get("content-length") ?? 0);
|
|
30199
|
+
if (contentLength > MAX_MEDIA_BYTES) {
|
|
30200
|
+
void response.body?.cancel().catch(() => void 0);
|
|
30201
|
+
throw new ResolveMediaError("MEDIA_TOO_LARGE", `media URL exceeds ${MAX_MEDIA_BYTES} bytes`);
|
|
30202
|
+
}
|
|
30203
|
+
const buffer = await readResponseWithCap(response);
|
|
30204
|
+
const sniffed = sniffMime2(buffer, extension);
|
|
30205
|
+
if (!sniffed) throw new ResolveMediaError("MEDIA_TYPE_UNSUPPORTED", "media URL magic bytes are unsupported");
|
|
30206
|
+
const kind2 = sniffed.startsWith("video/") ? "video" : "image";
|
|
30207
|
+
if (opts.expectedKind && kind2 !== opts.expectedKind) {
|
|
30208
|
+
throw new ResolveMediaError("MEDIA_TYPE_UNSUPPORTED", `expected ${opts.expectedKind} but received ${kind2}`);
|
|
30209
|
+
}
|
|
30210
|
+
if (format && sniffed !== format.mime) {
|
|
30211
|
+
throw new ResolveMediaError("MEDIA_TYPE_UNSUPPORTED", `media URL magic bytes do not match ${format.mime}`);
|
|
30212
|
+
}
|
|
30213
|
+
const filename = path2.basename(url2.pathname) || `media.${kind2 === "video" ? "mp4" : "jpg"}`;
|
|
30214
|
+
return {
|
|
30215
|
+
kind: kind2,
|
|
30216
|
+
mimetype: sniffed,
|
|
30217
|
+
filename,
|
|
30218
|
+
buffer
|
|
30219
|
+
};
|
|
30220
|
+
});
|
|
30221
|
+
} catch (error) {
|
|
30222
|
+
if (error instanceof ResolveMediaError) throw error;
|
|
30223
|
+
throw new ResolveMediaError(error instanceof PublicHttpsError && error.code === "INVALID_INPUT" ? "INVALID_INPUT" : "MEDIA_URL_UNAVAILABLE", error instanceof Error ? error.message : "media download failed");
|
|
29927
30224
|
}
|
|
29928
|
-
const filename = path2.basename(url2.pathname) || `media.${kind2 === "video" ? "mp4" : "jpg"}`;
|
|
29929
|
-
return {
|
|
29930
|
-
kind: kind2,
|
|
29931
|
-
mimetype: sniffed,
|
|
29932
|
-
filename,
|
|
29933
|
-
buffer
|
|
29934
|
-
};
|
|
29935
30225
|
}
|
|
29936
30226
|
__name(resolveMediaRef, "resolveMediaRef");
|
|
29937
30227
|
|
|
29938
30228
|
// src/tools/shared.ts
|
|
29939
|
-
import { z as
|
|
29940
|
-
var Lang =
|
|
29941
|
-
var Country =
|
|
30229
|
+
import { z as z132 } from "zod";
|
|
30230
|
+
var Lang = z132.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);
|
|
30231
|
+
var Country = z132.string().min(2).max(3);
|
|
29942
30232
|
var LandingStrategyEnum = LandingStrategySchema;
|
|
29943
30233
|
var LandingImageFormatEnum = LandingImageFormatSchema;
|
|
29944
30234
|
var VideoPresetEnum = VideoPreset.extract([
|
|
@@ -30042,7 +30332,7 @@ function animationStyleApplies(videoPreset, angleNarratives) {
|
|
|
30042
30332
|
return ANIMATED_VIDEO_PRESETS.has(videoPreset ?? "") || (angleNarratives ?? []).some((n) => ANIMATED_NARRATIVE_STYLES.has(n));
|
|
30043
30333
|
}
|
|
30044
30334
|
__name(animationStyleApplies, "animationStyleApplies");
|
|
30045
|
-
var VideoTierEnum =
|
|
30335
|
+
var VideoTierEnum = z132.enum([
|
|
30046
30336
|
"basic"
|
|
30047
30337
|
]);
|
|
30048
30338
|
function resolveVideoTier(_tier) {
|
|
@@ -30100,10 +30390,10 @@ var ImageCreativeStyleEnum = ImageCreativeStyle;
|
|
|
30100
30390
|
var ImageCreativeArchetypeEnum = ImageCreativeArchetype;
|
|
30101
30391
|
var ImageCreativeVisualTreatmentEnum = ImageCreativeVisualTreatment;
|
|
30102
30392
|
var ImageCreativeAspectRatioEnum = ImageCreativeAspectRatio;
|
|
30103
|
-
var ImageRefSchema =
|
|
30104
|
-
url:
|
|
30105
|
-
path:
|
|
30106
|
-
assetId:
|
|
30393
|
+
var ImageRefSchema = z132.object({
|
|
30394
|
+
url: z132.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.'),
|
|
30395
|
+
path: z132.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`.'),
|
|
30396
|
+
assetId: z132.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.")
|
|
30107
30397
|
}).refine((v) => [
|
|
30108
30398
|
v.url,
|
|
30109
30399
|
v.path,
|
|
@@ -30111,11 +30401,11 @@ var ImageRefSchema = z131.object({
|
|
|
30111
30401
|
].filter(Boolean).length === 1, {
|
|
30112
30402
|
message: "each image entry must have exactly one of `url`, `path`, or `assetId`"
|
|
30113
30403
|
});
|
|
30114
|
-
var ImagesInput =
|
|
30404
|
+
var ImagesInput = z132.array(ImageRefSchema).min(1).max(5).superRefine((images, ctx) => {
|
|
30115
30405
|
const assetCount = images.filter((image2) => Boolean(image2.assetId)).length;
|
|
30116
30406
|
if (assetCount > 0 && assetCount !== images.length) {
|
|
30117
30407
|
ctx.addIssue({
|
|
30118
|
-
code:
|
|
30408
|
+
code: z132.ZodIssueCode.custom,
|
|
30119
30409
|
message: "all image entries must use assetId, or all must use URL/path sources"
|
|
30120
30410
|
});
|
|
30121
30411
|
}
|
|
@@ -30196,14 +30486,14 @@ async function submitCreative(api, opts) {
|
|
|
30196
30486
|
});
|
|
30197
30487
|
}
|
|
30198
30488
|
__name(submitCreative, "submitCreative");
|
|
30199
|
-
var ResearchTargetCountryEnum =
|
|
30489
|
+
var ResearchTargetCountryEnum = z132.enum([
|
|
30200
30490
|
"CO",
|
|
30201
30491
|
"MX",
|
|
30202
30492
|
"PE",
|
|
30203
30493
|
"EC",
|
|
30204
30494
|
"CL"
|
|
30205
30495
|
]);
|
|
30206
|
-
var ResearchCategoryEnum =
|
|
30496
|
+
var ResearchCategoryEnum = z132.enum([
|
|
30207
30497
|
"auto",
|
|
30208
30498
|
"automotive",
|
|
30209
30499
|
"kitchen",
|
|
@@ -30998,7 +31288,7 @@ function approveRefusalMessage(reason) {
|
|
|
30998
31288
|
__name(approveRefusalMessage, "approveRefusalMessage");
|
|
30999
31289
|
|
|
31000
31290
|
// src/tools/task-outputs.ts
|
|
31001
|
-
import { z as
|
|
31291
|
+
import { z as z133 } from "zod";
|
|
31002
31292
|
|
|
31003
31293
|
// src/asset-proxy.ts
|
|
31004
31294
|
import { createHmac, timingSafeEqual } from "crypto";
|
|
@@ -31182,12 +31472,12 @@ function serviceVideoResultMedia(result) {
|
|
|
31182
31472
|
};
|
|
31183
31473
|
}
|
|
31184
31474
|
__name(serviceVideoResultMedia, "serviceVideoResultMedia");
|
|
31185
|
-
var CreativeVariantIdentity =
|
|
31186
|
-
variantTaskId:
|
|
31187
|
-
sourceTaskId:
|
|
31475
|
+
var CreativeVariantIdentity = z133.object({
|
|
31476
|
+
variantTaskId: z133.string().uuid(),
|
|
31477
|
+
sourceTaskId: z133.string().uuid(),
|
|
31188
31478
|
kind: TaskCreativeVariantKind,
|
|
31189
31479
|
status: TaskStatusSchema,
|
|
31190
|
-
error:
|
|
31480
|
+
error: z133.string().optional()
|
|
31191
31481
|
});
|
|
31192
31482
|
function proxifyOutputs(outputs, proxyBaseUrl) {
|
|
31193
31483
|
if (!proxyBaseUrl || !isProxyEnabled()) return outputs;
|
|
@@ -31791,29 +32081,29 @@ ${parsed.data.error ? `${escapeMarkdownText(parsed.data.error)}
|
|
|
31791
32081
|
__name(renderVideoScriptMarkdown, "renderVideoScriptMarkdown");
|
|
31792
32082
|
|
|
31793
32083
|
// src/tools/media-presentation.ts
|
|
31794
|
-
import { z as
|
|
31795
|
-
var HttpsPresentationUrlSchema =
|
|
32084
|
+
import { z as z134 } from "zod";
|
|
32085
|
+
var HttpsPresentationUrlSchema = z134.string().url().max(2048).refine((value) => new URL(value).protocol === "https:");
|
|
31796
32086
|
var ProductMakerPreviewUrlSchema = HttpsPresentationUrlSchema.refine((value) => {
|
|
31797
32087
|
const hostname = new URL(value).hostname.toLowerCase();
|
|
31798
32088
|
return hostname === "productmaker.app" || hostname.endsWith(".productmaker.app");
|
|
31799
32089
|
}, "preview URL must be owned by ProductMaker");
|
|
31800
|
-
var positiveIntAtMost = /* @__PURE__ */ __name((maximum) =>
|
|
31801
|
-
var PresentationImageSchema =
|
|
31802
|
-
kind:
|
|
32090
|
+
var positiveIntAtMost = /* @__PURE__ */ __name((maximum) => z134.number().int().positive().max(maximum), "positiveIntAtMost");
|
|
32091
|
+
var PresentationImageSchema = z134.object({
|
|
32092
|
+
kind: z134.literal("image"),
|
|
31803
32093
|
url: HttpsPresentationUrlSchema,
|
|
31804
|
-
mimeType:
|
|
32094
|
+
mimeType: z134.enum([
|
|
31805
32095
|
"image/jpeg",
|
|
31806
32096
|
"image/png"
|
|
31807
32097
|
]),
|
|
31808
32098
|
byteSize: positiveIntAtMost(5 * 1024 * 1024),
|
|
31809
32099
|
width: positiveIntAtMost(16384),
|
|
31810
32100
|
height: positiveIntAtMost(16384),
|
|
31811
|
-
alt:
|
|
32101
|
+
alt: z134.string().trim().min(1).max(240)
|
|
31812
32102
|
}).strict();
|
|
31813
32103
|
var McpMediaPresentationSchema = PresentationImageSchema;
|
|
31814
|
-
var GalleryImageSchema =
|
|
32104
|
+
var GalleryImageSchema = z134.object({
|
|
31815
32105
|
url: ProductMakerPreviewUrlSchema,
|
|
31816
|
-
alt:
|
|
32106
|
+
alt: z134.string().trim().min(1).max(240)
|
|
31817
32107
|
});
|
|
31818
32108
|
var MCP_GALLERY_IMAGE_MAX_BYTES = 10 * 1024 * 1024;
|
|
31819
32109
|
async function imageBlocks(image2, transport) {
|
|
@@ -31870,12 +32160,12 @@ async function buildMcpMediaPresentationContent(rawPresentation, transport = "ht
|
|
|
31870
32160
|
];
|
|
31871
32161
|
}
|
|
31872
32162
|
__name(buildMcpMediaPresentationContent, "buildMcpMediaPresentationContent");
|
|
31873
|
-
var OwnedImagePreviewSchema =
|
|
32163
|
+
var OwnedImagePreviewSchema = z134.object({
|
|
31874
32164
|
url: HttpsPresentationUrlSchema.refine((value) => {
|
|
31875
32165
|
const url2 = new URL(value);
|
|
31876
32166
|
return !url2.username && !url2.password && !isPrivateHost(url2.hostname);
|
|
31877
32167
|
}, "Preview URL must be public HTTPS without credentials"),
|
|
31878
|
-
alt:
|
|
32168
|
+
alt: z134.string().trim().min(1).max(240)
|
|
31879
32169
|
});
|
|
31880
32170
|
async function buildMcpImagePreviewContent(rawPreview, transport = "http") {
|
|
31881
32171
|
const preview2 = OwnedImagePreviewSchema.safeParse(rawPreview);
|
|
@@ -32171,7 +32461,7 @@ async function observeMcpMediaDelivery(fingerprint, work) {
|
|
|
32171
32461
|
__name(observeMcpMediaDelivery, "observeMcpMediaDelivery");
|
|
32172
32462
|
|
|
32173
32463
|
// src/tools/animated-video.ts
|
|
32174
|
-
import { z as
|
|
32464
|
+
import { z as z135 } from "zod";
|
|
32175
32465
|
|
|
32176
32466
|
// src/tools/paid-confirmation.ts
|
|
32177
32467
|
function describeMarketStudyChoice(enabled) {
|
|
@@ -32420,30 +32710,30 @@ function hookVariantsLabel(count) {
|
|
|
32420
32710
|
return count === 1 ? "1 versi\xF3n del gancho" : `${count} versiones del gancho`;
|
|
32421
32711
|
}
|
|
32422
32712
|
__name(hookVariantsLabel, "hookVariantsLabel");
|
|
32423
|
-
var DurationInput =
|
|
32424
|
-
var CreateAnimatedVideoInput =
|
|
32425
|
-
action:
|
|
32713
|
+
var DurationInput = z135.union(MOTION_GRAPHICS_DURATIONS.map((d) => z135.literal(d)));
|
|
32714
|
+
var CreateAnimatedVideoInput = z135.object({
|
|
32715
|
+
action: z135.enum([
|
|
32426
32716
|
"prepare",
|
|
32427
32717
|
"status",
|
|
32428
32718
|
"cancel",
|
|
32429
32719
|
"generate"
|
|
32430
32720
|
]).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."),
|
|
32431
|
-
brandId:
|
|
32432
|
-
websiteCaptureId:
|
|
32433
|
-
generationRequestId:
|
|
32434
|
-
confirmed:
|
|
32721
|
+
brandId: z135.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."),
|
|
32722
|
+
websiteCaptureId: z135.string().uuid().optional().describe("ID de la captura preparada. Obligatorio para status/cancel/generate; no inventarlo."),
|
|
32723
|
+
generationRequestId: z135.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."),
|
|
32724
|
+
confirmed: z135.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."),
|
|
32435
32725
|
// Plain text, not `.url()`: users type «cal.com», and the web accepts it.
|
|
32436
32726
|
// normalizeWebAddress (shared with the web) adds https:// and refuses
|
|
32437
32727
|
// anything that is not a public http(s) address.
|
|
32438
|
-
landingUrl:
|
|
32728
|
+
landingUrl: z135.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."),
|
|
32439
32729
|
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.'),
|
|
32440
32730
|
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.`),
|
|
32441
32731
|
language: MotionGraphicsLanguageSchema.optional().describe('Idioma de los textos del video: "es", "en" o "pt".'),
|
|
32442
32732
|
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.'),
|
|
32443
32733
|
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".'),
|
|
32444
|
-
notes:
|
|
32734
|
+
notes: z135.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."),
|
|
32445
32735
|
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.`),
|
|
32446
|
-
music:
|
|
32736
|
+
music: z135.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.")
|
|
32447
32737
|
}).strict();
|
|
32448
32738
|
function captureNextAction(capture) {
|
|
32449
32739
|
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.";
|
|
@@ -32496,33 +32786,33 @@ var FORMAT_LABELS = {
|
|
|
32496
32786
|
square: "cuadrado 1:1",
|
|
32497
32787
|
horizontal: "horizontal 16:9"
|
|
32498
32788
|
};
|
|
32499
|
-
var AnimatedVideoResultSchema =
|
|
32500
|
-
status:
|
|
32789
|
+
var AnimatedVideoResultSchema = z135.object({
|
|
32790
|
+
status: z135.enum([
|
|
32501
32791
|
"pending",
|
|
32502
32792
|
"done",
|
|
32503
32793
|
"error"
|
|
32504
32794
|
]),
|
|
32505
|
-
serviceMode:
|
|
32506
|
-
finalMp4Url:
|
|
32507
|
-
adCopy:
|
|
32508
|
-
adCopies:
|
|
32509
|
-
error:
|
|
32510
|
-
motionGraphics:
|
|
32795
|
+
serviceMode: z135.literal("motion_graphics"),
|
|
32796
|
+
finalMp4Url: z135.string().optional(),
|
|
32797
|
+
adCopy: z135.string().optional(),
|
|
32798
|
+
adCopies: z135.array(z135.string()).optional(),
|
|
32799
|
+
error: z135.string().optional(),
|
|
32800
|
+
motionGraphics: z135.object({
|
|
32511
32801
|
stage: MotionGraphicsStageSchema,
|
|
32512
32802
|
format: MotionGraphicsFormatSchema,
|
|
32513
|
-
durationSec:
|
|
32514
|
-
width:
|
|
32515
|
-
height:
|
|
32803
|
+
durationSec: z135.number(),
|
|
32804
|
+
width: z135.number().optional(),
|
|
32805
|
+
height: z135.number().optional(),
|
|
32516
32806
|
language: MotionGraphicsLanguageSchema.optional(),
|
|
32517
32807
|
videoQuality: MotionGraphicsVideoQualitySchema.optional().catch(void 0),
|
|
32518
32808
|
videoResolution: MotionGraphicsVideoResolutionSchema.optional().catch(void 0),
|
|
32519
|
-
creativeBatch:
|
|
32809
|
+
creativeBatch: z135.object({
|
|
32520
32810
|
index: MotionGraphicsHookVariantsSchema,
|
|
32521
32811
|
count: MotionGraphicsHookVariantsSchema,
|
|
32522
|
-
tasks:
|
|
32523
|
-
taskId:
|
|
32812
|
+
tasks: z135.array(z135.object({
|
|
32813
|
+
taskId: z135.string().uuid(),
|
|
32524
32814
|
index: MotionGraphicsHookVariantsSchema,
|
|
32525
|
-
status:
|
|
32815
|
+
status: z135.enum([
|
|
32526
32816
|
"queued",
|
|
32527
32817
|
"processing",
|
|
32528
32818
|
"done",
|
|
@@ -32530,29 +32820,29 @@ var AnimatedVideoResultSchema = z134.object({
|
|
|
32530
32820
|
])
|
|
32531
32821
|
})).max(MOTION_GRAPHICS_HOOK_VARIANTS_MAX)
|
|
32532
32822
|
}).optional().catch(void 0),
|
|
32533
|
-
queuedAt:
|
|
32534
|
-
jobsAhead:
|
|
32535
|
-
stageStartedAt:
|
|
32536
|
-
renderProgress:
|
|
32823
|
+
queuedAt: z135.string().optional(),
|
|
32824
|
+
jobsAhead: z135.number().int().nonnegative().optional(),
|
|
32825
|
+
stageStartedAt: z135.string().optional(),
|
|
32826
|
+
renderProgress: z135.number().optional(),
|
|
32537
32827
|
// AI clip fields (absent on a video paid before clips, or from an API
|
|
32538
32828
|
// that predates them). Lenient: an unreadable value is dropped
|
|
32539
32829
|
// instead of hiding the status.
|
|
32540
|
-
clipCount:
|
|
32541
|
-
clipsUsed:
|
|
32542
|
-
clipPriceCents:
|
|
32543
|
-
clipsRefunded:
|
|
32830
|
+
clipCount: z135.number().int().nonnegative().optional().catch(void 0),
|
|
32831
|
+
clipsUsed: z135.number().int().nonnegative().optional().catch(void 0),
|
|
32832
|
+
clipPriceCents: z135.number().finite().nonnegative().optional().catch(void 0),
|
|
32833
|
+
clipsRefunded: z135.number().int().nonnegative().optional().catch(void 0),
|
|
32544
32834
|
// Hook versions (absent from an API or a video before them): the
|
|
32545
32835
|
// count ordered and, once done, the signed URLs of the EXTRA versions
|
|
32546
32836
|
// (hook 2, hook 3); `finalMp4Url` is hook 1.
|
|
32547
|
-
hookVariants:
|
|
32548
|
-
cleanVideoUrl:
|
|
32549
|
-
cleanVariantUrls:
|
|
32550
|
-
variantUrls:
|
|
32837
|
+
hookVariants: z135.number().int().min(1).max(MOTION_GRAPHICS_HOOK_VARIANTS_MAX).optional().catch(void 0),
|
|
32838
|
+
cleanVideoUrl: z135.string().url().optional().catch(void 0),
|
|
32839
|
+
cleanVariantUrls: z135.array(z135.string().url()).max(MOTION_GRAPHICS_HOOK_VARIANTS_MAX - 1).optional().catch(void 0),
|
|
32840
|
+
variantUrls: z135.array(z135.string().min(1)).max(MOTION_GRAPHICS_HOOK_VARIANTS_MAX - 1).optional().catch(void 0),
|
|
32551
32841
|
// Extra hook versions given back, as the task recorded it once the
|
|
32552
32842
|
// refunds landed (absent until then, and from an API before it).
|
|
32553
|
-
variantsRefunded:
|
|
32554
|
-
activity:
|
|
32555
|
-
previewUrl:
|
|
32843
|
+
variantsRefunded: z135.number().int().nonnegative().optional().catch(void 0),
|
|
32844
|
+
activity: z135.array(MotionGraphicsActivitySchema).optional().catch(void 0),
|
|
32845
|
+
previewUrl: z135.string().optional()
|
|
32556
32846
|
}).passthrough()
|
|
32557
32847
|
}).passthrough();
|
|
32558
32848
|
function parseAnimatedVideoResult(value) {
|
|
@@ -32831,7 +33121,7 @@ function registerAnimatedVideoTools(server, api) {
|
|
|
32831
33121
|
nowMs: 0
|
|
32832
33122
|
})
|
|
32833
33123
|
});
|
|
32834
|
-
const parsedTaskIds =
|
|
33124
|
+
const parsedTaskIds = z135.array(z135.string().uuid()).min(1).max(MOTION_GRAPHICS_HOOK_VARIANTS_MAX).safeParse(created.taskIds);
|
|
32835
33125
|
const taskIds = parsedTaskIds.success && parsedTaskIds.data[0] === created.taskId ? parsedTaskIds.data : void 0;
|
|
32836
33126
|
const receipt = {
|
|
32837
33127
|
taskId: created.taskId,
|
|
@@ -32873,10 +33163,10 @@ function registerAnimatedVideoTools(server, api) {
|
|
|
32873
33163
|
__name(registerAnimatedVideoTools, "registerAnimatedVideoTools");
|
|
32874
33164
|
|
|
32875
33165
|
// src/tools/winning-product-image.ts
|
|
32876
|
-
import { z as
|
|
32877
|
-
var WinnerReferenceInput =
|
|
32878
|
-
runId:
|
|
32879
|
-
candidateSourceId:
|
|
33166
|
+
import { z as z136 } from "zod";
|
|
33167
|
+
var WinnerReferenceInput = z136.object({
|
|
33168
|
+
runId: z136.string().uuid(),
|
|
33169
|
+
candidateSourceId: z136.string().trim().min(1).max(160)
|
|
32880
33170
|
}).strict().describe("Referencia opaca de un producto devuelto por find_winning_products/get_product_research_run.");
|
|
32881
33171
|
async function resolveWinnerImages(api, winnerReference, images) {
|
|
32882
33172
|
if (!winnerReference) return images;
|
|
@@ -32913,62 +33203,62 @@ var createProductTaskSingleOutputDescription = renderCreateProductTaskAvoidWhenF
|
|
|
32913
33203
|
var CREATE_SERVICE_VIDEO_ON_MCP = PRODUCTMAKER_MCP_CAPABILITY_NAMES.includes("create_service_video");
|
|
32914
33204
|
var listTasksGuidance = getProductMakerCapabilityModelGuidance("list_tasks");
|
|
32915
33205
|
var editTaskDraftGuidance = getProductMakerCapabilityModelGuidance("edit_task_draft");
|
|
32916
|
-
var CreateProductTaskInput =
|
|
33206
|
+
var CreateProductTaskInput = z137.object({
|
|
32917
33207
|
images: ImagesInput.optional().describe("1\u20135 fotos del mismo producto. Omitir \xFAnicamente cuando projectId apunta a un proyecto propio con imagen guardada."),
|
|
32918
33208
|
winnerReference: WinnerReferenceInput.optional().describe(WINNER_IMAGE_CHOICE_GUIDANCE),
|
|
32919
|
-
projectId:
|
|
32920
|
-
productUrl:
|
|
33209
|
+
projectId: z137.string().uuid().optional().describe("Opcional. Proyecto guiado propio. Permite continuar sin volver a subir la imagen guardada; nunca evita cr\xE9ditos, cuotas ni confirmaciones."),
|
|
33210
|
+
productUrl: z137.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."),
|
|
32921
33211
|
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.'),
|
|
32922
33212
|
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.'),
|
|
32923
|
-
price:
|
|
32924
|
-
compareAtPrice:
|
|
32925
|
-
offer:
|
|
32926
|
-
paymentMethods:
|
|
32927
|
-
productContext:
|
|
32928
|
-
productLabel:
|
|
33213
|
+
price: z137.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.'),
|
|
33214
|
+
compareAtPrice: z137.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".'),
|
|
33215
|
+
offer: z137.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.'),
|
|
33216
|
+
paymentMethods: z137.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.'),
|
|
33217
|
+
productContext: z137.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."),
|
|
33218
|
+
productLabel: z137.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.'),
|
|
32929
33219
|
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.'),
|
|
32930
33220
|
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.'),
|
|
32931
|
-
durationSeconds:
|
|
32932
|
-
|
|
32933
|
-
|
|
33221
|
+
durationSeconds: z137.union([
|
|
33222
|
+
z137.literal(15),
|
|
33223
|
+
z137.literal(30)
|
|
32934
33224
|
]).default(15).describe("Opcional. Duraci\xF3n objetivo del video: 15s (single_call, default) o 30s (Multiescena, aproximadamente 30s y facturado por escena)."),
|
|
32935
|
-
pauseForAngleReview:
|
|
32936
|
-
aiDecidesAll:
|
|
33225
|
+
pauseForAngleReview: z137.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."),
|
|
33226
|
+
aiDecidesAll: z137.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."),
|
|
32937
33227
|
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.'),
|
|
32938
33228
|
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.'),
|
|
32939
33229
|
videoQuality: VideoQualityEnum.default("economy").describe('Opcional. Calidad: "economy" = Econ\xF3mico, "medium" = Medio o "pro" = Pro. El proveedor se elige internamente. Default: economy.'),
|
|
32940
33230
|
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).'),
|
|
32941
|
-
videoAngleCount:
|
|
33231
|
+
videoAngleCount: z137.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."),
|
|
32942
33232
|
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.'),
|
|
32943
|
-
outputs:
|
|
33233
|
+
outputs: z137.array(z137.enum([
|
|
32944
33234
|
"landing",
|
|
32945
33235
|
"video",
|
|
32946
33236
|
"image"
|
|
32947
33237
|
])).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}`),
|
|
32948
|
-
includeInitialLandingGif:
|
|
32949
|
-
competitorScanEnabled:
|
|
33238
|
+
includeInitialLandingGif: z137.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."),
|
|
33239
|
+
competitorScanEnabled: z137.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."),
|
|
32950
33240
|
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.'),
|
|
32951
|
-
replicateWinnerWhenFound:
|
|
32952
|
-
brandId:
|
|
32953
|
-
actorId:
|
|
33241
|
+
replicateWinnerWhenFound: z137.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."),
|
|
33242
|
+
brandId: z137.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."),
|
|
33243
|
+
actorId: z137.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."),
|
|
32954
33244
|
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.'),
|
|
32955
|
-
creativeDirection:
|
|
32956
|
-
headline:
|
|
32957
|
-
subheadline:
|
|
33245
|
+
creativeDirection: z137.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.'),
|
|
33246
|
+
headline: z137.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."),
|
|
33247
|
+
subheadline: z137.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."),
|
|
32958
33248
|
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".'),
|
|
32959
33249
|
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".')
|
|
32960
33250
|
}).strict();
|
|
32961
|
-
var CreateServiceVideoInput =
|
|
32962
|
-
landingUrl:
|
|
32963
|
-
additionalContext:
|
|
33251
|
+
var CreateServiceVideoInput = z137.object({
|
|
33252
|
+
landingUrl: z137.string().url().describe("URL p\xFAblica de la landing del servicio. Es suficiente para generar el video; los medios son opcionales en la web."),
|
|
33253
|
+
additionalContext: z137.string().max(3e3).optional().describe('Instrucci\xF3n natural prioritaria. Ej.: "adapta el ritmo de estos competidores", "enfatiza reservas de demos" o restricciones de claims.'),
|
|
32964
33254
|
language: Lang.describe("Idioma del video: es, en o pt."),
|
|
32965
|
-
voiceId:
|
|
32966
|
-
brandId:
|
|
32967
|
-
reviewMode:
|
|
33255
|
+
voiceId: z137.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.'),
|
|
33256
|
+
brandId: z137.string().uuid().optional().describe("Marca guardada del usuario. Aplica voz, mensajes, paleta y persona predeterminada."),
|
|
33257
|
+
reviewMode: z137.enum([
|
|
32968
33258
|
"automatic",
|
|
32969
33259
|
"guided"
|
|
32970
33260
|
]).default("automatic").describe("automatic = la IA elige concepto y guion sin pausas; guided = el usuario aprueba concepto y storyboard."),
|
|
32971
|
-
videoPreset:
|
|
33261
|
+
videoPreset: z137.enum([
|
|
32972
33262
|
"auto",
|
|
32973
33263
|
"ugc_dynamic",
|
|
32974
33264
|
"ugc_how_to",
|
|
@@ -32978,128 +33268,128 @@ var CreateServiceVideoInput = z136.object({
|
|
|
32978
33268
|
videoQuality: VideoQualityEnum.default("economy").describe('Opcional. Calidad: "economy" = Econ\xF3mico, "medium" = Medio o "pro" = Pro. El proveedor se elige internamente. Default: economy.'),
|
|
32979
33269
|
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.')
|
|
32980
33270
|
}).strict();
|
|
32981
|
-
var GetTaskStatusInput =
|
|
32982
|
-
taskId:
|
|
32983
|
-
waitSeconds:
|
|
33271
|
+
var GetTaskStatusInput = z137.object({
|
|
33272
|
+
taskId: z137.string().min(1),
|
|
33273
|
+
waitSeconds: z137.number().int().min(0).max(120).optional()
|
|
32984
33274
|
});
|
|
32985
|
-
var ListTasksInput =
|
|
32986
|
-
limit:
|
|
32987
|
-
offset:
|
|
32988
|
-
search:
|
|
33275
|
+
var ListTasksInput = z137.object({
|
|
33276
|
+
limit: z137.number().int().min(1).max(50).optional(),
|
|
33277
|
+
offset: z137.number().int().min(0).optional(),
|
|
33278
|
+
search: z137.string().optional()
|
|
32989
33279
|
});
|
|
32990
33280
|
var MCP_REVIEW_LIST_FIELD_MAX = IMAGE_WIZARD_LIST_FIELD_MAX;
|
|
32991
33281
|
var MCP_REVIEW_FEATURE_ITEM_MAX = IMAGE_FEATURE_ITEM_MAX;
|
|
32992
|
-
var EditTaskDraftInput =
|
|
32993
|
-
taskId:
|
|
32994
|
-
angleIndex:
|
|
32995
|
-
angle:
|
|
32996
|
-
angle:
|
|
32997
|
-
buyerPersona:
|
|
32998
|
-
actor:
|
|
32999
|
-
actorId:
|
|
33000
|
-
gender:
|
|
33282
|
+
var EditTaskDraftInput = z137.object({
|
|
33283
|
+
taskId: z137.string().min(1),
|
|
33284
|
+
angleIndex: z137.number().int().min(0),
|
|
33285
|
+
angle: z137.object({
|
|
33286
|
+
angle: z137.string().min(1).max(480),
|
|
33287
|
+
buyerPersona: z137.string().min(1).max(360),
|
|
33288
|
+
actor: z137.string().min(1).max(360).optional(),
|
|
33289
|
+
actorId: z137.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."),
|
|
33290
|
+
gender: z137.enum([
|
|
33001
33291
|
"female",
|
|
33002
33292
|
"male"
|
|
33003
33293
|
]).optional(),
|
|
33004
|
-
scriptNotes:
|
|
33294
|
+
scriptNotes: z137.string().max(2e3).optional()
|
|
33005
33295
|
})
|
|
33006
33296
|
});
|
|
33007
|
-
var PendingActionPayload =
|
|
33008
|
-
|
|
33009
|
-
kind:
|
|
33297
|
+
var PendingActionPayload = z137.discriminatedUnion("kind", [
|
|
33298
|
+
z137.object({
|
|
33299
|
+
kind: z137.literal("product_reference_set_approval"),
|
|
33010
33300
|
scope: ProductReferenceScopeEnum.optional(),
|
|
33011
|
-
scopeKey:
|
|
33012
|
-
scopeSnapshotId:
|
|
33013
|
-
expectedScopeHash:
|
|
33301
|
+
scopeKey: z137.string().min(1).max(120).optional(),
|
|
33302
|
+
scopeSnapshotId: z137.string().uuid().optional(),
|
|
33303
|
+
expectedScopeHash: z137.string().min(1).max(128).optional(),
|
|
33014
33304
|
decision: ProductReferenceResolutionDecisionEnum,
|
|
33015
33305
|
reason: ProductReferenceRejectionReasonEnum.optional(),
|
|
33016
|
-
affectedRefKeys:
|
|
33017
|
-
affectedPhotoNumbers:
|
|
33018
|
-
recoveryAction:
|
|
33306
|
+
affectedRefKeys: z137.array(z137.string().min(1).max(200)).min(1).optional(),
|
|
33307
|
+
affectedPhotoNumbers: z137.array(z137.number().int().positive()).min(1).optional(),
|
|
33308
|
+
recoveryAction: z137.enum([
|
|
33019
33309
|
"remove",
|
|
33020
33310
|
"replace",
|
|
33021
33311
|
"upload",
|
|
33022
33312
|
"return_to_selection"
|
|
33023
33313
|
]).optional()
|
|
33024
33314
|
}),
|
|
33025
|
-
|
|
33026
|
-
kind:
|
|
33027
|
-
selectedPhotoNumbers:
|
|
33315
|
+
z137.object({
|
|
33316
|
+
kind: z137.literal("product_reference_confirmation"),
|
|
33317
|
+
selectedPhotoNumbers: z137.array(z137.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."),
|
|
33028
33318
|
userChoice: ProductReferenceUserChoiceEnum.optional(),
|
|
33029
|
-
confirmedProductNames:
|
|
33319
|
+
confirmedProductNames: z137.array(z137.string().min(1).max(120)).min(2).max(8).optional(),
|
|
33030
33320
|
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."),
|
|
33031
33321
|
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."),
|
|
33032
|
-
useMultiSceneShowcase:
|
|
33322
|
+
useMultiSceneShowcase: z137.boolean().optional().describe("Compatibilidad con confirmaciones antiguas: si es true junto con userChoice=variants, conserva el showcase multiescena.")
|
|
33033
33323
|
}),
|
|
33034
|
-
|
|
33035
|
-
kind:
|
|
33324
|
+
z137.object({
|
|
33325
|
+
kind: z137.literal("angle_review_approval"),
|
|
33036
33326
|
// All edits are optional — empty action body just approves the
|
|
33037
33327
|
// backend-generated draft as-is. Field shapes mirror the angle-review
|
|
33038
33328
|
// request contracts in @packages/types/wizard.ts; the handler routes to
|
|
33039
33329
|
// the dedicated video/image/autopilot endpoint based on StatusResponse.kind.
|
|
33040
|
-
customAngle:
|
|
33330
|
+
customAngle: z137.string().min(1).max(480).optional(),
|
|
33041
33331
|
// Keep the shared MCP action within the strictest downstream contract:
|
|
33042
33332
|
// autopilot/image resume endpoints cap these fields at 1500 characters.
|
|
33043
|
-
painPoints:
|
|
33044
|
-
benefits:
|
|
33333
|
+
painPoints: z137.string().max(MCP_REVIEW_LIST_FIELD_MAX).optional(),
|
|
33334
|
+
benefits: z137.string().max(MCP_REVIEW_LIST_FIELD_MAX).optional(),
|
|
33045
33335
|
// Same policy as the two fields above: the strictest downstream contract
|
|
33046
33336
|
// wins. The image endpoint caps each feature at 240 while landing/video
|
|
33047
33337
|
// accept 300, and this single action routes to whichever the task is — so
|
|
33048
33338
|
// accepting 300 here turns a 250-character feature on an image task into
|
|
33049
33339
|
// an opaque HTTP 400 after the model already believed it had been saved.
|
|
33050
|
-
features:
|
|
33051
|
-
buyerPersona:
|
|
33340
|
+
features: z137.array(z137.string().min(1).max(MCP_REVIEW_FEATURE_ITEM_MAX)).max(10).optional(),
|
|
33341
|
+
buyerPersona: z137.string().min(1).max(360).optional(),
|
|
33052
33342
|
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.'),
|
|
33053
|
-
actor:
|
|
33054
|
-
actorId:
|
|
33055
|
-
creativeDirection:
|
|
33056
|
-
selectedAngleNumbers:
|
|
33057
|
-
angleEdits:
|
|
33058
|
-
angleNumber:
|
|
33059
|
-
narrativeSelection:
|
|
33343
|
+
actor: z137.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."),
|
|
33344
|
+
actorId: z137.string().max(120).nullable().optional(),
|
|
33345
|
+
creativeDirection: z137.string().max(2e3).optional(),
|
|
33346
|
+
selectedAngleNumbers: z137.array(z137.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."),
|
|
33347
|
+
angleEdits: z137.array(z137.object({
|
|
33348
|
+
angleNumber: z137.number().int().positive(),
|
|
33349
|
+
narrativeSelection: z137.enum([
|
|
33060
33350
|
"market",
|
|
33061
33351
|
"guided"
|
|
33062
33352
|
]).optional().describe("Elecci\xF3n expl\xEDcita: market usa el estudio disponible; guided usa la narrativa elegida para este \xE1ngulo."),
|
|
33063
|
-
angle:
|
|
33064
|
-
buyerPersona:
|
|
33353
|
+
angle: z137.string().min(1).max(480).optional(),
|
|
33354
|
+
buyerPersona: z137.string().min(1).max(360).optional(),
|
|
33065
33355
|
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.'),
|
|
33066
|
-
actor:
|
|
33067
|
-
actorId:
|
|
33068
|
-
creativeDirection:
|
|
33356
|
+
actor: z137.string().min(1).max(360).optional(),
|
|
33357
|
+
actorId: z137.string().max(120).nullable().optional(),
|
|
33358
|
+
creativeDirection: z137.string().max(2e3).optional()
|
|
33069
33359
|
}).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)."),
|
|
33070
|
-
gapDurationSeconds:
|
|
33071
|
-
|
|
33072
|
-
|
|
33360
|
+
gapDurationSeconds: z137.union([
|
|
33361
|
+
z137.literal(15),
|
|
33362
|
+
z137.literal(30)
|
|
33073
33363
|
]).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.")
|
|
33074
33364
|
}),
|
|
33075
|
-
|
|
33076
|
-
kind:
|
|
33077
|
-
angleId:
|
|
33078
|
-
angleNumber:
|
|
33079
|
-
content:
|
|
33080
|
-
creativeDirection:
|
|
33081
|
-
actor:
|
|
33365
|
+
z137.object({
|
|
33366
|
+
kind: z137.literal("service_angle_approval"),
|
|
33367
|
+
angleId: z137.string().uuid().optional(),
|
|
33368
|
+
angleNumber: z137.number().int().positive().optional(),
|
|
33369
|
+
content: z137.string().min(1).max(480).optional(),
|
|
33370
|
+
creativeDirection: z137.string().min(1).max(1500).optional(),
|
|
33371
|
+
actor: z137.string().min(1).max(360).optional()
|
|
33082
33372
|
}),
|
|
33083
|
-
|
|
33084
|
-
kind:
|
|
33085
|
-
voiceovers:
|
|
33086
|
-
visualDirections:
|
|
33087
|
-
voiceoversByScene:
|
|
33088
|
-
visualDirectionsByScene:
|
|
33373
|
+
z137.object({
|
|
33374
|
+
kind: z137.literal("service_script_approval"),
|
|
33375
|
+
voiceovers: z137.record(z137.string(), z137.string().min(1).max(600)).optional(),
|
|
33376
|
+
visualDirections: z137.record(z137.string(), z137.string().min(1).max(1200)).optional(),
|
|
33377
|
+
voiceoversByScene: z137.record(z137.string(), z137.string().min(1).max(600)).optional(),
|
|
33378
|
+
visualDirectionsByScene: z137.record(z137.string(), z137.string().min(1).max(1200)).optional()
|
|
33089
33379
|
})
|
|
33090
33380
|
]);
|
|
33091
|
-
var ResolvePendingActionInput =
|
|
33092
|
-
taskId:
|
|
33381
|
+
var ResolvePendingActionInput = z137.object({
|
|
33382
|
+
taskId: z137.string().min(1),
|
|
33093
33383
|
action: PendingActionPayload
|
|
33094
33384
|
}).strict();
|
|
33095
33385
|
function resolveVariantConfirmVideoTier(action) {
|
|
33096
33386
|
return action.useMultiSceneShowcase && action.userChoice === "variants" ? "standard" : void 0;
|
|
33097
33387
|
}
|
|
33098
33388
|
__name(resolveVariantConfirmVideoTier, "resolveVariantConfirmVideoTier");
|
|
33099
|
-
var StatusForResolveSchema =
|
|
33389
|
+
var StatusForResolveSchema = z137.object({
|
|
33100
33390
|
// Metadata used to select the backend resume contract. Older API responses
|
|
33101
33391
|
// may omit these fields, so the resolver keeps the generic fallback.
|
|
33102
|
-
kind:
|
|
33392
|
+
kind: z137.enum([
|
|
33103
33393
|
"autopilot",
|
|
33104
33394
|
"video",
|
|
33105
33395
|
"image",
|
|
@@ -33107,18 +33397,18 @@ var StatusForResolveSchema = z136.object({
|
|
|
33107
33397
|
"mix",
|
|
33108
33398
|
"service"
|
|
33109
33399
|
]).optional(),
|
|
33110
|
-
selectedOutputs:
|
|
33400
|
+
selectedOutputs: z137.array(z137.enum([
|
|
33111
33401
|
"landing",
|
|
33112
33402
|
"video",
|
|
33113
33403
|
"image"
|
|
33114
33404
|
])).optional(),
|
|
33115
|
-
videoAngleCount:
|
|
33116
|
-
ugcFormat:
|
|
33405
|
+
videoAngleCount: z137.number().int().min(1).max(3).optional(),
|
|
33406
|
+
ugcFormat: z137.enum([
|
|
33117
33407
|
"single_8s",
|
|
33118
33408
|
"single_15s",
|
|
33119
33409
|
"double_30s"
|
|
33120
33410
|
]).optional(),
|
|
33121
|
-
videoStyle:
|
|
33411
|
+
videoStyle: z137.enum([
|
|
33122
33412
|
"ugc",
|
|
33123
33413
|
"cartoon_3d",
|
|
33124
33414
|
"product_only",
|
|
@@ -33128,13 +33418,13 @@ var StatusForResolveSchema = z136.object({
|
|
|
33128
33418
|
videoQuality: VideoQualityEnum.optional(),
|
|
33129
33419
|
videoRouteId: VideoRouteIdEnum.optional(),
|
|
33130
33420
|
videoModel: TaskVideoModelEnum.optional(),
|
|
33131
|
-
videoTier:
|
|
33421
|
+
videoTier: z137.enum([
|
|
33132
33422
|
"legacy",
|
|
33133
33423
|
"standard",
|
|
33134
33424
|
"pro",
|
|
33135
33425
|
"single_call"
|
|
33136
33426
|
]).optional(),
|
|
33137
|
-
videoResolution:
|
|
33427
|
+
videoResolution: z137.enum([
|
|
33138
33428
|
"480p",
|
|
33139
33429
|
"720p"
|
|
33140
33430
|
]).optional(),
|
|
@@ -33142,60 +33432,60 @@ var StatusForResolveSchema = z136.object({
|
|
|
33142
33432
|
// A task created before a narrative was blocked/hidden must still parse,
|
|
33143
33433
|
// or resolve_pending_action could never approve it.
|
|
33144
33434
|
narrativeStyle: NarrativeStyle.optional(),
|
|
33145
|
-
pauseForVideoScriptReview:
|
|
33146
|
-
pendingUserAction:
|
|
33147
|
-
kind:
|
|
33148
|
-
candidateGrouping:
|
|
33149
|
-
selectedImageUrls:
|
|
33150
|
-
proposedProductNames:
|
|
33435
|
+
pauseForVideoScriptReview: z137.boolean().optional(),
|
|
33436
|
+
pendingUserAction: z137.object({
|
|
33437
|
+
kind: z137.string(),
|
|
33438
|
+
candidateGrouping: z137.string().optional(),
|
|
33439
|
+
selectedImageUrls: z137.array(z137.string()).optional(),
|
|
33440
|
+
proposedProductNames: z137.array(z137.string()).optional(),
|
|
33151
33441
|
// Adaptive reference questions are server-owned. Echo only the evidence
|
|
33152
33442
|
// identity and the choices shown by the current snapshot; never accept a
|
|
33153
33443
|
// client-supplied reference set as authorization.
|
|
33154
|
-
options:
|
|
33155
|
-
mode:
|
|
33444
|
+
options: z137.array(ProductReferenceUserChoiceEnum).max(4).optional(),
|
|
33445
|
+
mode: z137.enum([
|
|
33156
33446
|
"question",
|
|
33157
33447
|
"visual_recovery"
|
|
33158
33448
|
]).optional(),
|
|
33159
33449
|
/** Hand-mirrored from packages/types result.ts — see CLAUDE.md § MCP Server Sync. */
|
|
33160
|
-
canKeepSelection:
|
|
33161
|
-
decisionKey:
|
|
33162
|
-
questionOrdinal:
|
|
33163
|
-
questionCeiling:
|
|
33164
|
-
|
|
33165
|
-
|
|
33450
|
+
canKeepSelection: z137.boolean().optional(),
|
|
33451
|
+
decisionKey: z137.string().optional(),
|
|
33452
|
+
questionOrdinal: z137.number().int().min(1).max(5).optional(),
|
|
33453
|
+
questionCeiling: z137.union([
|
|
33454
|
+
z137.literal(3),
|
|
33455
|
+
z137.literal(5)
|
|
33166
33456
|
]).optional(),
|
|
33167
|
-
referenceEvidenceVersion:
|
|
33168
|
-
expectedEvidenceHash:
|
|
33169
|
-
question:
|
|
33170
|
-
prompt:
|
|
33171
|
-
helpText:
|
|
33172
|
-
options:
|
|
33457
|
+
referenceEvidenceVersion: z137.number().int().positive().optional(),
|
|
33458
|
+
expectedEvidenceHash: z137.string().optional(),
|
|
33459
|
+
question: z137.object({
|
|
33460
|
+
prompt: z137.string().min(1).max(240),
|
|
33461
|
+
helpText: z137.string().min(1).max(300).optional(),
|
|
33462
|
+
options: z137.array(z137.object({
|
|
33173
33463
|
value: ProductReferenceUserChoiceEnum,
|
|
33174
|
-
label:
|
|
33175
|
-
description:
|
|
33464
|
+
label: z137.string().min(1).max(120),
|
|
33465
|
+
description: z137.string().min(1).max(240).optional()
|
|
33176
33466
|
})).min(2).max(4)
|
|
33177
33467
|
}).optional(),
|
|
33178
33468
|
scope: ProductReferenceScopeEnum.optional(),
|
|
33179
|
-
scopeKey:
|
|
33180
|
-
scopeSnapshotId:
|
|
33181
|
-
scopeHash:
|
|
33182
|
-
baseReferenceSetHash:
|
|
33183
|
-
phase:
|
|
33469
|
+
scopeKey: z137.string().optional(),
|
|
33470
|
+
scopeSnapshotId: z137.string().uuid().optional(),
|
|
33471
|
+
scopeHash: z137.string().optional(),
|
|
33472
|
+
baseReferenceSetHash: z137.string().optional(),
|
|
33473
|
+
phase: z137.enum([
|
|
33184
33474
|
"preparation",
|
|
33185
33475
|
"final"
|
|
33186
33476
|
]).optional(),
|
|
33187
|
-
selectedRefKeys:
|
|
33188
|
-
allowedDecisions:
|
|
33189
|
-
previews:
|
|
33190
|
-
refKey:
|
|
33477
|
+
selectedRefKeys: z137.array(z137.string().min(1).max(200)).optional(),
|
|
33478
|
+
allowedDecisions: z137.array(ProductReferenceResolutionDecisionEnum).optional(),
|
|
33479
|
+
previews: z137.array(z137.object({
|
|
33480
|
+
refKey: z137.string().min(1).max(200)
|
|
33191
33481
|
}).passthrough()).optional(),
|
|
33192
|
-
customAngleDraft:
|
|
33193
|
-
painPointsDraft:
|
|
33194
|
-
benefitsDraft:
|
|
33195
|
-
featuresDraft:
|
|
33196
|
-
angleOptions:
|
|
33197
|
-
index:
|
|
33198
|
-
angle:
|
|
33482
|
+
customAngleDraft: z137.string().optional(),
|
|
33483
|
+
painPointsDraft: z137.string().optional(),
|
|
33484
|
+
benefitsDraft: z137.string().optional(),
|
|
33485
|
+
featuresDraft: z137.array(z137.string()).optional(),
|
|
33486
|
+
angleOptions: z137.array(z137.object({
|
|
33487
|
+
index: z137.number().int().nonnegative(),
|
|
33488
|
+
angle: z137.string().min(1)
|
|
33199
33489
|
}).passthrough()).min(1).max(5).optional(),
|
|
33200
33490
|
// Recommendation consumed only when the caller explicitly sends
|
|
33201
33491
|
// gapDurationSeconds: 30 in the angle approval action.
|
|
@@ -33205,17 +33495,17 @@ var StatusForResolveSchema = z136.object({
|
|
|
33205
33495
|
// keys. `actorId` (the saved custom actor the picker stored) was silently
|
|
33206
33496
|
// dropped here, so the approval handler below could not echo it back on the
|
|
33207
33497
|
// PATCH — the selection looked accepted and reached no render.
|
|
33208
|
-
angles:
|
|
33209
|
-
id:
|
|
33210
|
-
content:
|
|
33211
|
-
creativeDirection:
|
|
33212
|
-
actor:
|
|
33213
|
-
actorId:
|
|
33498
|
+
angles: z137.array(z137.object({
|
|
33499
|
+
id: z137.string().uuid(),
|
|
33500
|
+
content: z137.string(),
|
|
33501
|
+
creativeDirection: z137.string(),
|
|
33502
|
+
actor: z137.string(),
|
|
33503
|
+
actorId: z137.string().nullable().optional()
|
|
33214
33504
|
}).passthrough()).optional(),
|
|
33215
|
-
panels:
|
|
33216
|
-
beatId:
|
|
33217
|
-
voiceover:
|
|
33218
|
-
visualDirection:
|
|
33505
|
+
panels: z137.array(z137.object({
|
|
33506
|
+
beatId: z137.string(),
|
|
33507
|
+
voiceover: z137.string(),
|
|
33508
|
+
visualDirection: z137.string()
|
|
33219
33509
|
}).passthrough()).optional()
|
|
33220
33510
|
}).optional()
|
|
33221
33511
|
}).passthrough();
|
|
@@ -33568,8 +33858,8 @@ ANTES de invocar, resume outputs, cantidad de videos y datos comerciales, explic
|
|
|
33568
33858
|
server.registerTool("verify_landing_commerce", {
|
|
33569
33859
|
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.",
|
|
33570
33860
|
inputSchema: {
|
|
33571
|
-
taskId:
|
|
33572
|
-
editId:
|
|
33861
|
+
taskId: z137.string().uuid(),
|
|
33862
|
+
editId: z137.string().uuid()
|
|
33573
33863
|
},
|
|
33574
33864
|
annotations: OVERWRITES_EXTERNAL
|
|
33575
33865
|
}, async (input) => {
|
|
@@ -33840,16 +34130,16 @@ ANTES de invocar, resume outputs, cantidad de videos y datos comerciales, explic
|
|
|
33840
34130
|
if (input.search) qs.set("search", input.search);
|
|
33841
34131
|
const path3 = `/v1/tasks${qs.toString() ? "?" + qs.toString() : ""}`;
|
|
33842
34132
|
const r = await api.getJson(path3);
|
|
33843
|
-
const page =
|
|
33844
|
-
items:
|
|
34133
|
+
const page = z137.object({
|
|
34134
|
+
items: z137.array(z137.unknown())
|
|
33845
34135
|
}).passthrough().safeParse(r);
|
|
33846
34136
|
if (!page.success) return asText(r);
|
|
33847
34137
|
return asText({
|
|
33848
34138
|
...page.data,
|
|
33849
34139
|
items: page.data.items.map((item) => {
|
|
33850
|
-
const servicePage =
|
|
33851
|
-
taskId:
|
|
33852
|
-
resourceType:
|
|
34140
|
+
const servicePage = z137.object({
|
|
34141
|
+
taskId: z137.string().uuid(),
|
|
34142
|
+
resourceType: z137.literal("service_page")
|
|
33853
34143
|
}).passthrough().safeParse(item);
|
|
33854
34144
|
if (!servicePage.success) return item;
|
|
33855
34145
|
return {
|
|
@@ -34339,28 +34629,28 @@ ANTES de invocar, resume outputs, cantidad de videos y datos comerciales, explic
|
|
|
34339
34629
|
__name(registerTaskTools, "registerTaskTools");
|
|
34340
34630
|
|
|
34341
34631
|
// src/tools/brands.ts
|
|
34342
|
-
import { z as
|
|
34632
|
+
import { z as z138 } from "zod";
|
|
34343
34633
|
var guidance2 = getProductMakerCapabilityModelGuidance("create_brand");
|
|
34344
34634
|
var BRAND_START_REPLAY_WINDOW_MS = 15 * 60 * 1e3;
|
|
34345
34635
|
var BRAND_NOTES_MAX_CHARS = 2e4;
|
|
34346
34636
|
var BRAND_AMBIGUOUS_REQUEST_RETENTION_MS = 24 * 60 * 60 * 1e3;
|
|
34347
34637
|
var BRAND_CREATION_REQUEST_LIMIT = 500;
|
|
34348
34638
|
var brandCreationRequests = /* @__PURE__ */ new Map();
|
|
34349
|
-
var CreateBrandInput =
|
|
34350
|
-
action:
|
|
34639
|
+
var CreateBrandInput = z138.object({
|
|
34640
|
+
action: z138.enum([
|
|
34351
34641
|
"start",
|
|
34352
34642
|
"status",
|
|
34353
34643
|
"list"
|
|
34354
34644
|
]).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."),
|
|
34355
|
-
sourceUrl:
|
|
34356
|
-
businessNotes:
|
|
34357
|
-
language:
|
|
34645
|
+
sourceUrl: z138.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."),
|
|
34646
|
+
businessNotes: z138.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."),
|
|
34647
|
+
language: z138.enum([
|
|
34358
34648
|
"es",
|
|
34359
34649
|
"en",
|
|
34360
34650
|
"pt"
|
|
34361
34651
|
]).optional().describe("Idioma de la conversaci\xF3n. Solo se usa si la p\xE1gina del negocio no deja claro su idioma."),
|
|
34362
|
-
confirmed:
|
|
34363
|
-
brandId:
|
|
34652
|
+
confirmed: z138.boolean().optional().describe("Solo true despu\xE9s de mostrar el precio y que el usuario acepte. Sin confirmed, start solo cotiza y no cobra."),
|
|
34653
|
+
brandId: z138.string().uuid().optional().describe("ID de la marca. Obligatorio para status; no lo inventes.")
|
|
34364
34654
|
}).strict();
|
|
34365
34655
|
function normalizeBrandSourceUrl(raw) {
|
|
34366
34656
|
const value = raw.trim();
|
|
@@ -34644,24 +34934,24 @@ __name(registerBrandTools, "registerBrandTools");
|
|
|
34644
34934
|
|
|
34645
34935
|
// src/tools/service-landing.ts
|
|
34646
34936
|
import { createHash as createHash3, randomUUID } from "crypto";
|
|
34647
|
-
import { z as
|
|
34937
|
+
import { z as z139 } from "zod";
|
|
34648
34938
|
var guidance3 = getProductMakerCapabilityModelGuidance("create_service_landing");
|
|
34649
34939
|
var SERVICE_LANDING_KEY_NAMESPACE = "6f1d8c2a-3b4e-5f60-8a7b-9c0d1e2f3a4b";
|
|
34650
34940
|
var SERVICE_LANDING_INFO_MAX_CHARS = 2e4;
|
|
34651
|
-
var CreateServiceLandingInput =
|
|
34652
|
-
action:
|
|
34941
|
+
var CreateServiceLandingInput = z139.object({
|
|
34942
|
+
action: z139.enum([
|
|
34653
34943
|
"prepare",
|
|
34654
34944
|
"build",
|
|
34655
34945
|
"status",
|
|
34656
34946
|
"publish"
|
|
34657
34947
|
]).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."),
|
|
34658
|
-
brandId:
|
|
34659
|
-
servicePageId:
|
|
34660
|
-
businessInfo:
|
|
34661
|
-
confirmed:
|
|
34662
|
-
maxCredits:
|
|
34663
|
-
operationId:
|
|
34664
|
-
slug:
|
|
34948
|
+
brandId: z139.string().uuid().optional().describe("ID de la marca (de create_brand). Obligatorio para prepare. Nunca un taskId de producto."),
|
|
34949
|
+
servicePageId: z139.string().uuid().optional().describe("ID de la landing que devolvi\xF3 prepare. Obligatorio para build, status y publish."),
|
|
34950
|
+
businessInfo: z139.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."),
|
|
34951
|
+
confirmed: z139.boolean().optional().describe("Solo true despu\xE9s de mostrar el precio total y que el usuario acepte. Obligatorio para build."),
|
|
34952
|
+
maxCredits: z139.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."),
|
|
34953
|
+
operationId: z139.string().uuid().optional().describe("Opcional en status: la operaci\xF3n que devolvi\xF3 build."),
|
|
34954
|
+
slug: z139.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.")
|
|
34665
34955
|
}).strict();
|
|
34666
34956
|
function serviceKey(...parts) {
|
|
34667
34957
|
return deriveUuidV5(parts.join(":"), SERVICE_LANDING_KEY_NAMESPACE);
|
|
@@ -34858,7 +35148,11 @@ function registerServiceLandingTools(server, api) {
|
|
|
34858
35148
|
server.registerTool("create_service_landing", {
|
|
34859
35149
|
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.",
|
|
34860
35150
|
inputSchema: CreateServiceLandingInput.shape,
|
|
34861
|
-
|
|
35151
|
+
// action=publish exposes the page to the public internet.
|
|
35152
|
+
annotations: {
|
|
35153
|
+
...CREATES_PAID,
|
|
35154
|
+
openWorldHint: true
|
|
35155
|
+
}
|
|
34862
35156
|
}, async (rawInput) => {
|
|
34863
35157
|
try {
|
|
34864
35158
|
const input = CreateServiceLandingInput.parse(rawInput);
|
|
@@ -35036,7 +35330,7 @@ function registerServiceLandingTools(server, api) {
|
|
|
35036
35330
|
__name(registerServiceLandingTools, "registerServiceLandingTools");
|
|
35037
35331
|
|
|
35038
35332
|
// src/tools/service-images.ts
|
|
35039
|
-
import { z as
|
|
35333
|
+
import { z as z140 } from "zod";
|
|
35040
35334
|
var guidance4 = getProductMakerCapabilityModelGuidance("create_service_images");
|
|
35041
35335
|
var SERVICE_IMAGE_FORMATS = [
|
|
35042
35336
|
"4:5",
|
|
@@ -35049,23 +35343,23 @@ var LANGUAGE_TAGS = {
|
|
|
35049
35343
|
en: "en-US",
|
|
35050
35344
|
pt: "pt-BR"
|
|
35051
35345
|
};
|
|
35052
|
-
var CreateServiceImagesInput =
|
|
35053
|
-
brandId:
|
|
35054
|
-
instruction:
|
|
35055
|
-
delegateToAi:
|
|
35056
|
-
format:
|
|
35057
|
-
count:
|
|
35058
|
-
quality:
|
|
35346
|
+
var CreateServiceImagesInput = z140.object({
|
|
35347
|
+
brandId: z140.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."),
|
|
35348
|
+
instruction: z140.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."),
|
|
35349
|
+
delegateToAi: z140.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."),
|
|
35350
|
+
format: z140.enum(SERVICE_IMAGE_FORMATS).optional().describe('Formato: "4:5" (feed, por defecto), "9:16" (historias/reels) o "1:1" (cuadrado).'),
|
|
35351
|
+
count: z140.number().int().min(1).max(SERVICE_IMAGE_MAX_COUNT).optional().describe("Cu\xE1ntas im\xE1genes, de 1 a 3 (por defecto 1)."),
|
|
35352
|
+
quality: z140.enum([
|
|
35059
35353
|
"standard",
|
|
35060
35354
|
"high"
|
|
35061
35355
|
]).optional().describe('Calidad: "standard" (por defecto) o "high" (m\xE1s cara; no disponible en 4:5).'),
|
|
35062
|
-
language:
|
|
35356
|
+
language: z140.enum([
|
|
35063
35357
|
"es",
|
|
35064
35358
|
"en",
|
|
35065
35359
|
"pt"
|
|
35066
35360
|
]).optional().describe("Idioma de los textos de la imagen (por defecto es)."),
|
|
35067
|
-
adCopy:
|
|
35068
|
-
confirmed:
|
|
35361
|
+
adCopy: z140.boolean().optional().describe("Compatibilidad con clientes anteriores: el copy para publicar siempre est\xE1 incluido en cada imagen de servicios."),
|
|
35362
|
+
confirmed: z140.boolean().optional().describe("Solo true despu\xE9s de mostrar el precio y que el usuario acepte. Sin confirmed, solo cotiza y no cobra.")
|
|
35069
35363
|
}).strict();
|
|
35070
35364
|
function serviceImageModel(quality) {
|
|
35071
35365
|
return quality === "high" ? AD_CREATIVE_MODELS.PREMIUM : AD_CREATIVE_MODELS.STANDARD;
|
|
@@ -35182,7 +35476,7 @@ function registerServiceImageTools(server, api, options = {}) {
|
|
|
35182
35476
|
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."
|
|
35183
35477
|
});
|
|
35184
35478
|
} catch (error) {
|
|
35185
|
-
if (error instanceof
|
|
35479
|
+
if (error instanceof z140.ZodError) throw invalidInputFromZod(error);
|
|
35186
35480
|
throw translateError(error);
|
|
35187
35481
|
}
|
|
35188
35482
|
});
|
|
@@ -35190,7 +35484,7 @@ function registerServiceImageTools(server, api, options = {}) {
|
|
|
35190
35484
|
__name(registerServiceImageTools, "registerServiceImageTools");
|
|
35191
35485
|
|
|
35192
35486
|
// src/tools/creatives.ts
|
|
35193
|
-
import { z as
|
|
35487
|
+
import { z as z141 } from "zod";
|
|
35194
35488
|
var generateFreeVideoGuidance = getProductMakerCapabilityModelGuidance("generate_free_video");
|
|
35195
35489
|
var generateVideoCreativeGuidance = getProductMakerCapabilityModelGuidance("generate_video_creative");
|
|
35196
35490
|
var generateImageCreativesGuidance = getProductMakerCapabilityModelGuidance("generate_image_creatives");
|
|
@@ -35201,72 +35495,72 @@ var CreativeRefs = {
|
|
|
35201
35495
|
images: ImagesInput,
|
|
35202
35496
|
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.'),
|
|
35203
35497
|
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.'),
|
|
35204
|
-
price:
|
|
35205
|
-
offer:
|
|
35206
|
-
compareAtPrice:
|
|
35207
|
-
productContext:
|
|
35208
|
-
productLabel:
|
|
35209
|
-
buyerPersona:
|
|
35210
|
-
isCombo:
|
|
35211
|
-
competitorScanEnabled:
|
|
35212
|
-
brandId:
|
|
35498
|
+
price: z141.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.'),
|
|
35499
|
+
offer: z141.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.'),
|
|
35500
|
+
compareAtPrice: z141.string().min(1).max(120).optional().describe('Opcional. Precio anterior o de referencia para mostrar descuento. Ej. "$129.000" cuando price="$89.900".'),
|
|
35501
|
+
productContext: z141.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."),
|
|
35502
|
+
productLabel: z141.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.'),
|
|
35503
|
+
buyerPersona: z141.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.'),
|
|
35504
|
+
isCombo: z141.boolean().optional().describe("Opcional. Si true, el creativo presenta el producto como combo/pack. Default false."),
|
|
35505
|
+
competitorScanEnabled: z141.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."),
|
|
35506
|
+
brandId: z141.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.")
|
|
35213
35507
|
};
|
|
35214
|
-
var VideoPresenter =
|
|
35215
|
-
|
|
35216
|
-
mode:
|
|
35508
|
+
var VideoPresenter = z141.discriminatedUnion("mode", [
|
|
35509
|
+
z141.object({
|
|
35510
|
+
mode: z141.literal("none")
|
|
35217
35511
|
}).strict(),
|
|
35218
|
-
|
|
35219
|
-
mode:
|
|
35220
|
-
actorId:
|
|
35512
|
+
z141.object({
|
|
35513
|
+
mode: z141.literal("saved_actor"),
|
|
35514
|
+
actorId: z141.string().trim().min(1).max(120).describe("ID devuelto por list_actors para un actor guardado del usuario.")
|
|
35221
35515
|
}).strict(),
|
|
35222
|
-
|
|
35223
|
-
mode:
|
|
35224
|
-
description:
|
|
35516
|
+
z141.object({
|
|
35517
|
+
mode: z141.literal("described"),
|
|
35518
|
+
description: z141.string().trim().min(1).max(500).describe("Descripci\xF3n concreta de la persona que presenta el producto.")
|
|
35225
35519
|
}).strict()
|
|
35226
35520
|
]);
|
|
35227
|
-
var VideoCreativeSetup =
|
|
35228
|
-
|
|
35229
|
-
mode:
|
|
35521
|
+
var VideoCreativeSetup = z141.discriminatedUnion("mode", [
|
|
35522
|
+
z141.object({
|
|
35523
|
+
mode: z141.literal("automatic").describe('S\xF3lo cuando el usuario pidi\xF3 expl\xEDcitamente "decide todo".')
|
|
35230
35524
|
}).strict(),
|
|
35231
|
-
|
|
35232
|
-
mode:
|
|
35525
|
+
z141.object({
|
|
35526
|
+
mode: z141.literal("guided"),
|
|
35233
35527
|
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.'),
|
|
35234
|
-
buyerPersona:
|
|
35528
|
+
buyerPersona: z141.string().trim().min(1).max(360),
|
|
35235
35529
|
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.'),
|
|
35236
35530
|
presenter: VideoPresenter,
|
|
35237
|
-
creativeDirection:
|
|
35531
|
+
creativeDirection: z141.string().trim().min(1).max(500).nullable().describe("Direcci\xF3n visual elegida. null significa que el usuario deleg\xF3 expl\xEDcitamente esta decisi\xF3n.")
|
|
35238
35532
|
}).strict()
|
|
35239
35533
|
]);
|
|
35240
|
-
var VideoInput =
|
|
35534
|
+
var VideoInput = z141.object({
|
|
35241
35535
|
images: CreativeRefs.images.optional(),
|
|
35242
35536
|
winnerReference: WinnerReferenceInput.optional().describe(WINNER_IMAGE_CHOICE_GUIDANCE),
|
|
35243
|
-
title:
|
|
35244
|
-
primaryAngle:
|
|
35537
|
+
title: z141.string().min(1).describe('T\xEDtulo corto del producto. Aparece en la primera vista del video. Ej. "Crema Anti-Edad Premium".'),
|
|
35538
|
+
primaryAngle: z141.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".'),
|
|
35245
35539
|
language: CreativeRefs.language,
|
|
35246
35540
|
country: CreativeRefs.country,
|
|
35247
|
-
aspectRatio:
|
|
35541
|
+
aspectRatio: z141.enum([
|
|
35248
35542
|
"9:16",
|
|
35249
35543
|
"16:9",
|
|
35250
35544
|
"Auto"
|
|
35251
35545
|
]).describe('Relaci\xF3n de aspecto. "9:16" = vertical (TikTok/Reels \u2014 default recomendado para ads). "16:9" = horizontal (YouTube). "Auto" = decide la IA.'),
|
|
35252
35546
|
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.'),
|
|
35253
|
-
durationSeconds:
|
|
35254
|
-
videoAngleCount:
|
|
35547
|
+
durationSeconds: z141.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."),
|
|
35548
|
+
videoAngleCount: z141.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."),
|
|
35255
35549
|
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.'),
|
|
35256
35550
|
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."),
|
|
35257
35551
|
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.'),
|
|
35258
35552
|
videoQuality: VideoQualityEnum.default("economy").describe('Opcional. Calidad: "economy" = Econ\xF3mico, "medium" = Medio o "pro" = Pro. El sistema elige el proveedor internamente. Default: economy.'),
|
|
35259
35553
|
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.'),
|
|
35260
|
-
features:
|
|
35554
|
+
features: z141.array(z141.string()).optional().describe('Opcional. Lista de features/beneficios clave a mencionar. Ej. ["resistente al agua", "bater\xEDa 24h", "carga inal\xE1mbrica"].'),
|
|
35261
35555
|
price: CreativeRefs.price,
|
|
35262
35556
|
offer: CreativeRefs.offer,
|
|
35263
35557
|
productContext: CreativeRefs.productContext,
|
|
35264
35558
|
productLabel: CreativeRefs.productLabel,
|
|
35265
|
-
cta:
|
|
35266
|
-
scriptNotes:
|
|
35559
|
+
cta: z141.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.'),
|
|
35560
|
+
scriptNotes: z141.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.'),
|
|
35267
35561
|
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.'),
|
|
35268
35562
|
competitorScanEnabled: CreativeRefs.competitorScanEnabled,
|
|
35269
|
-
replicateWinnerWhenFound:
|
|
35563
|
+
replicateWinnerWhenFound: z141.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."),
|
|
35270
35564
|
brandId: CreativeRefs.brandId
|
|
35271
35565
|
}).strict();
|
|
35272
35566
|
var FreeVideoQualityEnum = VideoQualityEnum;
|
|
@@ -35284,7 +35578,7 @@ var FREE_VIDEO_DURATIONS_BY_QUALITY = {
|
|
|
35284
35578
|
length: 29
|
|
35285
35579
|
}, (_, index) => index + 2)
|
|
35286
35580
|
};
|
|
35287
|
-
var FreeVideoModelIdEnum =
|
|
35581
|
+
var FreeVideoModelIdEnum = z141.enum([
|
|
35288
35582
|
"grok-imagine-video-1-5-preview",
|
|
35289
35583
|
"minimax-h3/reference-to-video",
|
|
35290
35584
|
"wan/3-0-video",
|
|
@@ -35295,66 +35589,66 @@ var FREE_SELECTABLE_MODEL_IDS = [
|
|
|
35295
35589
|
"minimax-h3/reference-to-video",
|
|
35296
35590
|
"wan/3-0-video"
|
|
35297
35591
|
];
|
|
35298
|
-
var FreeVideoInput =
|
|
35592
|
+
var FreeVideoInput = z141.object({
|
|
35299
35593
|
images: ImagesInput.optional().describe("Opcional. 1-5 fotos del producto. Om\xEDtelo para generar desde texto puro."),
|
|
35300
|
-
rawPrompt:
|
|
35594
|
+
rawPrompt: z141.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."),
|
|
35301
35595
|
language: CreativeRefs.language,
|
|
35302
35596
|
quality: FreeVideoQualityEnum.describe('Calidad del video: "economy" = Econ\xF3mico, "medium" = Medio o "pro" = Pro (Wan).'),
|
|
35303
35597
|
// Retired in MCP 7.0.0 (Task 11): kept in the schema, accepted, and
|
|
35304
35598
|
// parsed ONLY so the handler can reject it with a message naming its
|
|
35305
35599
|
// replacement — dropping it silently would turn a caller's explicit
|
|
35306
35600
|
// Pro++ pick into a quiet fallback to Wan.
|
|
35307
|
-
freeProModel:
|
|
35601
|
+
freeProModel: z141.enum([
|
|
35308
35602
|
"wan",
|
|
35309
35603
|
"seedance"
|
|
35310
35604
|
]).optional().describe("ELIMINADO en MCP 7.0.0 \u2014 usa `modelId`. Enviar este campo produce un error."),
|
|
35311
35605
|
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.'),
|
|
35312
|
-
durationSeconds:
|
|
35606
|
+
durationSeconds: z141.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."),
|
|
35313
35607
|
videoResolution: VideoResolutionEnum.describe("Resoluci\xF3n del video: 480p o 720p."),
|
|
35314
|
-
generateAiMusic:
|
|
35608
|
+
generateAiMusic: z141.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.'),
|
|
35315
35609
|
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.'),
|
|
35316
|
-
addSubtitles:
|
|
35317
|
-
idempotencyKey:
|
|
35610
|
+
addSubtitles: z141.boolean().default(false).describe("Opcional. Quema subt\xEDtulos usando el rawPrompt exacto. Default false."),
|
|
35611
|
+
idempotencyKey: z141.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.")
|
|
35318
35612
|
}).strict();
|
|
35319
35613
|
var imageStyleList = ImageCreativeStyleEnum.options.join(" | ");
|
|
35320
|
-
var ImageCreativeSetup =
|
|
35321
|
-
|
|
35322
|
-
mode:
|
|
35614
|
+
var ImageCreativeSetup = z141.discriminatedUnion("mode", [
|
|
35615
|
+
z141.object({
|
|
35616
|
+
mode: z141.literal("automatic")
|
|
35323
35617
|
}).strict(),
|
|
35324
|
-
|
|
35325
|
-
mode:
|
|
35326
|
-
creativeArchetype:
|
|
35618
|
+
z141.object({
|
|
35619
|
+
mode: z141.literal("guided"),
|
|
35620
|
+
creativeArchetype: z141.union([
|
|
35327
35621
|
ImageCreativeArchetypeEnum,
|
|
35328
|
-
|
|
35622
|
+
z141.literal("ai_decides")
|
|
35329
35623
|
]),
|
|
35330
|
-
visualTreatment:
|
|
35624
|
+
visualTreatment: z141.union([
|
|
35331
35625
|
ImageCreativeVisualTreatmentEnum,
|
|
35332
|
-
|
|
35626
|
+
z141.literal("ai_decides")
|
|
35333
35627
|
]),
|
|
35334
35628
|
aspectRatio: ImageCreativeAspectRatioEnum,
|
|
35335
|
-
nVariants:
|
|
35336
|
-
|
|
35337
|
-
|
|
35338
|
-
|
|
35339
|
-
|
|
35340
|
-
|
|
35629
|
+
nVariants: z141.union([
|
|
35630
|
+
z141.literal(1),
|
|
35631
|
+
z141.literal(2),
|
|
35632
|
+
z141.literal(3),
|
|
35633
|
+
z141.literal(4),
|
|
35634
|
+
z141.literal(6)
|
|
35341
35635
|
]),
|
|
35342
|
-
imageModel:
|
|
35343
|
-
styleMix:
|
|
35344
|
-
generateAdCopy:
|
|
35345
|
-
competitorScanEnabled:
|
|
35346
|
-
productContext:
|
|
35347
|
-
creativeDirection:
|
|
35348
|
-
brandColors:
|
|
35349
|
-
headline:
|
|
35350
|
-
subheadline:
|
|
35636
|
+
imageModel: z141.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.'),
|
|
35637
|
+
styleMix: z141.boolean().nullable().describe("Decisi\xF3n s\xF3lo para 2\u20134 variantes. Usa null para 1 o 6, cuando la UI no hace esta pregunta."),
|
|
35638
|
+
generateAdCopy: z141.boolean(),
|
|
35639
|
+
competitorScanEnabled: z141.boolean(),
|
|
35640
|
+
productContext: z141.string().trim().min(1).max(3e3).nullable(),
|
|
35641
|
+
creativeDirection: z141.string().trim().min(1).max(500).nullable(),
|
|
35642
|
+
brandColors: z141.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.'),
|
|
35643
|
+
headline: z141.string().trim().min(1).max(120).nullable(),
|
|
35644
|
+
subheadline: z141.string().trim().min(1).max(160).nullable()
|
|
35351
35645
|
}).strict()
|
|
35352
35646
|
]).superRefine((setup, context) => {
|
|
35353
35647
|
if (setup.mode !== "guided") return;
|
|
35354
35648
|
const needsStyleMix = setup.nVariants >= 2 && setup.nVariants <= 4;
|
|
35355
35649
|
if (needsStyleMix && setup.styleMix === null) {
|
|
35356
35650
|
context.addIssue({
|
|
35357
|
-
code:
|
|
35651
|
+
code: z141.ZodIssueCode.custom,
|
|
35358
35652
|
path: [
|
|
35359
35653
|
"styleMix"
|
|
35360
35654
|
],
|
|
@@ -35363,7 +35657,7 @@ var ImageCreativeSetup = z140.discriminatedUnion("mode", [
|
|
|
35363
35657
|
}
|
|
35364
35658
|
if (!needsStyleMix && setup.styleMix !== null) {
|
|
35365
35659
|
context.addIssue({
|
|
35366
|
-
code:
|
|
35660
|
+
code: z141.ZodIssueCode.custom,
|
|
35367
35661
|
path: [
|
|
35368
35662
|
"styleMix"
|
|
35369
35663
|
],
|
|
@@ -35372,7 +35666,7 @@ var ImageCreativeSetup = z140.discriminatedUnion("mode", [
|
|
|
35372
35666
|
}
|
|
35373
35667
|
if (imageResolutionOf(setup.imageModel) === "2k" && setup.aspectRatio === "4:5") {
|
|
35374
35668
|
context.addIssue({
|
|
35375
|
-
code:
|
|
35669
|
+
code: z141.ZodIssueCode.custom,
|
|
35376
35670
|
path: [
|
|
35377
35671
|
"aspectRatio"
|
|
35378
35672
|
],
|
|
@@ -35380,55 +35674,55 @@ var ImageCreativeSetup = z140.discriminatedUnion("mode", [
|
|
|
35380
35674
|
});
|
|
35381
35675
|
}
|
|
35382
35676
|
});
|
|
35383
|
-
var LandingCreativeSetup =
|
|
35384
|
-
|
|
35385
|
-
mode:
|
|
35677
|
+
var LandingCreativeSetup = z141.discriminatedUnion("mode", [
|
|
35678
|
+
z141.object({
|
|
35679
|
+
mode: z141.literal("automatic")
|
|
35386
35680
|
}).strict(),
|
|
35387
|
-
|
|
35388
|
-
mode:
|
|
35681
|
+
z141.object({
|
|
35682
|
+
mode: z141.literal("guided"),
|
|
35389
35683
|
landingStrategy: LandingStrategyEnum,
|
|
35390
35684
|
landingImageFormat: LandingImageFormatEnum,
|
|
35391
|
-
competitorScanEnabled:
|
|
35392
|
-
paymentMethods:
|
|
35393
|
-
productContext:
|
|
35394
|
-
creativeDirection:
|
|
35395
|
-
headline:
|
|
35396
|
-
subheadline:
|
|
35685
|
+
competitorScanEnabled: z141.boolean(),
|
|
35686
|
+
paymentMethods: z141.string().trim().min(1).max(240).nullable(),
|
|
35687
|
+
productContext: z141.string().trim().min(1).max(3e3).nullable(),
|
|
35688
|
+
creativeDirection: z141.string().trim().min(1).max(500).nullable(),
|
|
35689
|
+
headline: z141.string().trim().min(1).max(120).nullable(),
|
|
35690
|
+
subheadline: z141.string().trim().min(1).max(160).nullable()
|
|
35397
35691
|
}).strict()
|
|
35398
35692
|
]);
|
|
35399
|
-
var ImageInput =
|
|
35693
|
+
var ImageInput = z141.object({
|
|
35400
35694
|
images: CreativeRefs.images.optional(),
|
|
35401
35695
|
winnerReference: WinnerReferenceInput.optional().describe(WINNER_IMAGE_CHOICE_GUIDANCE),
|
|
35402
|
-
productTitle:
|
|
35403
|
-
primaryAngle:
|
|
35696
|
+
productTitle: z141.string().min(1).describe('Nombre del producto. Ej. "Crema Anti-Edad Premium".'),
|
|
35697
|
+
primaryAngle: z141.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.'),
|
|
35404
35698
|
buyerPersona: CreativeRefs.buyerPersona,
|
|
35405
35699
|
language: CreativeRefs.language,
|
|
35406
35700
|
country: CreativeRefs.country,
|
|
35407
35701
|
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."),
|
|
35408
35702
|
imageStyle: ImageCreativeStyleEnum.optional().describe(`Opcional. Estilo del shot del producto. Valores: ${imageStyleList}. Si se omite, la IA escoge seg\xFAn el archetype.`),
|
|
35409
|
-
features:
|
|
35703
|
+
features: z141.array(z141.string()).optional().describe('Opcional. Features/beneficios a destacar visualmente. Ej. ["resistente al agua", "ultra liviano"].'),
|
|
35410
35704
|
price: CreativeRefs.price,
|
|
35411
|
-
omitPrice:
|
|
35705
|
+
omitPrice: z141.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."),
|
|
35412
35706
|
compareAtPrice: CreativeRefs.compareAtPrice,
|
|
35413
35707
|
offer: CreativeRefs.offer,
|
|
35414
35708
|
productLabel: CreativeRefs.productLabel,
|
|
35415
35709
|
isCombo: CreativeRefs.isCombo,
|
|
35416
35710
|
brandId: CreativeRefs.brandId
|
|
35417
35711
|
});
|
|
35418
|
-
var EditImageCreativeInput =
|
|
35419
|
-
taskId:
|
|
35420
|
-
variantIndex:
|
|
35421
|
-
clientRequestId:
|
|
35422
|
-
instruction:
|
|
35712
|
+
var EditImageCreativeInput = z141.object({
|
|
35713
|
+
taskId: z141.string().uuid().describe("ID de la tarea de imagen ya finalizada."),
|
|
35714
|
+
variantIndex: z141.number().int().min(0).max(10).describe("\xCDndice de la variante a corregir (0 = primera imagen)."),
|
|
35715
|
+
clientRequestId: z141.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."),
|
|
35716
|
+
instruction: z141.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".')
|
|
35423
35717
|
});
|
|
35424
|
-
var LandingInput =
|
|
35425
|
-
includeInitialLandingGif:
|
|
35718
|
+
var LandingInput = z141.object({
|
|
35719
|
+
includeInitialLandingGif: z141.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."),
|
|
35426
35720
|
images: CreativeRefs.images.optional(),
|
|
35427
35721
|
winnerReference: WinnerReferenceInput.optional().describe(WINNER_IMAGE_CHOICE_GUIDANCE),
|
|
35428
35722
|
language: CreativeRefs.language,
|
|
35429
35723
|
country: CreativeRefs.country,
|
|
35430
35724
|
creativeSetup: LandingCreativeSetup.describe("Configuraci\xF3n obligatoria de landing cl\xE1sica equivalente al wizard web. No expone landing editable/code mientras siga siendo admin-only."),
|
|
35431
|
-
salesAngle:
|
|
35725
|
+
salesAngle: z141.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.'),
|
|
35432
35726
|
buyerPersona: CreativeRefs.buyerPersona,
|
|
35433
35727
|
price: CreativeRefs.price,
|
|
35434
35728
|
compareAtPrice: CreativeRefs.compareAtPrice,
|
|
@@ -35496,8 +35790,9 @@ function resolveLandingSetup(setup) {
|
|
|
35496
35790
|
};
|
|
35497
35791
|
}
|
|
35498
35792
|
__name(resolveLandingSetup, "resolveLandingSetup");
|
|
35499
|
-
function registerCreativeTools(server, api, { transport = "http" } = {}) {
|
|
35793
|
+
function registerCreativeTools(server, api, { transport = "http", proxyBaseUrl = null } = {}) {
|
|
35500
35794
|
server.registerTool("generate_free_video", {
|
|
35795
|
+
title: "Custom-script video",
|
|
35501
35796
|
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.',
|
|
35502
35797
|
inputSchema: FreeVideoInput.shape,
|
|
35503
35798
|
annotations: CREATES_PAID_OPEN_WORLD
|
|
@@ -35710,13 +36005,46 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
|
|
|
35710
36005
|
server.registerTool("get_image_edit_status", {
|
|
35711
36006
|
description: imageEditStatusGuidance.summary,
|
|
35712
36007
|
inputSchema: {
|
|
35713
|
-
taskId:
|
|
35714
|
-
operationId:
|
|
36008
|
+
taskId: z141.string().uuid(),
|
|
36009
|
+
operationId: z141.string().uuid()
|
|
35715
36010
|
},
|
|
35716
|
-
annotations: READS_INTERNAL
|
|
36011
|
+
annotations: READS_INTERNAL,
|
|
36012
|
+
_meta: taskResultsToolMeta()
|
|
35717
36013
|
}, async (input) => {
|
|
35718
36014
|
try {
|
|
35719
|
-
|
|
36015
|
+
const { assetUrl, assetKey: _assetKey, ...operation } = ImageCreativeEditStatusResponse.parse(await api.getJson(`/v1/creatives/images/${encodeURIComponent(input.taskId)}/operations/${encodeURIComponent(input.operationId)}`));
|
|
36016
|
+
const imageExtension = assetUrl ? new URL(assetUrl).pathname.match(/\.(png|jpe?g|webp)$/i)?.[0]?.toLowerCase() : void 0;
|
|
36017
|
+
const proxyUrl = operation.status === "published" && assetUrl ? buildProxyUrl({
|
|
36018
|
+
r2Url: assetUrl,
|
|
36019
|
+
filename: `image-edit-${operation.variantIndex + 1}${imageExtension ?? ".bin"}`,
|
|
36020
|
+
proxyBaseUrl
|
|
36021
|
+
}) : null;
|
|
36022
|
+
const view = buildTaskResultsView({
|
|
36023
|
+
images: proxyUrl ? [
|
|
36024
|
+
{
|
|
36025
|
+
url: proxyUrl,
|
|
36026
|
+
alt: `Corrected image ${operation.variantIndex + 1}`
|
|
36027
|
+
}
|
|
36028
|
+
] : [],
|
|
36029
|
+
videos: [],
|
|
36030
|
+
links: []
|
|
36031
|
+
}, proxyBaseUrl);
|
|
36032
|
+
if (operation.status === "failed") view.state = "error";
|
|
36033
|
+
else if (operation.status !== "published") view.state = "pending";
|
|
36034
|
+
const deliveredUrl = view.images[0]?.url;
|
|
36035
|
+
const response = {
|
|
36036
|
+
...operation,
|
|
36037
|
+
...deliveredUrl ? {
|
|
36038
|
+
assetUrl: deliveredUrl
|
|
36039
|
+
} : {}
|
|
36040
|
+
};
|
|
36041
|
+
return {
|
|
36042
|
+
...asText(response),
|
|
36043
|
+
structuredContent: response,
|
|
36044
|
+
_meta: {
|
|
36045
|
+
taskResults: view
|
|
36046
|
+
}
|
|
36047
|
+
};
|
|
35720
36048
|
} catch (error) {
|
|
35721
36049
|
throw translateError(error);
|
|
35722
36050
|
}
|
|
@@ -35768,10 +36096,10 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
|
|
|
35768
36096
|
__name(registerCreativeTools, "registerCreativeTools");
|
|
35769
36097
|
|
|
35770
36098
|
// src/tools/creative-variants.ts
|
|
35771
|
-
import { z as
|
|
36099
|
+
import { z as z142 } from "zod";
|
|
35772
36100
|
var estimateCreativeVariantGuidance = getProductMakerCapabilityModelGuidance("estimate_creative_variant");
|
|
35773
36101
|
var createCreativeVariantGuidance = getProductMakerCapabilityModelGuidance("create_creative_variant");
|
|
35774
|
-
var LandingSectionKeyEnum =
|
|
36102
|
+
var LandingSectionKeyEnum = z142.enum([
|
|
35775
36103
|
"problem_hook",
|
|
35776
36104
|
"problem_cost",
|
|
35777
36105
|
"solution_mechanism",
|
|
@@ -35811,24 +36139,24 @@ var LandingSectionKeyEnum = z141.enum([
|
|
|
35811
36139
|
"lifestyle_fit",
|
|
35812
36140
|
"join_lifestyle"
|
|
35813
36141
|
]);
|
|
35814
|
-
var CreativeVariantKindEnum =
|
|
36142
|
+
var CreativeVariantKindEnum = z142.enum([
|
|
35815
36143
|
"video",
|
|
35816
36144
|
"image",
|
|
35817
36145
|
"landing"
|
|
35818
36146
|
]);
|
|
35819
|
-
var EstimateCreativeVariantInputBase =
|
|
35820
|
-
sourceTaskId:
|
|
36147
|
+
var EstimateCreativeVariantInputBase = z142.object({
|
|
36148
|
+
sourceTaskId: z142.string().uuid().describe("ID de la tarea original completada."),
|
|
35821
36149
|
kind: CreativeVariantKindEnum,
|
|
35822
36150
|
videoPreset: TaskCreativeVariantVideoPresetEnum.optional().describe("Opcional para video. Solo cambia el preset p\xFAblico; el servidor conserva el modelo/tier del task origen."),
|
|
35823
36151
|
videoResolution: VideoResolutionEnum.optional().describe("Opcional para video. Resoluci\xF3n del nuevo render."),
|
|
35824
|
-
nVariants:
|
|
36152
|
+
nVariants: z142.number().int().min(1).max(3).optional().describe("Opcional para imagen. N\xFAmero de im\xE1genes adicionales (1-3)."),
|
|
35825
36153
|
sectionKey: LandingSectionKeyEnum.optional().describe("Opcional para landing. Si ya conoces la secci\xF3n, evita cotizar la clasificaci\xF3n con IA."),
|
|
35826
|
-
instructions:
|
|
36154
|
+
instructions: z142.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.")
|
|
35827
36155
|
}).strict();
|
|
35828
36156
|
var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine((input, ctx) => {
|
|
35829
36157
|
if (input.kind === "video" && input.nVariants !== void 0) {
|
|
35830
36158
|
ctx.addIssue({
|
|
35831
|
-
code:
|
|
36159
|
+
code: z142.ZodIssueCode.custom,
|
|
35832
36160
|
path: [
|
|
35833
36161
|
"nVariants"
|
|
35834
36162
|
],
|
|
@@ -35838,7 +36166,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
35838
36166
|
if (input.kind !== "video") {
|
|
35839
36167
|
if (input.videoPreset !== void 0) {
|
|
35840
36168
|
ctx.addIssue({
|
|
35841
|
-
code:
|
|
36169
|
+
code: z142.ZodIssueCode.custom,
|
|
35842
36170
|
path: [
|
|
35843
36171
|
"videoPreset"
|
|
35844
36172
|
],
|
|
@@ -35847,7 +36175,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
35847
36175
|
}
|
|
35848
36176
|
if (input.videoResolution !== void 0) {
|
|
35849
36177
|
ctx.addIssue({
|
|
35850
|
-
code:
|
|
36178
|
+
code: z142.ZodIssueCode.custom,
|
|
35851
36179
|
path: [
|
|
35852
36180
|
"videoResolution"
|
|
35853
36181
|
],
|
|
@@ -35857,7 +36185,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
35857
36185
|
}
|
|
35858
36186
|
if (input.kind !== "image" && input.nVariants !== void 0) {
|
|
35859
36187
|
ctx.addIssue({
|
|
35860
|
-
code:
|
|
36188
|
+
code: z142.ZodIssueCode.custom,
|
|
35861
36189
|
path: [
|
|
35862
36190
|
"nVariants"
|
|
35863
36191
|
],
|
|
@@ -35867,7 +36195,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
35867
36195
|
if (input.kind !== "landing") {
|
|
35868
36196
|
if (input.sectionKey !== void 0) {
|
|
35869
36197
|
ctx.addIssue({
|
|
35870
|
-
code:
|
|
36198
|
+
code: z142.ZodIssueCode.custom,
|
|
35871
36199
|
path: [
|
|
35872
36200
|
"sectionKey"
|
|
35873
36201
|
],
|
|
@@ -35876,7 +36204,7 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
35876
36204
|
}
|
|
35877
36205
|
if (input.instructions !== void 0) {
|
|
35878
36206
|
ctx.addIssue({
|
|
35879
|
-
code:
|
|
36207
|
+
code: z142.ZodIssueCode.custom,
|
|
35880
36208
|
path: [
|
|
35881
36209
|
"instructions"
|
|
35882
36210
|
],
|
|
@@ -35885,31 +36213,31 @@ var EstimateCreativeVariantInput = EstimateCreativeVariantInputBase.superRefine(
|
|
|
35885
36213
|
}
|
|
35886
36214
|
}
|
|
35887
36215
|
});
|
|
35888
|
-
var CreateCreativeVariantInputBase =
|
|
35889
|
-
sourceTaskId:
|
|
36216
|
+
var CreateCreativeVariantInputBase = z142.object({
|
|
36217
|
+
sourceTaskId: z142.string().uuid().describe("ID de la tarea original completada."),
|
|
35890
36218
|
kind: CreativeVariantKindEnum,
|
|
35891
|
-
confirm:
|
|
35892
|
-
clientRequestId:
|
|
35893
|
-
angle:
|
|
35894
|
-
buyerPersona:
|
|
35895
|
-
scriptNotes:
|
|
35896
|
-
creativeDirection:
|
|
36219
|
+
confirm: z142.literal(true).describe("Confirmaci\xF3n expl\xEDcita del usuario despu\xE9s de mostrarle el estimado de cr\xE9ditos."),
|
|
36220
|
+
clientRequestId: z142.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."),
|
|
36221
|
+
angle: z142.string().trim().min(1).max(500).optional().describe("\xC1ngulo nuevo para video o imagen."),
|
|
36222
|
+
buyerPersona: z142.string().trim().min(1).max(360).optional().describe("Buyer persona coherente con el \xE1ngulo."),
|
|
36223
|
+
scriptNotes: z142.string().trim().min(1).max(2e3).optional().describe("Direcci\xF3n breve del guion para una variante de video."),
|
|
36224
|
+
creativeDirection: z142.string().trim().min(1).max(2e3).optional().describe("Direcci\xF3n creativa para una variante de imagen."),
|
|
35897
36225
|
videoPreset: TaskCreativeVariantVideoPresetEnum.optional().describe("Opcional para video. Preset p\xFAblico; el servidor resuelve modelo/tier desde el task origen."),
|
|
35898
36226
|
videoResolution: VideoResolutionEnum.optional().describe("Opcional para video."),
|
|
35899
|
-
generateAiMusic:
|
|
35900
|
-
nVariants:
|
|
36227
|
+
generateAiMusic: z142.boolean().optional().describe("Opcional para video."),
|
|
36228
|
+
nVariants: z142.number().int().min(1).max(3).optional().describe("Opcional para imagen. N\xFAmero de im\xE1genes adicionales (1-3)."),
|
|
35901
36229
|
imageStyle: ImageCreativeStyleEnum.optional().describe("Opcional para imagen."),
|
|
35902
36230
|
creativeArchetype: ImageCreativeArchetypeEnum.optional().describe("Opcional para imagen."),
|
|
35903
36231
|
visualTreatment: ImageCreativeVisualTreatmentEnum.optional().describe("Opcional para imagen."),
|
|
35904
36232
|
aspectRatio: ImageCreativeAspectRatioEnum.optional().describe("Opcional para imagen."),
|
|
35905
|
-
styleMix:
|
|
36233
|
+
styleMix: z142.boolean().optional().describe("Opcional para imagen."),
|
|
35906
36234
|
sectionKey: LandingSectionKeyEnum.optional().describe("Opcional para landing. Si se omite, se clasifica con IA a partir de instructions."),
|
|
35907
|
-
instructions:
|
|
36235
|
+
instructions: z142.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.")
|
|
35908
36236
|
}).strict();
|
|
35909
36237
|
var CreateCreativeVariantInput = CreateCreativeVariantInputBase.superRefine((input, ctx) => {
|
|
35910
36238
|
if ((input.kind === "video" || input.kind === "image") && !input.angle) {
|
|
35911
36239
|
ctx.addIssue({
|
|
35912
|
-
code:
|
|
36240
|
+
code: z142.ZodIssueCode.custom,
|
|
35913
36241
|
path: [
|
|
35914
36242
|
"angle"
|
|
35915
36243
|
],
|
|
@@ -35918,7 +36246,7 @@ var CreateCreativeVariantInput = CreateCreativeVariantInputBase.superRefine((inp
|
|
|
35918
36246
|
}
|
|
35919
36247
|
if (input.kind === "landing" && !input.sectionKey && !input.instructions) {
|
|
35920
36248
|
ctx.addIssue({
|
|
35921
|
-
code:
|
|
36249
|
+
code: z142.ZodIssueCode.custom,
|
|
35922
36250
|
path: [
|
|
35923
36251
|
"instructions"
|
|
35924
36252
|
],
|
|
@@ -35964,7 +36292,7 @@ var CreateCreativeVariantInput = CreateCreativeVariantInputBase.superRefine((inp
|
|
|
35964
36292
|
for (const field of fieldsByKind[input.kind]) {
|
|
35965
36293
|
if (input[field] !== void 0) {
|
|
35966
36294
|
ctx.addIssue({
|
|
35967
|
-
code:
|
|
36295
|
+
code: z142.ZodIssueCode.custom,
|
|
35968
36296
|
path: [
|
|
35969
36297
|
field
|
|
35970
36298
|
],
|
|
@@ -36122,13 +36450,13 @@ function registerCreativeVariantTools(server, api) {
|
|
|
36122
36450
|
__name(registerCreativeVariantTools, "registerCreativeVariantTools");
|
|
36123
36451
|
|
|
36124
36452
|
// src/tools/script-review.ts
|
|
36125
|
-
import { z as
|
|
36453
|
+
import { z as z143 } from "zod";
|
|
36126
36454
|
var getVideoScriptGuidance = getProductMakerCapabilityModelGuidance("get_video_script");
|
|
36127
36455
|
var regenerateVideoScriptGuidance = getProductMakerCapabilityModelGuidance("regenerate_video_script");
|
|
36128
36456
|
var approveVideoScriptGuidance = getProductMakerCapabilityModelGuidance("approve_video_script");
|
|
36129
|
-
var GetVideoScriptInput =
|
|
36130
|
-
taskId:
|
|
36131
|
-
angleKey:
|
|
36457
|
+
var GetVideoScriptInput = z143.object({
|
|
36458
|
+
taskId: z143.string().min(1).describe("ID de la tarea (la misma que devolvi\xF3 create_product_task / generate_video_creative)."),
|
|
36459
|
+
angleKey: z143.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.')
|
|
36132
36460
|
}).strict();
|
|
36133
36461
|
var ApproveVideoScriptInput = ApproveVideoScriptCapabilityRequest;
|
|
36134
36462
|
var RegenerateVideoScriptInput = RegenerateVideoScriptCapabilityRequest.extend({
|
|
@@ -36234,7 +36562,7 @@ Consulta get_task_status con esta tarea antes de reintentar. Si la tarea termin\
|
|
|
36234
36562
|
__name(registerScriptReviewTools, "registerScriptReviewTools");
|
|
36235
36563
|
|
|
36236
36564
|
// src/tools/connections.ts
|
|
36237
|
-
import { z as
|
|
36565
|
+
import { z as z144 } from "zod";
|
|
36238
36566
|
var listShopifyShopsGuidance = getProductMakerCapabilityModelGuidance("list_shopify_shops");
|
|
36239
36567
|
var listShopifyProductsGuidance = getProductMakerCapabilityModelGuidance("list_shopify_products");
|
|
36240
36568
|
var listMetaAdAccountsGuidance = getProductMakerCapabilityModelGuidance("list_meta_ad_accounts");
|
|
@@ -36243,7 +36571,7 @@ var listTikTokAdvertisersGuidance = getProductMakerCapabilityModelGuidance("list
|
|
|
36243
36571
|
function registerConnectionTools(server, api) {
|
|
36244
36572
|
server.registerTool("list_shopify_shops", {
|
|
36245
36573
|
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.",
|
|
36246
|
-
inputSchema:
|
|
36574
|
+
inputSchema: z144.object({}).shape,
|
|
36247
36575
|
annotations: READS_INTERNAL
|
|
36248
36576
|
}, async () => {
|
|
36249
36577
|
try {
|
|
@@ -36255,10 +36583,10 @@ function registerConnectionTools(server, api) {
|
|
|
36255
36583
|
});
|
|
36256
36584
|
server.registerTool("list_shopify_products", {
|
|
36257
36585
|
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.',
|
|
36258
|
-
inputSchema:
|
|
36259
|
-
shopDomain:
|
|
36260
|
-
query:
|
|
36261
|
-
taskId:
|
|
36586
|
+
inputSchema: z144.object({
|
|
36587
|
+
shopDomain: z144.string().min(3).max(120),
|
|
36588
|
+
query: z144.string().trim().max(120).optional(),
|
|
36589
|
+
taskId: z144.string().uuid().optional()
|
|
36262
36590
|
}).shape,
|
|
36263
36591
|
annotations: READS_EXTERNAL
|
|
36264
36592
|
}, async ({ shopDomain, query, taskId }) => {
|
|
@@ -36276,7 +36604,7 @@ function registerConnectionTools(server, api) {
|
|
|
36276
36604
|
});
|
|
36277
36605
|
server.registerTool("list_meta_ad_accounts", {
|
|
36278
36606
|
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.",
|
|
36279
|
-
inputSchema:
|
|
36607
|
+
inputSchema: z144.object({}).shape,
|
|
36280
36608
|
annotations: READS_EXTERNAL
|
|
36281
36609
|
}, async () => {
|
|
36282
36610
|
try {
|
|
@@ -36288,7 +36616,7 @@ function registerConnectionTools(server, api) {
|
|
|
36288
36616
|
});
|
|
36289
36617
|
server.registerTool("list_meta_destinations", {
|
|
36290
36618
|
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.",
|
|
36291
|
-
inputSchema:
|
|
36619
|
+
inputSchema: z144.object({}).shape,
|
|
36292
36620
|
annotations: READS_EXTERNAL,
|
|
36293
36621
|
_meta: {
|
|
36294
36622
|
"openai/widgetAccessible": true
|
|
@@ -36303,7 +36631,7 @@ function registerConnectionTools(server, api) {
|
|
|
36303
36631
|
});
|
|
36304
36632
|
server.registerTool("list_tiktok_advertisers", {
|
|
36305
36633
|
description: listTikTokAdvertisersGuidance.summary + " Si est\xE1 vac\xEDa, ind\xEDcale al usuario conectar TikTok en productmaker.app/integrations.",
|
|
36306
|
-
inputSchema:
|
|
36634
|
+
inputSchema: z144.object({}).shape,
|
|
36307
36635
|
annotations: READS_EXTERNAL
|
|
36308
36636
|
}, async () => {
|
|
36309
36637
|
try {
|
|
@@ -36317,7 +36645,7 @@ function registerConnectionTools(server, api) {
|
|
|
36317
36645
|
__name(registerConnectionTools, "registerConnectionTools");
|
|
36318
36646
|
|
|
36319
36647
|
// src/tools/actors.ts
|
|
36320
|
-
import { z as
|
|
36648
|
+
import { z as z145 } from "zod";
|
|
36321
36649
|
var listActorsGuidance = getProductMakerCapabilityModelGuidance("list_actors");
|
|
36322
36650
|
var actorVoiceGuidance = {
|
|
36323
36651
|
capabilities: getProductMakerCapabilityModelGuidance("get_actor_voice_capabilities"),
|
|
@@ -36357,7 +36685,7 @@ function sanitizeVoiceResult(value) {
|
|
|
36357
36685
|
__name(sanitizeVoiceResult, "sanitizeVoiceResult");
|
|
36358
36686
|
function registerActorTools(server, api) {
|
|
36359
36687
|
const actor = {
|
|
36360
|
-
actorId:
|
|
36688
|
+
actorId: z145.string().min(1).max(64)
|
|
36361
36689
|
};
|
|
36362
36690
|
const safe = /* @__PURE__ */ __name(async (request) => {
|
|
36363
36691
|
try {
|
|
@@ -36386,8 +36714,8 @@ function registerActorTools(server, api) {
|
|
|
36386
36714
|
inputSchema: {
|
|
36387
36715
|
...actor,
|
|
36388
36716
|
draft: ActorVoiceDraftSchema,
|
|
36389
|
-
requestKey:
|
|
36390
|
-
acceptedQuoteId:
|
|
36717
|
+
requestKey: z145.string().trim().min(1).max(128),
|
|
36718
|
+
acceptedQuoteId: z145.string().trim().min(1).max(128)
|
|
36391
36719
|
},
|
|
36392
36720
|
annotations: CREATES_PAID
|
|
36393
36721
|
}, async ({ actorId, ...body }) => safe(api.postJson(`/v1/actors/${encodeURIComponent(actorId)}/voice/previews`, body)));
|
|
@@ -36395,7 +36723,7 @@ function registerActorTools(server, api) {
|
|
|
36395
36723
|
description: actorVoiceGuidance.get.summary,
|
|
36396
36724
|
inputSchema: {
|
|
36397
36725
|
...actor,
|
|
36398
|
-
candidateId:
|
|
36726
|
+
candidateId: z145.string().min(1).max(64)
|
|
36399
36727
|
},
|
|
36400
36728
|
// Polling can resume the first paid submission of an accepted quote.
|
|
36401
36729
|
// The immutable candidate/operation key keeps recovery idempotent.
|
|
@@ -36408,16 +36736,16 @@ function registerActorTools(server, api) {
|
|
|
36408
36736
|
description: actorVoiceGuidance.approve.summary,
|
|
36409
36737
|
inputSchema: {
|
|
36410
36738
|
...actor,
|
|
36411
|
-
candidateId:
|
|
36412
|
-
expectedRevision:
|
|
36413
|
-
expectedApprovedRevision:
|
|
36739
|
+
candidateId: z145.string().min(1).max(64),
|
|
36740
|
+
expectedRevision: z145.number().int().positive(),
|
|
36741
|
+
expectedApprovedRevision: z145.number().int().positive().nullable().optional()
|
|
36414
36742
|
},
|
|
36415
36743
|
// Replaces the selected voice and withdraws the old catalog grants.
|
|
36416
36744
|
annotations: OVERWRITES_INTERNAL
|
|
36417
36745
|
}, async ({ actorId, ...body }) => safe(api.postJson(`/v1/actors/${encodeURIComponent(actorId)}/voice/approve`, body)));
|
|
36418
36746
|
server.registerTool("list_actors", {
|
|
36419
36747
|
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.",
|
|
36420
|
-
inputSchema:
|
|
36748
|
+
inputSchema: z145.object({}).shape,
|
|
36421
36749
|
annotations: READS_INTERNAL
|
|
36422
36750
|
}, async () => {
|
|
36423
36751
|
try {
|
|
@@ -36450,7 +36778,7 @@ function registerActorTools(server, api) {
|
|
|
36450
36778
|
__name(registerActorTools, "registerActorTools");
|
|
36451
36779
|
|
|
36452
36780
|
// src/tools/publish.ts
|
|
36453
|
-
import { z as
|
|
36781
|
+
import { z as z146 } from "zod";
|
|
36454
36782
|
|
|
36455
36783
|
// src/tools/meta-publish-attempt-id.ts
|
|
36456
36784
|
var META_PUBLISH_NAMESPACE = "7b19c9ac-b4eb-4db1-970e-c2f24ce30b98";
|
|
@@ -36489,40 +36817,40 @@ var publishToShopifyGuidance = getProductMakerCapabilityModelGuidance("publish_t
|
|
|
36489
36817
|
var publishToMetaGuidance = getProductMakerCapabilityModelGuidance("publish_to_meta");
|
|
36490
36818
|
var publishCreativesToMetaGuidance = getProductMakerCapabilityModelGuidance("publish_creatives_to_meta");
|
|
36491
36819
|
var publishToTikTokGuidance = getProductMakerCapabilityModelGuidance("publish_to_tiktok");
|
|
36492
|
-
var ShopifyInput =
|
|
36493
|
-
taskId:
|
|
36494
|
-
shopDomain:
|
|
36820
|
+
var ShopifyInput = z146.object({
|
|
36821
|
+
taskId: z146.string().min(1),
|
|
36822
|
+
shopDomain: z146.string().min(1),
|
|
36495
36823
|
/** Override: number of landing images to include. Default = ALL images the
|
|
36496
36824
|
* task generated (the MCP fetches /v1/results first to count). Bounded
|
|
36497
36825
|
* generously here; the backend validates each index against
|
|
36498
36826
|
* landing.images.length and rejects out-of-range. */
|
|
36499
|
-
numLandingImages:
|
|
36500
|
-
variantIndex:
|
|
36827
|
+
numLandingImages: z146.number().int().min(1).max(20).optional(),
|
|
36828
|
+
variantIndex: z146.number().int().min(0).optional(),
|
|
36501
36829
|
/** ASK THE USER before setting: "¿Tu producto tiene variantes (talla, color, etc.)?".
|
|
36502
36830
|
* When true, the landing renders variant-picker pills; when false, it ships
|
|
36503
36831
|
* a single-option product page. Defaults to false. */
|
|
36504
|
-
showVariantPicker:
|
|
36832
|
+
showVariantPicker: z146.boolean().optional(),
|
|
36505
36833
|
/** Default `auto` updates the product previously published for this task and
|
|
36506
36834
|
* shop. Use `existing` with targetProductId to choose another product, or
|
|
36507
36835
|
* `new` only when the user explicitly wants a duplicate/new listing. */
|
|
36508
|
-
publicationMode:
|
|
36836
|
+
publicationMode: z146.enum([
|
|
36509
36837
|
"auto",
|
|
36510
36838
|
"existing",
|
|
36511
36839
|
"new"
|
|
36512
36840
|
]).optional(),
|
|
36513
36841
|
/** Shopify product GID, numeric id, or admin product URL. Required when
|
|
36514
36842
|
* publicationMode is `existing`. */
|
|
36515
|
-
targetProductId:
|
|
36843
|
+
targetProductId: z146.string().trim().min(1).max(300).optional(),
|
|
36516
36844
|
/** Add an announcement bar text above add-to-cart (optional). */
|
|
36517
|
-
announcementBarEnabled:
|
|
36518
|
-
announcementBarText:
|
|
36845
|
+
announcementBarEnabled: z146.boolean().optional(),
|
|
36846
|
+
announcementBarText: z146.string().trim().min(1).max(SHOPIFY_ANNOUNCEMENT_BAR_MAX_LENGTH).optional(),
|
|
36519
36847
|
commerceCorrection: PublishCommerceCorrectionSchema.optional().describe("Complete seller-confirmed commerce values, with the revision and fingerprint returned in recovery. Never invent missing prices or convert currency."),
|
|
36520
|
-
confirmed:
|
|
36848
|
+
confirmed: z146.boolean().optional().describe("Required as true only when submitting commerceCorrection, after showing its values and obtaining explicit seller confirmation.")
|
|
36521
36849
|
});
|
|
36522
36850
|
var ShopifyInputWithAnnouncementBarValidation = ShopifyInput.superRefine((value, ctx) => {
|
|
36523
36851
|
if (value.commerceCorrection && value.confirmed !== true) {
|
|
36524
36852
|
ctx.addIssue({
|
|
36525
|
-
code:
|
|
36853
|
+
code: z146.ZodIssueCode.custom,
|
|
36526
36854
|
path: [
|
|
36527
36855
|
"confirmed"
|
|
36528
36856
|
],
|
|
@@ -36531,7 +36859,7 @@ var ShopifyInputWithAnnouncementBarValidation = ShopifyInput.superRefine((value,
|
|
|
36531
36859
|
}
|
|
36532
36860
|
if (value.announcementBarEnabled && !value.announcementBarText) {
|
|
36533
36861
|
ctx.addIssue({
|
|
36534
|
-
code:
|
|
36862
|
+
code: z146.ZodIssueCode.custom,
|
|
36535
36863
|
path: [
|
|
36536
36864
|
"announcementBarText"
|
|
36537
36865
|
],
|
|
@@ -36540,7 +36868,7 @@ var ShopifyInputWithAnnouncementBarValidation = ShopifyInput.superRefine((value,
|
|
|
36540
36868
|
}
|
|
36541
36869
|
if (!value.announcementBarEnabled && value.announcementBarText) {
|
|
36542
36870
|
ctx.addIssue({
|
|
36543
|
-
code:
|
|
36871
|
+
code: z146.ZodIssueCode.custom,
|
|
36544
36872
|
path: [
|
|
36545
36873
|
"announcementBarText"
|
|
36546
36874
|
],
|
|
@@ -36554,90 +36882,90 @@ function parseShopifyInput(rawInput) {
|
|
|
36554
36882
|
return parsed.data;
|
|
36555
36883
|
}
|
|
36556
36884
|
__name(parseShopifyInput, "parseShopifyInput");
|
|
36557
|
-
var McpCampaignConfig =
|
|
36885
|
+
var McpCampaignConfig = z146.object({
|
|
36558
36886
|
/** Legacy USD amount. Meta retains its existing conversion; the TikTok
|
|
36559
36887
|
* schema below restricts this input to USD advertiser accounts. */
|
|
36560
|
-
dailyBudgetUsd:
|
|
36888
|
+
dailyBudgetUsd: z146.number().positive().optional(),
|
|
36561
36889
|
/** ISO-3166-1 alpha-2 country codes or resolvable names (CountryIso2Schema
|
|
36562
36890
|
* normalizes at validation). Meta accepts max 1 (single-country ad set).
|
|
36563
36891
|
* TikTok takes only the first entry. */
|
|
36564
|
-
targetCountries:
|
|
36565
|
-
campaignName:
|
|
36892
|
+
targetCountries: z146.array(CountryIso2Schema).max(1).optional(),
|
|
36893
|
+
campaignName: z146.string().trim().min(1).max(100).optional()
|
|
36566
36894
|
});
|
|
36567
36895
|
var McpAdAccountId = MetaAdAccountId.describe('Meta ad account id. Accepts "act_1234567890" or the bare numeric id "1234567890".');
|
|
36568
|
-
var MetaInput =
|
|
36569
|
-
taskId:
|
|
36896
|
+
var MetaInput = z146.object({
|
|
36897
|
+
taskId: z146.string().min(1),
|
|
36570
36898
|
/** Exact Meta Business portfolio selected from `list_meta_destinations`. */
|
|
36571
|
-
portfolioId:
|
|
36899
|
+
portfolioId: z146.string().trim().min(1),
|
|
36572
36900
|
/** Exact Facebook Page selected from `list_meta_destinations`. */
|
|
36573
|
-
pageId:
|
|
36901
|
+
pageId: z146.string().trim().min(1),
|
|
36574
36902
|
adAccountId: McpAdAccountId,
|
|
36575
|
-
pixelId:
|
|
36576
|
-
landingUrl:
|
|
36903
|
+
pixelId: z146.string().min(1).optional(),
|
|
36904
|
+
landingUrl: z146.string().url().optional(),
|
|
36577
36905
|
/** Select which angles to publish by 0-based index. Omit to publish all
|
|
36578
36906
|
* successful angles. Example: a task with 3 angles → `[0,2]` publishes #0 + #2. */
|
|
36579
|
-
angleIndexes:
|
|
36907
|
+
angleIndexes: z146.array(z146.number().int().nonnegative()).optional(),
|
|
36580
36908
|
campaignConfig: McpCampaignConfig.optional(),
|
|
36581
36909
|
/** Override the ad's primary text (body copy). If omitted, ProductMaker
|
|
36582
36910
|
* uses the auto-generated copy attached to the task. Capped at 500 chars
|
|
36583
36911
|
* (Meta's primary-text limit). */
|
|
36584
|
-
primaryText:
|
|
36912
|
+
primaryText: z146.string().trim().min(1).max(500).optional(),
|
|
36585
36913
|
/** Optional WhatsApp number. When set, the ad button opens a WhatsApp chat
|
|
36586
36914
|
* (wa.me) instead of a landing page. */
|
|
36587
|
-
phoneNumber:
|
|
36915
|
+
phoneNumber: z146.string().min(8).max(32).optional()
|
|
36588
36916
|
});
|
|
36589
|
-
var GroupedTaskSource =
|
|
36590
|
-
type:
|
|
36591
|
-
taskId:
|
|
36592
|
-
angleIndex:
|
|
36593
|
-
videoVariant:
|
|
36917
|
+
var GroupedTaskSource = z146.object({
|
|
36918
|
+
type: z146.literal("task"),
|
|
36919
|
+
taskId: z146.string().min(1),
|
|
36920
|
+
angleIndex: z146.number().int().min(0).optional(),
|
|
36921
|
+
videoVariant: z146.enum([
|
|
36594
36922
|
"with_subtitles",
|
|
36595
36923
|
"without_subtitles"
|
|
36596
36924
|
]).optional(),
|
|
36597
|
-
imageVariantIndex:
|
|
36598
|
-
});
|
|
36599
|
-
var GroupedMediaSource =
|
|
36600
|
-
type:
|
|
36601
|
-
url:
|
|
36602
|
-
path:
|
|
36603
|
-
thumbnailUrl:
|
|
36604
|
-
thumbnailPath:
|
|
36925
|
+
imageVariantIndex: z146.number().int().min(0).optional()
|
|
36926
|
+
});
|
|
36927
|
+
var GroupedMediaSource = z146.object({
|
|
36928
|
+
type: z146.literal("media"),
|
|
36929
|
+
url: z146.string().url().optional().describe("Public HTTPS media URL. For a file on the user laptop, use request_media_upload on the hosted MCP instead."),
|
|
36930
|
+
path: z146.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".'),
|
|
36931
|
+
thumbnailUrl: z146.string().url().optional(),
|
|
36932
|
+
thumbnailPath: z146.string().min(1).optional()
|
|
36605
36933
|
}).refine((value) => Boolean(value.url) !== Boolean(value.path), {
|
|
36606
36934
|
message: "media source needs exactly one of `url` or `path`"
|
|
36607
36935
|
}).refine((value) => !(value.thumbnailUrl && value.thumbnailPath), {
|
|
36608
36936
|
message: "media source needs at most one of `thumbnailUrl` or `thumbnailPath`"
|
|
36609
36937
|
});
|
|
36610
|
-
var GroupedUploadedAssetSource =
|
|
36611
|
-
type:
|
|
36938
|
+
var GroupedUploadedAssetSource = z146.object({
|
|
36939
|
+
type: z146.literal("asset"),
|
|
36612
36940
|
/** Returned by check_media_upload. ProductMaker resolves ownership server-side. */
|
|
36613
|
-
assetId:
|
|
36614
|
-
thumbnailAssetId:
|
|
36941
|
+
assetId: z146.string().uuid(),
|
|
36942
|
+
thumbnailAssetId: z146.string().uuid().optional()
|
|
36615
36943
|
});
|
|
36616
|
-
var GroupedCreative =
|
|
36617
|
-
type:
|
|
36944
|
+
var GroupedCreative = z146.object({
|
|
36945
|
+
type: z146.enum([
|
|
36618
36946
|
"image",
|
|
36619
36947
|
"video"
|
|
36620
36948
|
]),
|
|
36621
|
-
source:
|
|
36949
|
+
source: z146.union([
|
|
36622
36950
|
GroupedTaskSource,
|
|
36623
36951
|
GroupedMediaSource,
|
|
36624
36952
|
GroupedUploadedAssetSource
|
|
36625
36953
|
]),
|
|
36626
|
-
title:
|
|
36627
|
-
description:
|
|
36954
|
+
title: z146.string().max(120).optional(),
|
|
36955
|
+
description: z146.string().max(500).optional(),
|
|
36628
36956
|
cta: MetaCtaType.optional(),
|
|
36629
|
-
creativeId:
|
|
36957
|
+
creativeId: z146.string().min(1).optional()
|
|
36630
36958
|
});
|
|
36631
|
-
var GroupedCampaignConfigBase =
|
|
36959
|
+
var GroupedCampaignConfigBase = z146.object({
|
|
36632
36960
|
campaignType: MetaCampaignType.default("sales"),
|
|
36633
36961
|
optimizationMode: MetaOptimizationMode.default("advantage_plus"),
|
|
36634
36962
|
budgetMode: MetaBudgetMode.optional(),
|
|
36635
|
-
campaignDailyBudget:
|
|
36636
|
-
adSets:
|
|
36637
|
-
tempId:
|
|
36638
|
-
name:
|
|
36639
|
-
dailyBudget:
|
|
36640
|
-
creativeIds:
|
|
36963
|
+
campaignDailyBudget: z146.number().int().positive().optional(),
|
|
36964
|
+
adSets: z146.array(z146.object({
|
|
36965
|
+
tempId: z146.string().min(1).max(80),
|
|
36966
|
+
name: z146.string().min(1).max(100),
|
|
36967
|
+
dailyBudget: z146.number().int().positive().optional(),
|
|
36968
|
+
creativeIds: z146.array(z146.string().min(1)).min(1)
|
|
36641
36969
|
})).min(1).max(CAMPAIGN_LIMITS.MAX_AD_SETS),
|
|
36642
36970
|
/**
|
|
36643
36971
|
* Valid values are 'landing' and 'whatsapp' — there is no 'website'. This
|
|
@@ -36645,17 +36973,17 @@ var GroupedCampaignConfigBase = z145.object({
|
|
|
36645
36973
|
* opaque `invalid_literal` and made the WhatsApp destination — supported by
|
|
36646
36974
|
* the API since #849 — unreachable from this tool.
|
|
36647
36975
|
*/
|
|
36648
|
-
destination:
|
|
36649
|
-
|
|
36650
|
-
type:
|
|
36651
|
-
landingUrl:
|
|
36976
|
+
destination: z146.discriminatedUnion("type", [
|
|
36977
|
+
z146.object({
|
|
36978
|
+
type: z146.literal("landing"),
|
|
36979
|
+
landingUrl: z146.string().url()
|
|
36652
36980
|
}),
|
|
36653
|
-
|
|
36654
|
-
type:
|
|
36655
|
-
phoneNumber:
|
|
36981
|
+
z146.object({
|
|
36982
|
+
type: z146.literal("whatsapp"),
|
|
36983
|
+
phoneNumber: z146.string().min(8).max(32)
|
|
36656
36984
|
})
|
|
36657
36985
|
]),
|
|
36658
|
-
saveAsDefault:
|
|
36986
|
+
saveAsDefault: z146.boolean().default(false),
|
|
36659
36987
|
budgetPolicy: MetaBudgetPolicy.optional()
|
|
36660
36988
|
});
|
|
36661
36989
|
function resolveBudgetMode(value) {
|
|
@@ -36669,7 +36997,7 @@ var GroupedCampaignConfig = GroupedCampaignConfigBase.superRefine((value, ctx) =
|
|
|
36669
36997
|
const budgetMode = resolveBudgetMode(value);
|
|
36670
36998
|
if (budgetMode === "campaign" && value.campaignDailyBudget === void 0) {
|
|
36671
36999
|
ctx.addIssue({
|
|
36672
|
-
code:
|
|
37000
|
+
code: z146.ZodIssueCode.custom,
|
|
36673
37001
|
path: [
|
|
36674
37002
|
"campaignDailyBudget"
|
|
36675
37003
|
],
|
|
@@ -36680,7 +37008,7 @@ var GroupedCampaignConfig = GroupedCampaignConfigBase.superRefine((value, ctx) =
|
|
|
36680
37008
|
value.adSets.forEach((adSet, index) => {
|
|
36681
37009
|
if (adSet.dailyBudget === void 0) {
|
|
36682
37010
|
ctx.addIssue({
|
|
36683
|
-
code:
|
|
37011
|
+
code: z146.ZodIssueCode.custom,
|
|
36684
37012
|
path: [
|
|
36685
37013
|
"adSets",
|
|
36686
37014
|
index,
|
|
@@ -36695,25 +37023,25 @@ var GroupedCampaignConfig = GroupedCampaignConfigBase.superRefine((value, ctx) =
|
|
|
36695
37023
|
...value,
|
|
36696
37024
|
budgetMode: resolveBudgetMode(value)
|
|
36697
37025
|
}));
|
|
36698
|
-
var PublishCreativesToMetaInputBase =
|
|
36699
|
-
creatives:
|
|
37026
|
+
var PublishCreativesToMetaInputBase = z146.object({
|
|
37027
|
+
creatives: z146.array(GroupedCreative).min(1).max(CAMPAIGN_LIMITS.MAX_CREATIVES),
|
|
36700
37028
|
/** Exact Meta Business portfolio selected from `list_meta_destinations`. */
|
|
36701
|
-
portfolioId:
|
|
37029
|
+
portfolioId: z146.string().trim().min(1),
|
|
36702
37030
|
/** Exact Facebook Page selected from `list_meta_destinations`. */
|
|
36703
|
-
pageId:
|
|
37031
|
+
pageId: z146.string().trim().min(1),
|
|
36704
37032
|
adAccountId: McpAdAccountId,
|
|
36705
|
-
pixelId:
|
|
37033
|
+
pixelId: z146.string().min(1).optional(),
|
|
36706
37034
|
/** Required unless `campaignConfig.destination` is whatsapp or a creative
|
|
36707
37035
|
* comes from a task (the API then lands on the published service landing
|
|
36708
37036
|
* of its service tasks) — see the superRefine below. Mirrors the API's
|
|
36709
37037
|
* `FacebookCreateCampaignFromTasksBodySchema`. */
|
|
36710
|
-
landingUrl:
|
|
37038
|
+
landingUrl: z146.string().url().optional(),
|
|
36711
37039
|
/** ISO-2 code or resolvable country name — mirrors the API contract
|
|
36712
37040
|
* (CountryIso2Schema), which normalizes names to alpha-2 at validation.
|
|
36713
37041
|
* Omitted: the brand country of its service tasks (the API answers
|
|
36714
37042
|
* META_NO_TARGET_COUNTRY when it has none); "US" for products. */
|
|
36715
37043
|
targetCountry: CountryIso2Schema.optional(),
|
|
36716
|
-
campaignName:
|
|
37044
|
+
campaignName: z146.string().trim().min(1).max(100),
|
|
36717
37045
|
campaignConfig: GroupedCampaignConfig.optional()
|
|
36718
37046
|
});
|
|
36719
37047
|
var PublishCreativesToMetaInput = PublishCreativesToMetaInputBase.superRefine((value, ctx) => {
|
|
@@ -36721,7 +37049,7 @@ var PublishCreativesToMetaInput = PublishCreativesToMetaInputBase.superRefine((v
|
|
|
36721
37049
|
const hasTaskCreative = value.creatives.some((creative) => creative.source.type === "task");
|
|
36722
37050
|
if (destination?.type !== "whatsapp" && destination?.type !== "landing" && !value.landingUrl && !hasTaskCreative) {
|
|
36723
37051
|
ctx.addIssue({
|
|
36724
|
-
code:
|
|
37052
|
+
code: z146.ZodIssueCode.custom,
|
|
36725
37053
|
path: [
|
|
36726
37054
|
"landingUrl"
|
|
36727
37055
|
],
|
|
@@ -36730,7 +37058,7 @@ var PublishCreativesToMetaInput = PublishCreativesToMetaInputBase.superRefine((v
|
|
|
36730
37058
|
}
|
|
36731
37059
|
if (destination?.type === "landing" && value.landingUrl && destination.landingUrl !== value.landingUrl) {
|
|
36732
37060
|
ctx.addIssue({
|
|
36733
|
-
code:
|
|
37061
|
+
code: z146.ZodIssueCode.custom,
|
|
36734
37062
|
path: [
|
|
36735
37063
|
"campaignConfig",
|
|
36736
37064
|
"destination",
|
|
@@ -36749,16 +37077,16 @@ var PublishCreativesToMetaInput = PublishCreativesToMetaInputBase.superRefine((v
|
|
|
36749
37077
|
}
|
|
36750
37078
|
return value;
|
|
36751
37079
|
});
|
|
36752
|
-
var PresignUploadResponseSchema =
|
|
36753
|
-
uploadUrl:
|
|
37080
|
+
var PresignUploadResponseSchema = z146.object({
|
|
37081
|
+
uploadUrl: z146.string().url().refine((value) => {
|
|
36754
37082
|
const url2 = new URL(value);
|
|
36755
37083
|
return !url2.username && !url2.password && url2.protocol === "https:";
|
|
36756
37084
|
}, "presigned upload URL must be HTTPS without embedded credentials"),
|
|
36757
|
-
publicUrl:
|
|
37085
|
+
publicUrl: z146.string().url().refine((value) => {
|
|
36758
37086
|
const url2 = new URL(value);
|
|
36759
37087
|
return !url2.username && !url2.password;
|
|
36760
37088
|
}, "public media URL must not contain embedded credentials"),
|
|
36761
|
-
stagingAssetId:
|
|
37089
|
+
stagingAssetId: z146.string().uuid()
|
|
36762
37090
|
});
|
|
36763
37091
|
async function uploadResolvedMedia(api, media, assetRole) {
|
|
36764
37092
|
if (!media.buffer) throw new ResolveMediaError("INVALID_INPUT", "resolved media has no bytes");
|
|
@@ -36784,14 +37112,14 @@ async function resolveAndUploadMedia(api, ref, expectedKind, assetRole, transpor
|
|
|
36784
37112
|
}
|
|
36785
37113
|
__name(resolveAndUploadMedia, "resolveAndUploadMedia");
|
|
36786
37114
|
var TikTokMcpCampaignConfig = McpCampaignConfig.extend({
|
|
36787
|
-
dailyBudgetUsd:
|
|
36788
|
-
dailyBudget:
|
|
37115
|
+
dailyBudgetUsd: z146.number().finite().positive().optional().describe("Legacy USD amount, supported only for USD advertiser accounts. No FX conversion."),
|
|
37116
|
+
dailyBudget: z146.number().finite().positive().optional().describe("Daily budget in the selected advertiser currency major units; pass budgetCurrency together."),
|
|
36789
37117
|
budgetCurrency: TikTokBudgetCurrencySchema.optional(),
|
|
36790
37118
|
destination: TikTokCampaignDestinationSchema.optional()
|
|
36791
37119
|
}).superRefine((value, ctx) => {
|
|
36792
37120
|
if (value.dailyBudgetUsd !== void 0 && (value.dailyBudget !== void 0 || value.budgetCurrency !== void 0)) {
|
|
36793
37121
|
ctx.addIssue({
|
|
36794
|
-
code:
|
|
37122
|
+
code: z146.ZodIssueCode.custom,
|
|
36795
37123
|
path: [
|
|
36796
37124
|
"dailyBudgetUsd"
|
|
36797
37125
|
],
|
|
@@ -36800,7 +37128,7 @@ var TikTokMcpCampaignConfig = McpCampaignConfig.extend({
|
|
|
36800
37128
|
}
|
|
36801
37129
|
if (value.dailyBudget !== void 0 !== (value.budgetCurrency !== void 0)) {
|
|
36802
37130
|
ctx.addIssue({
|
|
36803
|
-
code:
|
|
37131
|
+
code: z146.ZodIssueCode.custom,
|
|
36804
37132
|
path: [
|
|
36805
37133
|
"budgetCurrency"
|
|
36806
37134
|
],
|
|
@@ -36808,23 +37136,27 @@ var TikTokMcpCampaignConfig = McpCampaignConfig.extend({
|
|
|
36808
37136
|
});
|
|
36809
37137
|
}
|
|
36810
37138
|
});
|
|
36811
|
-
var TikTokInput =
|
|
36812
|
-
taskId:
|
|
36813
|
-
advertiserId:
|
|
36814
|
-
pixelId:
|
|
36815
|
-
landingUrl:
|
|
36816
|
-
angleIndexes:
|
|
37139
|
+
var TikTokInput = z146.object({
|
|
37140
|
+
taskId: z146.string().min(1),
|
|
37141
|
+
advertiserId: z146.string().min(1),
|
|
37142
|
+
pixelId: z146.string().min(1).optional(),
|
|
37143
|
+
landingUrl: z146.string().url().optional(),
|
|
37144
|
+
angleIndexes: z146.array(z146.number().int().nonnegative()).optional(),
|
|
36817
37145
|
campaignConfig: TikTokMcpCampaignConfig.optional(),
|
|
36818
37146
|
/** Override the ad's description (TikTok's body copy). If omitted,
|
|
36819
37147
|
* ProductMaker uses the auto-generated copy attached to the task. */
|
|
36820
|
-
primaryText:
|
|
37148
|
+
primaryText: z146.string().trim().min(1).max(500).optional()
|
|
36821
37149
|
});
|
|
36822
37150
|
function registerPublishTools(server, api, opts = {}) {
|
|
36823
37151
|
const transport = opts.transport ?? "http";
|
|
36824
37152
|
server.registerTool("publish_to_shopify", {
|
|
36825
37153
|
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.",
|
|
36826
37154
|
inputSchema: ShopifyInput.shape,
|
|
36827
|
-
|
|
37155
|
+
// Publication reaches the public storefront, beyond private account data.
|
|
37156
|
+
annotations: {
|
|
37157
|
+
...OVERWRITES_EXTERNAL,
|
|
37158
|
+
openWorldHint: true
|
|
37159
|
+
}
|
|
36828
37160
|
}, async (input, extra) => {
|
|
36829
37161
|
try {
|
|
36830
37162
|
const parsed = parseShopifyInput(input);
|
|
@@ -36941,7 +37273,13 @@ function registerPublishTools(server, api, opts = {}) {
|
|
|
36941
37273
|
server.registerTool("publish_creatives_to_meta", {
|
|
36942
37274
|
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.',
|
|
36943
37275
|
inputSchema: PublishCreativesToMetaInputBase.shape,
|
|
36944
|
-
|
|
37276
|
+
// May fetch arbitrary public media URLs and consumes owned upload assets
|
|
37277
|
+
// after successful publication, even though the campaign starts paused.
|
|
37278
|
+
annotations: {
|
|
37279
|
+
...CREATES_EXTERNAL,
|
|
37280
|
+
openWorldHint: true,
|
|
37281
|
+
destructiveHint: true
|
|
37282
|
+
}
|
|
36945
37283
|
}, async (rawInput, extra) => {
|
|
36946
37284
|
try {
|
|
36947
37285
|
const parsed = PublishCreativesToMetaInput.safeParse(rawInput);
|
|
@@ -37177,39 +37515,40 @@ __name(toTikTokCampaignConfig, "toTikTokCampaignConfig");
|
|
|
37177
37515
|
|
|
37178
37516
|
// src/tools/meta-upload.ts
|
|
37179
37517
|
import { registerAppTool } from "@modelcontextprotocol/ext-apps/server";
|
|
37180
|
-
import { z as
|
|
37518
|
+
import { z as z147 } from "zod";
|
|
37181
37519
|
var requestMediaUploadGuidance = getProductMakerCapabilityModelGuidance("request_media_upload");
|
|
37182
|
-
var
|
|
37183
|
-
|
|
37520
|
+
var checkMediaUploadGuidance = getProductMakerCapabilityModelGuidance("check_media_upload");
|
|
37521
|
+
var UploadSessionInputBase = z147.object({
|
|
37522
|
+
mediaKind: z147.enum([
|
|
37184
37523
|
"image",
|
|
37185
37524
|
"video",
|
|
37186
37525
|
"mixed"
|
|
37187
37526
|
]).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."),
|
|
37188
|
-
assetRole:
|
|
37527
|
+
assetRole: z147.enum([
|
|
37189
37528
|
"media",
|
|
37190
37529
|
"thumbnail"
|
|
37191
37530
|
]).default("media").describe("Use media for the creative itself; thumbnail only for an image thumbnail."),
|
|
37192
|
-
uploadPurpose:
|
|
37531
|
+
uploadPurpose: z147.enum([
|
|
37193
37532
|
"product_reference",
|
|
37194
37533
|
"creative",
|
|
37195
37534
|
"generic"
|
|
37196
37535
|
]).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."),
|
|
37197
|
-
maxFiles:
|
|
37536
|
+
maxFiles: z147.number().int().min(1).max(10).optional().describe("Optional batch limit: image accepts 1-5, video exactly 1, and mixed 1-10.")
|
|
37198
37537
|
});
|
|
37199
|
-
var OpenAiFileReference =
|
|
37200
|
-
download_url:
|
|
37201
|
-
file_id:
|
|
37202
|
-
mime_type:
|
|
37203
|
-
file_name:
|
|
37538
|
+
var OpenAiFileReference = z147.object({
|
|
37539
|
+
download_url: z147.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."),
|
|
37540
|
+
file_id: z147.string().min(1),
|
|
37541
|
+
mime_type: z147.string().min(1).optional(),
|
|
37542
|
+
file_name: z147.string().min(1).optional()
|
|
37204
37543
|
});
|
|
37205
37544
|
var UploadSessionToolInputShape = {
|
|
37206
37545
|
...UploadSessionInputBase.shape,
|
|
37207
|
-
files:
|
|
37546
|
+
files: z147.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.")
|
|
37208
37547
|
};
|
|
37209
|
-
var UploadSessionToolInput =
|
|
37548
|
+
var UploadSessionToolInput = z147.object(UploadSessionToolInputShape).superRefine((value, ctx) => {
|
|
37210
37549
|
if (value.assetRole === "thumbnail" && value.mediaKind !== "image") {
|
|
37211
37550
|
ctx.addIssue({
|
|
37212
|
-
code:
|
|
37551
|
+
code: z147.ZodIssueCode.custom,
|
|
37213
37552
|
path: [
|
|
37214
37553
|
"mediaKind"
|
|
37215
37554
|
],
|
|
@@ -37218,7 +37557,7 @@ var UploadSessionToolInput = z146.object(UploadSessionToolInputShape).superRefin
|
|
|
37218
37557
|
}
|
|
37219
37558
|
if (value.mediaKind === "video" && value.maxFiles !== void 0 && value.maxFiles !== 1) {
|
|
37220
37559
|
ctx.addIssue({
|
|
37221
|
-
code:
|
|
37560
|
+
code: z147.ZodIssueCode.custom,
|
|
37222
37561
|
path: [
|
|
37223
37562
|
"maxFiles"
|
|
37224
37563
|
],
|
|
@@ -37227,7 +37566,7 @@ var UploadSessionToolInput = z146.object(UploadSessionToolInputShape).superRefin
|
|
|
37227
37566
|
}
|
|
37228
37567
|
if (value.mediaKind === "image" && (value.maxFiles ?? 1) > 5) {
|
|
37229
37568
|
ctx.addIssue({
|
|
37230
|
-
code:
|
|
37569
|
+
code: z147.ZodIssueCode.custom,
|
|
37231
37570
|
path: [
|
|
37232
37571
|
"maxFiles"
|
|
37233
37572
|
],
|
|
@@ -37235,38 +37574,38 @@ var UploadSessionToolInput = z146.object(UploadSessionToolInputShape).superRefin
|
|
|
37235
37574
|
});
|
|
37236
37575
|
}
|
|
37237
37576
|
});
|
|
37238
|
-
var UploadSessionResponse =
|
|
37239
|
-
sessionId:
|
|
37240
|
-
uploadPageUrl:
|
|
37241
|
-
mediaKind:
|
|
37577
|
+
var UploadSessionResponse = z147.object({
|
|
37578
|
+
sessionId: z147.string().uuid(),
|
|
37579
|
+
uploadPageUrl: z147.string().url(),
|
|
37580
|
+
mediaKind: z147.enum([
|
|
37242
37581
|
"image",
|
|
37243
37582
|
"video",
|
|
37244
37583
|
"mixed"
|
|
37245
37584
|
]),
|
|
37246
|
-
assetRole:
|
|
37585
|
+
assetRole: z147.enum([
|
|
37247
37586
|
"media",
|
|
37248
37587
|
"thumbnail"
|
|
37249
37588
|
]),
|
|
37250
|
-
maxFiles:
|
|
37251
|
-
expiresAt:
|
|
37589
|
+
maxFiles: z147.number().int().positive(),
|
|
37590
|
+
expiresAt: z147.string().datetime()
|
|
37252
37591
|
});
|
|
37253
|
-
var UploadStatusInput =
|
|
37254
|
-
sessionId:
|
|
37592
|
+
var UploadStatusInput = z147.object({
|
|
37593
|
+
sessionId: z147.string().uuid().describe("The sessionId returned by request_media_upload.")
|
|
37255
37594
|
});
|
|
37256
|
-
var UniversalUploadStatusResponse =
|
|
37257
|
-
|
|
37258
|
-
status:
|
|
37259
|
-
assets:
|
|
37595
|
+
var UniversalUploadStatusResponse = z147.discriminatedUnion("status", [
|
|
37596
|
+
z147.object({
|
|
37597
|
+
status: z147.literal("pending"),
|
|
37598
|
+
assets: z147.array(z147.never()).length(0)
|
|
37260
37599
|
}),
|
|
37261
|
-
|
|
37262
|
-
status:
|
|
37263
|
-
assets:
|
|
37264
|
-
assetId:
|
|
37265
|
-
mediaKind:
|
|
37600
|
+
z147.object({
|
|
37601
|
+
status: z147.literal("uploaded"),
|
|
37602
|
+
assets: z147.array(z147.object({
|
|
37603
|
+
assetId: z147.string().uuid(),
|
|
37604
|
+
mediaKind: z147.enum([
|
|
37266
37605
|
"image",
|
|
37267
37606
|
"video"
|
|
37268
37607
|
]),
|
|
37269
|
-
assetRole:
|
|
37608
|
+
assetRole: z147.enum([
|
|
37270
37609
|
"media",
|
|
37271
37610
|
"thumbnail"
|
|
37272
37611
|
])
|
|
@@ -37274,13 +37613,13 @@ var UniversalUploadStatusResponse = z146.discriminatedUnion("status", [
|
|
|
37274
37613
|
})
|
|
37275
37614
|
]);
|
|
37276
37615
|
var UniversalUploadStructuredResponse = UploadSessionResponse.extend({
|
|
37277
|
-
uploadPurpose:
|
|
37616
|
+
uploadPurpose: z147.enum([
|
|
37278
37617
|
"product_reference",
|
|
37279
37618
|
"creative",
|
|
37280
37619
|
"generic"
|
|
37281
37620
|
]),
|
|
37282
|
-
checkToolName:
|
|
37283
|
-
resolveEndpoint:
|
|
37621
|
+
checkToolName: z147.literal("check_media_upload"),
|
|
37622
|
+
resolveEndpoint: z147.string().url()
|
|
37284
37623
|
});
|
|
37285
37624
|
function contextualizeUploadPageUrl(uploadPageUrl, uploadPurpose) {
|
|
37286
37625
|
const url2 = new URL(uploadPageUrl);
|
|
@@ -37312,7 +37651,7 @@ __name(resolveEndpointForSession, "resolveEndpointForSession");
|
|
|
37312
37651
|
function registerMediaUploadTools(server, api) {
|
|
37313
37652
|
registerAppTool(server, "request_media_upload", {
|
|
37314
37653
|
title: "Subir archivos",
|
|
37315
|
-
description: requestMediaUploadGuidance.summary + "
|
|
37654
|
+
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.",
|
|
37316
37655
|
inputSchema: UploadSessionToolInputShape,
|
|
37317
37656
|
annotations: CREATES_INTERNAL,
|
|
37318
37657
|
outputSchema: UniversalUploadStructuredResponse.shape,
|
|
@@ -37353,7 +37692,7 @@ function registerMediaUploadTools(server, api) {
|
|
|
37353
37692
|
}
|
|
37354
37693
|
});
|
|
37355
37694
|
server.registerTool("check_media_upload", {
|
|
37356
|
-
description:
|
|
37695
|
+
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.",
|
|
37357
37696
|
inputSchema: UploadStatusInput.shape,
|
|
37358
37697
|
annotations: READS_INTERNAL,
|
|
37359
37698
|
_meta: {
|
|
@@ -37371,15 +37710,15 @@ function registerMediaUploadTools(server, api) {
|
|
|
37371
37710
|
__name(registerMediaUploadTools, "registerMediaUploadTools");
|
|
37372
37711
|
|
|
37373
37712
|
// src/tools/campaigns.ts
|
|
37374
|
-
import { z as
|
|
37713
|
+
import { z as z148 } from "zod";
|
|
37375
37714
|
var getMetaCampaignStatusGuidance = getProductMakerCapabilityModelGuidance("get_meta_campaign_status");
|
|
37376
37715
|
var getTikTokCampaignStatusGuidance = getProductMakerCapabilityModelGuidance("get_tiktok_campaign_status");
|
|
37377
|
-
var MetaStatusInput =
|
|
37378
|
-
campaignId:
|
|
37716
|
+
var MetaStatusInput = z148.object({
|
|
37717
|
+
campaignId: z148.string().min(1).describe("Meta campaign ID returned by publish_to_meta.")
|
|
37379
37718
|
});
|
|
37380
|
-
var TikTokStatusInput =
|
|
37381
|
-
campaignId:
|
|
37382
|
-
advertiserId:
|
|
37719
|
+
var TikTokStatusInput = z148.object({
|
|
37720
|
+
campaignId: z148.string().min(1).describe("TikTok campaign ID returned by publish_to_tiktok."),
|
|
37721
|
+
advertiserId: z148.string().min(1).describe("The same advertiserId used to publish the campaign.")
|
|
37383
37722
|
});
|
|
37384
37723
|
function registerCampaignTools(server, api) {
|
|
37385
37724
|
server.registerTool("get_meta_campaign_status", {
|
|
@@ -37484,10 +37823,10 @@ function renderTikTokStatusMarkdown(r) {
|
|
|
37484
37823
|
__name(renderTikTokStatusMarkdown, "renderTikTokStatusMarkdown");
|
|
37485
37824
|
|
|
37486
37825
|
// ../../packages/product-economics/src/product-costing.ts
|
|
37487
|
-
import { z as
|
|
37826
|
+
import { z as z150 } from "zod";
|
|
37488
37827
|
|
|
37489
37828
|
// ../../packages/product-economics/src/product-costing-countries.ts
|
|
37490
|
-
import { z as
|
|
37829
|
+
import { z as z149 } from "zod";
|
|
37491
37830
|
|
|
37492
37831
|
// ../../packages/product-economics/src/product-costing-shipping-references.json
|
|
37493
37832
|
var product_costing_shipping_references_default = {
|
|
@@ -37551,21 +37890,21 @@ var product_costing_shipping_references_default = {
|
|
|
37551
37890
|
};
|
|
37552
37891
|
|
|
37553
37892
|
// ../../packages/product-economics/src/product-costing-countries.ts
|
|
37554
|
-
var ShippingReferenceSchema =
|
|
37555
|
-
reference:
|
|
37556
|
-
min:
|
|
37557
|
-
max:
|
|
37558
|
-
updatedAt:
|
|
37559
|
-
confidence:
|
|
37893
|
+
var ShippingReferenceSchema = z149.object({
|
|
37894
|
+
reference: z149.number().positive(),
|
|
37895
|
+
min: z149.number().nonnegative(),
|
|
37896
|
+
max: z149.number().positive(),
|
|
37897
|
+
updatedAt: z149.string().date(),
|
|
37898
|
+
confidence: z149.enum([
|
|
37560
37899
|
"high",
|
|
37561
37900
|
"medium",
|
|
37562
37901
|
"medium-low"
|
|
37563
37902
|
]),
|
|
37564
|
-
sources:
|
|
37903
|
+
sources: z149.array(z149.string().url().startsWith("https://")).min(1).max(3)
|
|
37565
37904
|
}).superRefine(({ reference, min, max }, context) => {
|
|
37566
37905
|
if (min > reference || reference > max) {
|
|
37567
37906
|
context.addIssue({
|
|
37568
|
-
code:
|
|
37907
|
+
code: z149.ZodIssueCode.custom,
|
|
37569
37908
|
message: "shipping_reference_outside_range",
|
|
37570
37909
|
path: [
|
|
37571
37910
|
"reference"
|
|
@@ -37573,7 +37912,7 @@ var ShippingReferenceSchema = z148.object({
|
|
|
37573
37912
|
});
|
|
37574
37913
|
}
|
|
37575
37914
|
});
|
|
37576
|
-
var SHIPPING_REFERENCES =
|
|
37915
|
+
var SHIPPING_REFERENCES = z149.record(ShippingReferenceSchema).parse(product_costing_shipping_references_default);
|
|
37577
37916
|
var PRODUCT_COSTING_COUNTRY_CODES = Object.freeze(Object.keys(SHIPPING_REFERENCES));
|
|
37578
37917
|
var SPANISH_LOCALE_COUNTRIES = /* @__PURE__ */ new Set([
|
|
37579
37918
|
"MX",
|
|
@@ -37755,18 +38094,18 @@ function getProductCostingCountryName(country) {
|
|
|
37755
38094
|
__name(getProductCostingCountryName, "getProductCostingCountryName");
|
|
37756
38095
|
|
|
37757
38096
|
// ../../packages/product-economics/src/product-costing.ts
|
|
37758
|
-
var ProductCostingInputSchema =
|
|
37759
|
-
country:
|
|
37760
|
-
supplierCost:
|
|
37761
|
-
shippingCost:
|
|
37762
|
-
returnRate:
|
|
37763
|
-
cancellationRate:
|
|
37764
|
-
adsManagerCpa:
|
|
37765
|
-
desiredMargin:
|
|
38097
|
+
var ProductCostingInputSchema = z150.object({
|
|
38098
|
+
country: z150.string().refine(isProductCostingCountryCode, "unsupported_country"),
|
|
38099
|
+
supplierCost: z150.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").positive("supplier_cost_positive"),
|
|
38100
|
+
shippingCost: z150.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").nonnegative("shipping_cost_nonnegative"),
|
|
38101
|
+
returnRate: z150.number().finite().min(0, "rate_nonnegative").lt(1, "rate_below_one"),
|
|
38102
|
+
cancellationRate: z150.number().finite().min(0, "rate_nonnegative").lt(1, "rate_below_one"),
|
|
38103
|
+
adsManagerCpa: z150.number().finite().max(Number.MAX_SAFE_INTEGER, "amount_too_large").nonnegative("cpa_nonnegative"),
|
|
38104
|
+
desiredMargin: z150.number().finite().min(0, "margin_nonnegative").lt(1, "margin_below_one")
|
|
37766
38105
|
}).superRefine(({ returnRate, cancellationRate }, context) => {
|
|
37767
38106
|
if (returnRate + cancellationRate >= 1) {
|
|
37768
38107
|
context.addIssue({
|
|
37769
|
-
code:
|
|
38108
|
+
code: z150.ZodIssueCode.custom,
|
|
37770
38109
|
message: "delivery_rate_positive",
|
|
37771
38110
|
path: [
|
|
37772
38111
|
"cancellationRate"
|
|
@@ -37863,15 +38202,15 @@ function calculateProductCosting(rawInput) {
|
|
|
37863
38202
|
__name(calculateProductCosting, "calculateProductCosting");
|
|
37864
38203
|
|
|
37865
38204
|
// src/tools/product-costing.ts
|
|
37866
|
-
import { z as
|
|
38205
|
+
import { z as z151 } from "zod";
|
|
37867
38206
|
var costProductGuidance = getProductMakerCapabilityModelGuidance("cost_product");
|
|
37868
|
-
var optionalAmount = /* @__PURE__ */ __name((description) =>
|
|
37869
|
-
var optionalPercent = /* @__PURE__ */ __name((description) =>
|
|
37870
|
-
var ProductCostingToolInput =
|
|
37871
|
-
country:
|
|
38207
|
+
var optionalAmount = /* @__PURE__ */ __name((description) => z151.number().finite().max(Number.MAX_SAFE_INTEGER).nonnegative().optional().describe(description), "optionalAmount");
|
|
38208
|
+
var optionalPercent = /* @__PURE__ */ __name((description) => z151.number().finite().min(0).lt(100).optional().describe(description), "optionalPercent");
|
|
38209
|
+
var ProductCostingToolInput = z151.object({
|
|
38210
|
+
country: z151.string().trim().toUpperCase().refine(isProductCostingCountryCode, {
|
|
37872
38211
|
message: "Pa\xEDs no soportado por ProductMaker."
|
|
37873
38212
|
}).optional().describe("C\xF3digo ISO-2 de cualquiera de los pa\xEDses disponibles en ProductMaker. Omite para Colombia."),
|
|
37874
|
-
supplierCost:
|
|
38213
|
+
supplierCost: z151.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."),
|
|
37875
38214
|
supplierCostCop: optionalAmount("Alias anterior para costo del proveedor en Colombia. Prefiere supplierCost."),
|
|
37876
38215
|
shippingCost: optionalAmount("Costo de un env\xEDo en la moneda del pa\xEDs. Omite para usar el valor gu\xEDa vigente."),
|
|
37877
38216
|
shippingCostCop: optionalAmount("Alias anterior para flete en Colombia. Prefiere shippingCost."),
|
|
@@ -38034,7 +38373,7 @@ function formatPercent(value) {
|
|
|
38034
38373
|
__name(formatPercent, "formatPercent");
|
|
38035
38374
|
|
|
38036
38375
|
// src/tools/product-research.ts
|
|
38037
|
-
import { z as
|
|
38376
|
+
import { z as z152 } from "zod";
|
|
38038
38377
|
|
|
38039
38378
|
// src/tools/product-research-idempotency.ts
|
|
38040
38379
|
var RESEARCH_IDEMPOTENCY_WINDOW_MS = 2 * 60 * 60 * 1e3;
|
|
@@ -38405,17 +38744,17 @@ var TERMINAL2 = /* @__PURE__ */ new Set([
|
|
|
38405
38744
|
]);
|
|
38406
38745
|
var POLL_INTERVAL_MS = 5e3;
|
|
38407
38746
|
var MAX_WAIT_SECONDS = 120;
|
|
38408
|
-
var FindInput =
|
|
38747
|
+
var FindInput = z152.object({
|
|
38409
38748
|
targetCountry: ResearchTargetCountryEnum.describe("Pa\xEDs objetivo. S\xF3lo CO, MX, PE, EC y CL tienen cobertura de investigaci\xF3n."),
|
|
38410
38749
|
categoryKey: ResearchCategoryEnum.default("auto").describe('Categor\xEDa a investigar. Usa "auto" para que la IA elija seg\xFAn el mercado.'),
|
|
38411
|
-
confirmed:
|
|
38412
|
-
waitSeconds:
|
|
38750
|
+
confirmed: z152.boolean().describe(`Ponlo en true SOLO despu\xE9s de que el usuario haya aceptado el cargo de ${PRICE_CREDITS} cr\xE9ditos. No lo asumas.`),
|
|
38751
|
+
waitSeconds: z152.number().int().min(0).max(MAX_WAIT_SECONDS).optional().describe("Segundos a esperar el reporte antes de devolver el runId. M\xE1ximo 120.")
|
|
38413
38752
|
});
|
|
38414
|
-
var GetInput =
|
|
38415
|
-
runId:
|
|
38753
|
+
var GetInput = z152.object({
|
|
38754
|
+
runId: z152.string().uuid().describe("ID del run devuelto por find_winning_products.")
|
|
38416
38755
|
});
|
|
38417
|
-
var ListInput =
|
|
38418
|
-
limit:
|
|
38756
|
+
var ListInput = z152.object({
|
|
38757
|
+
limit: z152.number().int().min(1).max(20).default(10)
|
|
38419
38758
|
});
|
|
38420
38759
|
function textResult(text4) {
|
|
38421
38760
|
return {
|
|
@@ -38554,17 +38893,17 @@ _Esta llamada no cobr\xF3 cr\xE9ditos: se reutiliz\xF3 una investigaci\xF3n reci
|
|
|
38554
38893
|
__name(registerProductResearchTools, "registerProductResearchTools");
|
|
38555
38894
|
|
|
38556
38895
|
// src/tools/dropshipping-projects.ts
|
|
38557
|
-
import { z as
|
|
38896
|
+
import { z as z153 } from "zod";
|
|
38558
38897
|
var startDropshippingProjectGuidance = getProductMakerCapabilityModelGuidance("start_dropshipping_project");
|
|
38559
38898
|
var costDropshippingProjectGuidance = getProductMakerCapabilityModelGuidance("cost_dropshipping_project");
|
|
38560
38899
|
var guideDropshippingLaunchGuidance = getProductMakerCapabilityModelGuidance("guide_dropshipping_launch");
|
|
38561
|
-
var ProjectId =
|
|
38562
|
-
var DropshippingProjectStatus =
|
|
38900
|
+
var ProjectId = z153.string().uuid();
|
|
38901
|
+
var DropshippingProjectStatus = z153.enum([
|
|
38563
38902
|
"active",
|
|
38564
38903
|
"completed",
|
|
38565
38904
|
"archived"
|
|
38566
38905
|
]);
|
|
38567
|
-
var DropshippingProjectPhase =
|
|
38906
|
+
var DropshippingProjectPhase = z153.enum([
|
|
38568
38907
|
"research",
|
|
38569
38908
|
"costing",
|
|
38570
38909
|
"media",
|
|
@@ -38573,21 +38912,21 @@ var DropshippingProjectPhase = z152.enum([
|
|
|
38573
38912
|
"advertising",
|
|
38574
38913
|
"complete"
|
|
38575
38914
|
]);
|
|
38576
|
-
var DropshippingRewardMilestone =
|
|
38915
|
+
var DropshippingRewardMilestone = z153.enum([
|
|
38577
38916
|
"product_selected",
|
|
38578
38917
|
"costing_saved",
|
|
38579
38918
|
"product_media_ready",
|
|
38580
38919
|
"creatives_completed",
|
|
38581
38920
|
"campaign_created"
|
|
38582
38921
|
]);
|
|
38583
|
-
var TargetCountry =
|
|
38922
|
+
var TargetCountry = z153.enum([
|
|
38584
38923
|
"CO",
|
|
38585
38924
|
"MX",
|
|
38586
38925
|
"PE",
|
|
38587
38926
|
"EC",
|
|
38588
38927
|
"CL"
|
|
38589
38928
|
]);
|
|
38590
|
-
var ResearchCategory =
|
|
38929
|
+
var ResearchCategory = z153.enum([
|
|
38591
38930
|
"auto",
|
|
38592
38931
|
"automotive",
|
|
38593
38932
|
"kitchen",
|
|
@@ -38602,7 +38941,7 @@ var ResearchCategory = z152.enum([
|
|
|
38602
38941
|
"garden",
|
|
38603
38942
|
"cleaning"
|
|
38604
38943
|
]);
|
|
38605
|
-
var HttpUrl3 =
|
|
38944
|
+
var HttpUrl3 = z153.string().url().max(2e3).refine((value) => {
|
|
38606
38945
|
try {
|
|
38607
38946
|
return [
|
|
38608
38947
|
"http:",
|
|
@@ -38612,45 +38951,45 @@ var HttpUrl3 = z152.string().url().max(2e3).refine((value) => {
|
|
|
38612
38951
|
return false;
|
|
38613
38952
|
}
|
|
38614
38953
|
}, "URL must use HTTP(S)");
|
|
38615
|
-
var ResearchCandidateOrigin =
|
|
38616
|
-
type:
|
|
38954
|
+
var ResearchCandidateOrigin = z153.object({
|
|
38955
|
+
type: z153.literal("research_candidate"),
|
|
38617
38956
|
runId: ProjectId,
|
|
38618
|
-
candidateSourceId:
|
|
38957
|
+
candidateSourceId: z153.string().min(1).max(160)
|
|
38619
38958
|
});
|
|
38620
|
-
var TaskOrigin =
|
|
38621
|
-
type:
|
|
38959
|
+
var TaskOrigin = z153.object({
|
|
38960
|
+
type: z153.literal("task"),
|
|
38622
38961
|
taskId: ProjectId
|
|
38623
38962
|
});
|
|
38624
|
-
var ManualOrigin =
|
|
38625
|
-
type:
|
|
38963
|
+
var ManualOrigin = z153.object({
|
|
38964
|
+
type: z153.literal("manual"),
|
|
38626
38965
|
clientRequestId: ProjectId,
|
|
38627
|
-
country:
|
|
38966
|
+
country: z153.string().min(2).max(3).optional()
|
|
38628
38967
|
});
|
|
38629
|
-
var StartProjectInput =
|
|
38630
|
-
origin:
|
|
38968
|
+
var StartProjectInput = z153.object({
|
|
38969
|
+
origin: z153.discriminatedUnion("type", [
|
|
38631
38970
|
ResearchCandidateOrigin,
|
|
38632
38971
|
TaskOrigin,
|
|
38633
38972
|
ManualOrigin
|
|
38634
38973
|
]),
|
|
38635
|
-
productName:
|
|
38974
|
+
productName: z153.string().trim().min(1).max(240).optional(),
|
|
38636
38975
|
productUrl: HttpUrl3.optional(),
|
|
38637
|
-
country:
|
|
38638
|
-
languageCode:
|
|
38976
|
+
country: z153.string().min(2).max(3).optional(),
|
|
38977
|
+
languageCode: z153.string().min(2).max(16).optional()
|
|
38639
38978
|
}).strict();
|
|
38640
|
-
var CostProjectInput =
|
|
38979
|
+
var CostProjectInput = z153.object({
|
|
38641
38980
|
projectId: ProjectId,
|
|
38642
|
-
version:
|
|
38643
|
-
country:
|
|
38981
|
+
version: z153.number().int().positive().describe("Version le\xEDda del proyecto. Copia la \xFAltima que viste; no se la pidas al usuario."),
|
|
38982
|
+
country: z153.string().trim().toUpperCase().refine(isProductCostingCountryCode, {
|
|
38644
38983
|
message: "Pa\xEDs no soportado por ProductMaker."
|
|
38645
38984
|
}).describe("C\xF3digo ISO-2 del pa\xEDs del proyecto. Usa el `targetCountry` que devuelve el proyecto."),
|
|
38646
|
-
supplierCost:
|
|
38647
|
-
shippingCost:
|
|
38648
|
-
returnRate:
|
|
38649
|
-
cancellationRate:
|
|
38650
|
-
adsManagerCpa:
|
|
38651
|
-
desiredMargin:
|
|
38652
|
-
});
|
|
38653
|
-
var GuideInput =
|
|
38985
|
+
supplierCost: z153.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."),
|
|
38986
|
+
shippingCost: z153.number().finite().nonnegative().optional().describe("Costo de un env\xEDo. Omite para usar el valor gu\xEDa del pa\xEDs en vez de inventarlo."),
|
|
38987
|
+
returnRate: z153.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."),
|
|
38988
|
+
cancellationRate: z153.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."),
|
|
38989
|
+
adsManagerCpa: z153.number().finite().nonnegative().optional().describe("Costo por compra de Ads Manager. Omite para usar el valor gu\xEDa del pa\xEDs."),
|
|
38990
|
+
desiredMargin: z153.number().finite().min(0).lt(1).optional().describe("Margen deseado (0.1 = 10%). Omite para usar el valor gu\xEDa del pa\xEDs.")
|
|
38991
|
+
});
|
|
38992
|
+
var GuideInput = z153.object({
|
|
38654
38993
|
projectId: ProjectId.optional()
|
|
38655
38994
|
}).strict();
|
|
38656
38995
|
var NEXT_ACTION_KINDS = [
|
|
@@ -38668,29 +39007,29 @@ var NEXT_ACTION_KINDS = [
|
|
|
38668
39007
|
"repair_meta_campaign",
|
|
38669
39008
|
"review_paused_campaign"
|
|
38670
39009
|
];
|
|
38671
|
-
var DropshippingProjectNextAction =
|
|
38672
|
-
kind:
|
|
38673
|
-
href:
|
|
39010
|
+
var DropshippingProjectNextAction = z153.discriminatedUnion("kind", NEXT_ACTION_KINDS.map((kind2) => z153.object({
|
|
39011
|
+
kind: z153.literal(kind2),
|
|
39012
|
+
href: z153.string().min(1)
|
|
38674
39013
|
})));
|
|
38675
|
-
var CostingInputSchema =
|
|
38676
|
-
country:
|
|
38677
|
-
supplierCost:
|
|
38678
|
-
shippingCost:
|
|
38679
|
-
returnRate:
|
|
38680
|
-
cancellationRate:
|
|
38681
|
-
adsManagerCpa:
|
|
38682
|
-
desiredMargin:
|
|
39014
|
+
var CostingInputSchema = z153.object({
|
|
39015
|
+
country: z153.string().min(2).max(3),
|
|
39016
|
+
supplierCost: z153.number().finite().positive(),
|
|
39017
|
+
shippingCost: z153.number().finite().nonnegative(),
|
|
39018
|
+
returnRate: z153.number().finite().min(0).lt(1),
|
|
39019
|
+
cancellationRate: z153.number().finite().min(0).lt(1),
|
|
39020
|
+
adsManagerCpa: z153.number().finite().nonnegative(),
|
|
39021
|
+
desiredMargin: z153.number().finite().min(0).lt(1)
|
|
38683
39022
|
}).superRefine(({ returnRate, cancellationRate }, context) => {
|
|
38684
39023
|
if (returnRate + cancellationRate >= 1) context.addIssue({
|
|
38685
|
-
code:
|
|
39024
|
+
code: z153.ZodIssueCode.custom,
|
|
38686
39025
|
path: [
|
|
38687
39026
|
"cancellationRate"
|
|
38688
39027
|
]
|
|
38689
39028
|
});
|
|
38690
39029
|
});
|
|
38691
|
-
var FiniteCostingNumber =
|
|
38692
|
-
var CostingResultSchema =
|
|
38693
|
-
country:
|
|
39030
|
+
var FiniteCostingNumber = z153.number().finite();
|
|
39031
|
+
var CostingResultSchema = z153.object({
|
|
39032
|
+
country: z153.string().min(2).max(3),
|
|
38694
39033
|
deliveryRate: FiniteCostingNumber,
|
|
38695
39034
|
adjustedShippingCost: FiniteCostingNumber,
|
|
38696
39035
|
adjustedCpa: FiniteCostingNumber,
|
|
@@ -38703,45 +39042,45 @@ var CostingResultSchema = z152.object({
|
|
|
38703
39042
|
estimatedProfit: FiniteCostingNumber,
|
|
38704
39043
|
actualMargin: FiniteCostingNumber,
|
|
38705
39044
|
breakEvenAdsManagerCpa: FiniteCostingNumber,
|
|
38706
|
-
wasPriceAutoAdjusted:
|
|
38707
|
-
profitabilityStatus:
|
|
38708
|
-
});
|
|
38709
|
-
var RewardProgressSchema =
|
|
38710
|
-
campaignKey:
|
|
38711
|
-
totalCredits:
|
|
38712
|
-
earnedCredits:
|
|
38713
|
-
milestones:
|
|
39045
|
+
wasPriceAutoAdjusted: z153.boolean(),
|
|
39046
|
+
profitabilityStatus: z153.literal("healthy")
|
|
39047
|
+
});
|
|
39048
|
+
var RewardProgressSchema = z153.object({
|
|
39049
|
+
campaignKey: z153.literal("dropshipping-launch-v1"),
|
|
39050
|
+
totalCredits: z153.number().int().positive(),
|
|
39051
|
+
earnedCredits: z153.number().int().nonnegative(),
|
|
39052
|
+
milestones: z153.array(z153.object({
|
|
38714
39053
|
milestone: DropshippingRewardMilestone,
|
|
38715
|
-
credits:
|
|
38716
|
-
available:
|
|
38717
|
-
earned:
|
|
39054
|
+
credits: z153.number().int().positive(),
|
|
39055
|
+
available: z153.boolean().optional(),
|
|
39056
|
+
earned: z153.boolean(),
|
|
38718
39057
|
grantId: ProjectId.nullable(),
|
|
38719
|
-
grantedAt:
|
|
39058
|
+
grantedAt: z153.string().datetime().nullable()
|
|
38720
39059
|
})).length(DropshippingRewardMilestone.options.length)
|
|
38721
39060
|
});
|
|
38722
|
-
var ProjectResponseSchema =
|
|
39061
|
+
var ProjectResponseSchema = z153.object({
|
|
38723
39062
|
id: ProjectId,
|
|
38724
39063
|
taskId: ProjectId.nullable(),
|
|
38725
39064
|
status: DropshippingProjectStatus,
|
|
38726
|
-
originKey:
|
|
39065
|
+
originKey: z153.string().min(1),
|
|
38727
39066
|
researchRunId: ProjectId.nullable(),
|
|
38728
|
-
candidateSourceId:
|
|
38729
|
-
productName:
|
|
39067
|
+
candidateSourceId: z153.string().nullable(),
|
|
39068
|
+
productName: z153.string().nullable(),
|
|
38730
39069
|
productUrl: HttpUrl3.nullable(),
|
|
38731
39070
|
referenceImageUrl: HttpUrl3.nullable(),
|
|
38732
|
-
targetCountry:
|
|
38733
|
-
languageCode:
|
|
38734
|
-
hasReferenceImage:
|
|
38735
|
-
hasWinnerReferenceImage:
|
|
39071
|
+
targetCountry: z153.string().nullable(),
|
|
39072
|
+
languageCode: z153.string().nullable(),
|
|
39073
|
+
hasReferenceImage: z153.boolean(),
|
|
39074
|
+
hasWinnerReferenceImage: z153.boolean(),
|
|
38736
39075
|
costingInput: CostingInputSchema.nullable(),
|
|
38737
39076
|
costingResult: CostingResultSchema.nullable(),
|
|
38738
|
-
costingVersion:
|
|
38739
|
-
version:
|
|
39077
|
+
costingVersion: z153.number().int().positive().nullable(),
|
|
39078
|
+
version: z153.number().int().positive(),
|
|
38740
39079
|
phase: DropshippingProjectPhase,
|
|
38741
39080
|
nextAction: DropshippingProjectNextAction.nullable(),
|
|
38742
39081
|
rewards: RewardProgressSchema,
|
|
38743
|
-
createdAt:
|
|
38744
|
-
updatedAt:
|
|
39082
|
+
createdAt: z153.string().datetime(),
|
|
39083
|
+
updatedAt: z153.string().datetime()
|
|
38745
39084
|
}).passthrough();
|
|
38746
39085
|
var NEXT_ACTION_GUIDANCE = {
|
|
38747
39086
|
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.",
|
|
@@ -38861,58 +39200,58 @@ function registerDropshippingProjectTools(server, api) {
|
|
|
38861
39200
|
__name(registerDropshippingProjectTools, "registerDropshippingProjectTools");
|
|
38862
39201
|
|
|
38863
39202
|
// src/tools/daily-control.ts
|
|
38864
|
-
import { z as
|
|
39203
|
+
import { z as z154 } from "zod";
|
|
38865
39204
|
var getDailyControlGuidance = getProductMakerCapabilityModelGuidance("get_daily_control");
|
|
38866
39205
|
var linkCampaignToProjectGuidance = getProductMakerCapabilityModelGuidance("link_campaign_to_project");
|
|
38867
39206
|
var reportDailyReturnsGuidance = getProductMakerCapabilityModelGuidance("report_daily_returns");
|
|
38868
|
-
var ProviderCampaignIdentifier =
|
|
38869
|
-
var DateOnly =
|
|
38870
|
-
var DailyControlInput =
|
|
39207
|
+
var ProviderCampaignIdentifier = z154.string().min(1).max(64).regex(/^[A-Za-z0-9_-]+$/);
|
|
39208
|
+
var DateOnly = z154.string().regex(/^\d{4}-\d{2}-\d{2}$/);
|
|
39209
|
+
var DailyControlInput = z154.object({
|
|
38871
39210
|
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."),
|
|
38872
|
-
days:
|
|
38873
|
-
projectId:
|
|
39211
|
+
days: z154.number().int().min(1).max(14).default(3).describe("Tama\xF1o de la ventana, 1 a 14 d\xEDas."),
|
|
39212
|
+
projectId: z154.string().uuid().optional().describe("Opcional: limita el reporte a un solo proyecto en vez de todos los productos activos.")
|
|
38874
39213
|
}).strict();
|
|
38875
|
-
var LinkCampaignInput =
|
|
38876
|
-
projectId:
|
|
38877
|
-
provider:
|
|
39214
|
+
var LinkCampaignInput = z154.object({
|
|
39215
|
+
projectId: z154.string().uuid(),
|
|
39216
|
+
provider: z154.enum([
|
|
38878
39217
|
"meta",
|
|
38879
39218
|
"tiktok"
|
|
38880
39219
|
]),
|
|
38881
39220
|
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."),
|
|
38882
39221
|
campaignId: ProviderCampaignIdentifier
|
|
38883
39222
|
}).strict();
|
|
38884
|
-
var ReportReturnsInput =
|
|
38885
|
-
projectId:
|
|
39223
|
+
var ReportReturnsInput = z154.object({
|
|
39224
|
+
projectId: z154.string().uuid(),
|
|
38886
39225
|
date: DateOnly,
|
|
38887
|
-
returnedOrders:
|
|
38888
|
-
note:
|
|
38889
|
-
}).strict();
|
|
38890
|
-
var DailyControlStatusMirror = DailyControlStatusEnum.or(
|
|
38891
|
-
var DailyControlLightMirror = DailyControlLightEnum.or(
|
|
38892
|
-
var DailyControlSourceMirror = DailyControlSourceEnum.or(
|
|
38893
|
-
var DailyControlVerdictMirror = DailyControlVerdictEnum.or(
|
|
38894
|
-
var DailyControlDayMirror =
|
|
38895
|
-
date:
|
|
38896
|
-
returnedOrders:
|
|
39226
|
+
returnedOrders: z154.number().int().min(0).max(1e5),
|
|
39227
|
+
note: z154.string().trim().max(280).optional()
|
|
39228
|
+
}).strict();
|
|
39229
|
+
var DailyControlStatusMirror = DailyControlStatusEnum.or(z154.string());
|
|
39230
|
+
var DailyControlLightMirror = DailyControlLightEnum.or(z154.string());
|
|
39231
|
+
var DailyControlSourceMirror = DailyControlSourceEnum.or(z154.string());
|
|
39232
|
+
var DailyControlVerdictMirror = DailyControlVerdictEnum.or(z154.string());
|
|
39233
|
+
var DailyControlDayMirror = z154.object({
|
|
39234
|
+
date: z154.string(),
|
|
39235
|
+
returnedOrders: z154.number(),
|
|
38897
39236
|
returnedSource: DailyControlSourceMirror,
|
|
38898
39237
|
cancelledSource: DailyControlSourceMirror,
|
|
38899
|
-
hasEstimates:
|
|
39238
|
+
hasEstimates: z154.boolean(),
|
|
38900
39239
|
light: DailyControlLightMirror
|
|
38901
39240
|
}).passthrough();
|
|
38902
|
-
var DailyControlProductMirror =
|
|
38903
|
-
projectId:
|
|
38904
|
-
productName:
|
|
39241
|
+
var DailyControlProductMirror = z154.object({
|
|
39242
|
+
projectId: z154.string().uuid(),
|
|
39243
|
+
productName: z154.string().nullable(),
|
|
38905
39244
|
status: DailyControlStatusMirror,
|
|
38906
|
-
statusMessage:
|
|
38907
|
-
days:
|
|
39245
|
+
statusMessage: z154.string().nullable(),
|
|
39246
|
+
days: z154.array(DailyControlDayMirror),
|
|
38908
39247
|
verdict: DailyControlVerdictMirror.nullable(),
|
|
38909
|
-
rationale:
|
|
39248
|
+
rationale: z154.string().nullable()
|
|
38910
39249
|
}).passthrough();
|
|
38911
|
-
var DailyControlResponseMirror =
|
|
38912
|
-
since:
|
|
38913
|
-
until:
|
|
38914
|
-
products:
|
|
38915
|
-
archivedProjectSpend:
|
|
39250
|
+
var DailyControlResponseMirror = z154.object({
|
|
39251
|
+
since: z154.string(),
|
|
39252
|
+
until: z154.string(),
|
|
39253
|
+
products: z154.array(DailyControlProductMirror),
|
|
39254
|
+
archivedProjectSpend: z154.array(z154.unknown())
|
|
38916
39255
|
}).passthrough();
|
|
38917
39256
|
var MCP_WARNINGS_KEY = "_mcpDailyControlWarnings";
|
|
38918
39257
|
function withWarnings(response) {
|
|
@@ -38988,7 +39327,7 @@ __name(registerDailyControlTools, "registerDailyControlTools");
|
|
|
38988
39327
|
import { createHash as createHash4 } from "crypto";
|
|
38989
39328
|
import { readFile, stat } from "fs/promises";
|
|
38990
39329
|
import { basename as basename2, isAbsolute, resolve as resolve3 } from "path";
|
|
38991
|
-
import { z as
|
|
39330
|
+
import { z as z155 } from "zod";
|
|
38992
39331
|
var getProductControlGuidance = getProductMakerCapabilityModelGuidance("get_product_control");
|
|
38993
39332
|
var listImportableProductsGuidance = getProductMakerCapabilityModelGuidance("list_importable_products");
|
|
38994
39333
|
var importProductControlsGuidance = getProductMakerCapabilityModelGuidance("import_product_controls");
|
|
@@ -39010,42 +39349,42 @@ var listLinkableCampaignsGuidance = getProductMakerCapabilityModelGuidance("list
|
|
|
39010
39349
|
var linkProductCampaignGuidance = getProductMakerCapabilityModelGuidance("link_product_campaign");
|
|
39011
39350
|
var deleteMonthlyExpenseGuidance = getProductMakerCapabilityModelGuidance("delete_monthly_expense");
|
|
39012
39351
|
var removeProductControlGuidance = getProductMakerCapabilityModelGuidance("remove_product_control");
|
|
39013
|
-
var DateOnly2 =
|
|
39352
|
+
var DateOnly2 = z155.string().regex(/^\d{4}-\d{2}-\d{2}$/);
|
|
39014
39353
|
var IMPORT_BATCH_MAX2 = 50;
|
|
39015
|
-
var ProductControlListInput =
|
|
39016
|
-
days:
|
|
39354
|
+
var ProductControlListInput = z155.object({
|
|
39355
|
+
days: z155.number().int().min(1).max(14).default(3).describe("Tama\xF1o de la ventana, 1 a 14 d\xEDas."),
|
|
39017
39356
|
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."),
|
|
39018
|
-
shopDomain:
|
|
39357
|
+
shopDomain: z155.string().min(1).optional().describe("Tienda, si tiene m\xE1s de una conectada.")
|
|
39019
39358
|
}).strict();
|
|
39020
|
-
var ImportableProductsInput =
|
|
39021
|
-
q:
|
|
39022
|
-
after:
|
|
39023
|
-
shopDomain:
|
|
39359
|
+
var ImportableProductsInput = z155.object({
|
|
39360
|
+
q: z155.string().trim().max(120).optional().describe("B\xFAsqueda por t\xEDtulo. Sin esto un cat\xE1logo grande es inrecorrible."),
|
|
39361
|
+
after: z155.string().max(512).optional().describe("Cursor de la p\xE1gina anterior, tal como vino en la respuesta."),
|
|
39362
|
+
shopDomain: z155.string().min(1).optional()
|
|
39024
39363
|
}).strict();
|
|
39025
|
-
var ImportProductControlsInput =
|
|
39026
|
-
shopifyProductIds:
|
|
39364
|
+
var ImportProductControlsInput = z155.object({
|
|
39365
|
+
shopifyProductIds: z155.array(z155.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."),
|
|
39027
39366
|
// Obligatorio, como en el endpoint: con dos tiendas conectadas, importar
|
|
39028
39367
|
// sin decir a cuál archivaría el producto bajo la tienda equivocada.
|
|
39029
|
-
shopDomain:
|
|
39368
|
+
shopDomain: z155.string().min(1)
|
|
39030
39369
|
}).strict();
|
|
39031
|
-
var SetProductCostingInput =
|
|
39370
|
+
var SetProductCostingInput = z155.object({
|
|
39032
39371
|
/* La identidad, de UNA forma: la del producto nuestro, o la pareja
|
|
39033
39372
|
tienda + id de Shopify. El esquema canónico también las admite juntas,
|
|
39034
39373
|
pero pedirlas por separado evita que el modelo emita una mezcla que
|
|
39035
39374
|
diga dos productos a la vez. */
|
|
39036
|
-
productControlId:
|
|
39037
|
-
manualSellingPrice:
|
|
39038
|
-
shopDomain:
|
|
39039
|
-
shopifyProductId:
|
|
39040
|
-
targetCountry:
|
|
39041
|
-
supplierCost:
|
|
39042
|
-
shippingCost:
|
|
39043
|
-
returnRate:
|
|
39044
|
-
cancellationRate:
|
|
39045
|
-
}).strict();
|
|
39046
|
-
var Count =
|
|
39047
|
-
var ReportProductCountsInput =
|
|
39048
|
-
productControlId:
|
|
39375
|
+
productControlId: z155.string().uuid().optional().describe("El id nuestro (lo devuelven get_product_control y create_manual_product). Obligatorio para productos manuales o importados sin tienda."),
|
|
39376
|
+
manualSellingPrice: z155.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."),
|
|
39377
|
+
shopDomain: z155.string().min(1).optional(),
|
|
39378
|
+
shopifyProductId: z155.string().min(1).max(64).optional(),
|
|
39379
|
+
targetCountry: z155.string().length(2).describe("Pa\xEDs de venta en ISO-2, que fija moneda y referencia de flete."),
|
|
39380
|
+
supplierCost: z155.number().finite().positive().describe("Lo que le cuesta el producto al proveedor. Nunca cero."),
|
|
39381
|
+
shippingCost: z155.number().finite().nonnegative(),
|
|
39382
|
+
returnRate: z155.number().finite().min(0).lt(1).describe("Proporci\xF3n 0\u20131, no porcentaje: 0.15 es quince por ciento."),
|
|
39383
|
+
cancellationRate: z155.number().finite().min(0).lt(1)
|
|
39384
|
+
}).strict();
|
|
39385
|
+
var Count = z155.number().int().min(0).max(1e5).nullable().optional();
|
|
39386
|
+
var ReportProductCountsInput = z155.object({
|
|
39387
|
+
productControlId: z155.string().uuid().describe("El identificador que devuelve get_product_control."),
|
|
39049
39388
|
date: DateOnly2,
|
|
39050
39389
|
orders: Count.describe("Pedidos que entraron ese d\xEDa."),
|
|
39051
39390
|
cancelledOrders: Count,
|
|
@@ -39055,64 +39394,64 @@ var ReportProductCountsInput = z154.object({
|
|
|
39055
39394
|
}).strict();
|
|
39056
39395
|
var MAX_ORDERS_FILE_BYTES = 8 * 1024 * 1024;
|
|
39057
39396
|
var SyncShopifyOrdersInput = SyncShopifyOrdersSchema;
|
|
39058
|
-
var ImportOrdersFileInput =
|
|
39397
|
+
var ImportOrdersFileInput = z155.object({
|
|
39059
39398
|
/** Ruta ABSOLUTA en la máquina de quien usa el MCP. */
|
|
39060
|
-
path:
|
|
39061
|
-
operationId:
|
|
39399
|
+
path: z155.string().min(1),
|
|
39400
|
+
operationId: z155.string().uuid().optional(),
|
|
39062
39401
|
/** Solo mirar: devuelve el resumen y no escribe nada. */
|
|
39063
|
-
dryRun:
|
|
39402
|
+
dryRun: z155.boolean().optional()
|
|
39064
39403
|
}).strict();
|
|
39065
|
-
var CreateOperationInput =
|
|
39066
|
-
countryCode:
|
|
39067
|
-
label:
|
|
39404
|
+
var CreateOperationInput = z155.object({
|
|
39405
|
+
countryCode: z155.string().length(2),
|
|
39406
|
+
label: z155.string().trim().min(1).max(60).optional()
|
|
39068
39407
|
}).strict();
|
|
39069
|
-
var MergeProductsInput =
|
|
39070
|
-
sourceId:
|
|
39071
|
-
targetId:
|
|
39408
|
+
var MergeProductsInput = z155.object({
|
|
39409
|
+
sourceId: z155.string().uuid(),
|
|
39410
|
+
targetId: z155.string().uuid()
|
|
39072
39411
|
}).strict();
|
|
39073
|
-
var SetMonthlyExpenseInput =
|
|
39074
|
-
month:
|
|
39412
|
+
var SetMonthlyExpenseInput = z155.object({
|
|
39413
|
+
month: z155.string().regex(/^\d{4}-(0[1-9]|1[0-2])$/).optional(),
|
|
39075
39414
|
label: SaveMonthlyExpenseSchema.shape.label,
|
|
39076
|
-
amount:
|
|
39077
|
-
operationId:
|
|
39415
|
+
amount: z155.number().finite().min(0),
|
|
39416
|
+
operationId: z155.string().uuid().optional()
|
|
39078
39417
|
}).strict();
|
|
39079
|
-
var SetAdSpendInput =
|
|
39080
|
-
month:
|
|
39081
|
-
operationId:
|
|
39418
|
+
var SetAdSpendInput = z155.object({
|
|
39419
|
+
month: z155.string().regex(/^\d{4}-(0[1-9]|1[0-2])$/).optional(),
|
|
39420
|
+
operationId: z155.string().uuid().optional(),
|
|
39082
39421
|
/** null RETIRA la corrección; cero afirma que no pautó. */
|
|
39083
|
-
amount:
|
|
39422
|
+
amount: z155.number().finite().min(0).nullable()
|
|
39084
39423
|
}).strict();
|
|
39085
|
-
var GetFinancesInput =
|
|
39086
|
-
month:
|
|
39087
|
-
operationId:
|
|
39424
|
+
var GetFinancesInput = z155.object({
|
|
39425
|
+
month: z155.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."),
|
|
39426
|
+
operationId: z155.string().uuid().optional().describe("El negocio, si tiene mas de uno. Con uno solo no hace falta decirlo.")
|
|
39088
39427
|
}).strict();
|
|
39089
39428
|
var GetDropiStatusInput = DropiCountryQuerySchema.extend({
|
|
39090
39429
|
countryCode: DropiCountrySchema.optional().describe("Pa\xEDs de la cuenta (ISO-2). Si se omite, el servidor responde por Colombia (CO)."),
|
|
39091
|
-
connectionId:
|
|
39430
|
+
connectionId: z155.string().uuid().optional().describe("Una cuenta concreta cuando el pa\xEDs tiene varias. Sale de la lista de conexiones; nunca se inventa.")
|
|
39092
39431
|
});
|
|
39093
39432
|
var SyncDropiOrdersInput = SyncDropiFinancialSchema;
|
|
39094
39433
|
var CreateManualProductInput = CreateManualProductControlSchema.innerType().omit({
|
|
39095
39434
|
requestId: true
|
|
39096
39435
|
});
|
|
39097
|
-
var ListUnmatchedProductsInput =
|
|
39098
|
-
operationId:
|
|
39436
|
+
var ListUnmatchedProductsInput = z155.object({
|
|
39437
|
+
operationId: z155.string().uuid().optional().describe("El negocio, si tiene mas de uno. Con uno solo no hace falta decirlo.")
|
|
39099
39438
|
}).strict();
|
|
39100
39439
|
var ResolveUnmatchedProductInput = ResolveUnmatchedProductSchema;
|
|
39101
|
-
var ListLinkableCampaignsInput =
|
|
39102
|
-
var LinkProductCampaignInput =
|
|
39103
|
-
provider:
|
|
39440
|
+
var ListLinkableCampaignsInput = z155.object({}).strict();
|
|
39441
|
+
var LinkProductCampaignInput = z155.object({
|
|
39442
|
+
provider: z155.enum([
|
|
39104
39443
|
"meta",
|
|
39105
39444
|
"tiktok"
|
|
39106
39445
|
]).describe("La red publicitaria de la campa\xF1a."),
|
|
39107
|
-
accountId:
|
|
39108
|
-
campaignId:
|
|
39109
|
-
productControlId:
|
|
39110
|
-
shopDomain:
|
|
39111
|
-
shopifyProductId:
|
|
39446
|
+
accountId: z155.string().min(1).max(64).regex(/^[A-Za-z0-9_-]+$/).describe("La cuenta publicitaria. Sale de list_linkable_campaigns; nunca se inventa."),
|
|
39447
|
+
campaignId: z155.string().min(1).max(64).regex(/^[A-Za-z0-9_-]+$/).describe("La campa\xF1a. Sale de list_linkable_campaigns; nunca se inventa."),
|
|
39448
|
+
productControlId: z155.string().uuid().optional().describe("El id del producto en su control financiero."),
|
|
39449
|
+
shopDomain: z155.string().min(1).optional().describe("La tienda del producto; con shopifyProductId es la otra forma de decir cu\xE1l es."),
|
|
39450
|
+
shopifyProductId: z155.string().min(1).optional().describe("El id del producto en su tienda. Va junto a shopDomain.")
|
|
39112
39451
|
}).strict();
|
|
39113
39452
|
var DeleteMonthlyExpenseInput = DeleteMonthlyExpenseSchema;
|
|
39114
|
-
var RemoveProductControlInput =
|
|
39115
|
-
productControlId:
|
|
39453
|
+
var RemoveProductControlInput = z155.object({
|
|
39454
|
+
productControlId: z155.string().uuid().describe("El id del producto en su control. Sale del listado; nunca se inventa.")
|
|
39116
39455
|
}).strict();
|
|
39117
39456
|
function manualProductRequestId(input) {
|
|
39118
39457
|
const digest3 = createHash4("sha256").update(JSON.stringify(input, [
|
|
@@ -39453,28 +39792,28 @@ function registerProductControlTools(server, api, { transport = "http" } = {}) {
|
|
|
39453
39792
|
__name(registerProductControlTools, "registerProductControlTools");
|
|
39454
39793
|
|
|
39455
39794
|
// src/tools/market-study-estimate.ts
|
|
39456
|
-
import { z as
|
|
39795
|
+
import { z as z156 } from "zod";
|
|
39457
39796
|
var estimateMarketStudyGuidance = getProductMakerCapabilityModelGuidance("estimate_market_study_cost");
|
|
39458
|
-
var ProductTaskOutputEnum =
|
|
39797
|
+
var ProductTaskOutputEnum = z156.enum([
|
|
39459
39798
|
"landing",
|
|
39460
39799
|
"video",
|
|
39461
39800
|
"image"
|
|
39462
39801
|
]);
|
|
39463
|
-
var EstimateMarketStudyInput =
|
|
39464
|
-
kind:
|
|
39802
|
+
var EstimateMarketStudyInput = z156.object({
|
|
39803
|
+
kind: z156.enum([
|
|
39465
39804
|
"video",
|
|
39466
39805
|
"product_task"
|
|
39467
39806
|
]).describe('"video" para generate_video_creative; "product_task" para create_product_task.'),
|
|
39468
|
-
videoAngleCount:
|
|
39469
|
-
durationSeconds:
|
|
39807
|
+
videoAngleCount: z156.number().int().min(1).max(3).describe("Los mismos 1, 2 o 3 videos que vas a pedir al crear."),
|
|
39808
|
+
durationSeconds: z156.number().int().min(4).max(30).default(15).describe("La misma duraci\xF3n que enviar\xE1s al crear."),
|
|
39470
39809
|
videoStructure: VideoStructureEnum.default("single_take"),
|
|
39471
39810
|
videoQuality: VideoQualityEnum.default("economy"),
|
|
39472
39811
|
videoResolution: VideoResolutionEnum.default("480p"),
|
|
39473
|
-
productImageCount:
|
|
39812
|
+
productImageCount: z156.number().int().min(1).max(5).optional().describe("Cantidad de fotos del producto que adjuntar\xE1s (1-5)."),
|
|
39474
39813
|
outputs: ProductTaskOutputEnum.array().min(1).max(3).optional().describe('Solo para kind="product_task": los mismos outputs de create_product_task. Default: todos.'),
|
|
39475
|
-
generateAiMusic:
|
|
39814
|
+
generateAiMusic: z156.boolean().optional(),
|
|
39476
39815
|
generateAiEdit: AiEditRequestSchema.optional(),
|
|
39477
|
-
replicateWinnerWhenFound:
|
|
39816
|
+
replicateWinnerWhenFound: z156.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.")
|
|
39478
39817
|
}).strict();
|
|
39479
39818
|
function buildMarketStudyEstimateRequests(input) {
|
|
39480
39819
|
const quotesVideo = input.kind === "video" || (input.outputs ?? [
|
|
@@ -39596,11 +39935,11 @@ __name(registerMarketStudyEstimateTools, "registerMarketStudyEstimateTools");
|
|
|
39596
39935
|
// src/guidance/productmaker-guidance.ts
|
|
39597
39936
|
var START_DROPSHIPPING_PROMPT_NAME = "start_dropshipping";
|
|
39598
39937
|
var PRODUCTMAKER_SERVER_INSTRUCTIONS = [
|
|
39599
|
-
"
|
|
39938
|
+
"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.",
|
|
39600
39939
|
"For requests that do not need a new media upload, understand the user's intent before choosing a ProductMaker tool.",
|
|
39601
39940
|
"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.",
|
|
39602
|
-
"
|
|
39603
|
-
"
|
|
39941
|
+
"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.",
|
|
39942
|
+
"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.",
|
|
39604
39943
|
"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.",
|
|
39605
39944
|
"Use the guided journey only for an explicit end-to-end launch.",
|
|
39606
39945
|
"In guided mode, create a project only after a product is chosen, then call guide_dropshipping_launch after every mutation and follow its nextAction.",
|
|
@@ -39672,7 +40011,7 @@ function createServer(opts) {
|
|
|
39672
40011
|
signal: opts.abortSignal
|
|
39673
40012
|
});
|
|
39674
40013
|
registerProductMakerPrompts(server);
|
|
39675
|
-
registerMediaUploadAppResource(server, api.publicUrl("/v1/uploaded-assets/sessions/resolve"));
|
|
40014
|
+
registerMediaUploadAppResource(server, api.publicUrl("/v1/uploaded-assets/sessions/resolve"), opts.mediaUploadStorageOrigins);
|
|
39676
40015
|
registerTaskResultsAppResource(server, proxyBaseUrl);
|
|
39677
40016
|
registerProductCostingTools(server);
|
|
39678
40017
|
registerProductResearchTools(server, api);
|
|
@@ -39686,6 +40025,7 @@ function createServer(opts) {
|
|
|
39686
40025
|
registerServiceImageTools(server, api);
|
|
39687
40026
|
registerAnimatedVideoTools(server, api);
|
|
39688
40027
|
registerCreativeTools(server, api, {
|
|
40028
|
+
proxyBaseUrl,
|
|
39689
40029
|
transport
|
|
39690
40030
|
});
|
|
39691
40031
|
registerCreativeVariantTools(server, api);
|
|
@@ -39706,30 +40046,6 @@ function createServer(opts) {
|
|
|
39706
40046
|
}
|
|
39707
40047
|
__name(createServer, "createServer");
|
|
39708
40048
|
|
|
39709
|
-
// src/validate-api-url.ts
|
|
39710
|
-
function assertSafePublicBaseUrl(envName, url2) {
|
|
39711
|
-
let u;
|
|
39712
|
-
try {
|
|
39713
|
-
u = new URL(url2);
|
|
39714
|
-
} catch {
|
|
39715
|
-
throw new Error(`${envName} is not a valid URL: ${url2}`);
|
|
39716
|
-
}
|
|
39717
|
-
if (u.protocol !== "https:") {
|
|
39718
|
-
throw new Error(`${envName} must use https (got ${u.protocol})`);
|
|
39719
|
-
}
|
|
39720
|
-
if (u.username || u.password || u.search || u.hash) {
|
|
39721
|
-
throw new Error(`${envName} must not contain credentials, query parameters, or fragments`);
|
|
39722
|
-
}
|
|
39723
|
-
if (isPrivateHost(u.hostname)) {
|
|
39724
|
-
throw new Error(`${envName} must not point to a private/loopback host: ${u.hostname}`);
|
|
39725
|
-
}
|
|
39726
|
-
}
|
|
39727
|
-
__name(assertSafePublicBaseUrl, "assertSafePublicBaseUrl");
|
|
39728
|
-
function assertSafeApiBaseUrl(url2) {
|
|
39729
|
-
assertSafePublicBaseUrl("PM_API_URL", url2);
|
|
39730
|
-
}
|
|
39731
|
-
__name(assertSafeApiBaseUrl, "assertSafeApiBaseUrl");
|
|
39732
|
-
|
|
39733
40049
|
// src/stdio.ts
|
|
39734
40050
|
async function main() {
|
|
39735
40051
|
const apiKey = process.env.PM_API_KEY;
|
|
@@ -39747,7 +40063,8 @@ async function main() {
|
|
|
39747
40063
|
const server = createServer({
|
|
39748
40064
|
credential: new StaticApiKeyCredential(apiKey),
|
|
39749
40065
|
apiBaseUrl,
|
|
39750
|
-
transport: "stdio"
|
|
40066
|
+
transport: "stdio",
|
|
40067
|
+
mediaUploadStorageOrigins: parseMediaUploadStorageOrigins(process.env.PM_MEDIA_UPLOAD_STORAGE_ORIGINS)
|
|
39751
40068
|
});
|
|
39752
40069
|
await server.connect(new StdioServerTransport());
|
|
39753
40070
|
}
|