@productmaker/mcp 11.5.3 → 11.5.5
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 +5 -0
- package/dist/http.js +505 -301
- package/dist/stdio.js +256 -52
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -303,6 +303,11 @@ The `base64` field was removed. MCP JSON-RPC transports truncate large tool argu
|
|
|
303
303
|
|
|
304
304
|
## Version history
|
|
305
305
|
|
|
306
|
+
### 11.5.4 — Limpieza protegida al recrear una landing de servicio
|
|
307
|
+
|
|
308
|
+
- La web usa el parámetro opcional `failedCreationOnly` en `DELETE /v1/service-pages/:id` para reemplazar una creación fallida sin borrar una landing generada ni una generación en curso.
|
|
309
|
+
- No cambia ninguna herramienta ni esquema público MCP. La versión patch cumple la sincronización obligatoria del repositorio para cambios publicables; el paquete npm se publicará tras el merge.
|
|
310
|
+
|
|
306
311
|
### 11.5.1 — Copy junto a cada pieza de servicios
|
|
307
312
|
|
|
308
313
|
Los resultados de videos e imágenes de servicios incluyen texto para publicar
|
package/dist/http.js
CHANGED
|
@@ -8403,7 +8403,9 @@ var CampaignCopyEstimationOptions = z40.object({
|
|
|
8403
8403
|
/** Number of image creatives that can each use one vision analysis (0-10). */
|
|
8404
8404
|
nImages: z40.number().int().min(0).max(10).default(0)
|
|
8405
8405
|
});
|
|
8406
|
-
var BrandExtractEstimationOptions = z40.object({
|
|
8406
|
+
var BrandExtractEstimationOptions = z40.object({
|
|
8407
|
+
sourceUrl: z40.string().url().optional()
|
|
8408
|
+
}).strict();
|
|
8407
8409
|
var PersonaSuggestEstimationOptions = z40.object({}).strict();
|
|
8408
8410
|
var ChatParseEstimationOptions = z40.object({}).strict();
|
|
8409
8411
|
var CodeLandingEditEstimationOptions = z40.object({}).strict();
|
|
@@ -9858,10 +9860,476 @@ var TikTokCreateCampaignResponseSchema = z52.object({
|
|
|
9858
9860
|
var GEMINI_OMNI_FLAT_OUR_COST_CENTS = 84;
|
|
9859
9861
|
var GEMINI_OMNI_FLAT_USER_PRICE_CENTS = 168;
|
|
9860
9862
|
|
|
9863
|
+
// ../../packages/types/src/public-host.ts
|
|
9864
|
+
function normalizeHostname(value) {
|
|
9865
|
+
const normalized = value.trim().toLowerCase().replace(/\.+$/, "");
|
|
9866
|
+
return normalized.startsWith("[") && normalized.endsWith("]") ? normalized.slice(1, -1) : normalized;
|
|
9867
|
+
}
|
|
9868
|
+
__name(normalizeHostname, "normalizeHostname");
|
|
9869
|
+
function parseIpv4(address) {
|
|
9870
|
+
const octets = address.split(".");
|
|
9871
|
+
if (octets.length !== 4 || !octets.every((octet) => /^(0|[1-9]\d{0,2})$/.test(octet))) {
|
|
9872
|
+
return null;
|
|
9873
|
+
}
|
|
9874
|
+
const parsedOctets = octets.map(Number);
|
|
9875
|
+
if (parsedOctets.some((octet) => octet > 255)) return null;
|
|
9876
|
+
const [first, second, third, fourth] = parsedOctets;
|
|
9877
|
+
if (first === void 0 || second === void 0 || third === void 0 || fourth === void 0) {
|
|
9878
|
+
return null;
|
|
9879
|
+
}
|
|
9880
|
+
return [
|
|
9881
|
+
first,
|
|
9882
|
+
second,
|
|
9883
|
+
third,
|
|
9884
|
+
fourth
|
|
9885
|
+
];
|
|
9886
|
+
}
|
|
9887
|
+
__name(parseIpv4, "parseIpv4");
|
|
9888
|
+
function isPublicIpv4Octets([first, second, third]) {
|
|
9889
|
+
if (first === 0 || first === 10 || first === 127 || first >= 224) return false;
|
|
9890
|
+
if (first === 100 && second >= 64 && second <= 127) return false;
|
|
9891
|
+
if (first === 169 && second === 254) return false;
|
|
9892
|
+
if (first === 172 && second >= 16 && second <= 31) return false;
|
|
9893
|
+
if (first === 192 && second === 0 && (third === 0 || third === 2)) return false;
|
|
9894
|
+
if (first === 192 && second === 88 && third === 99) return false;
|
|
9895
|
+
if (first === 192 && second === 168) return false;
|
|
9896
|
+
if (first === 198 && (second === 18 || second === 19)) return false;
|
|
9897
|
+
if (first === 198 && second === 51 && third === 100) return false;
|
|
9898
|
+
if (first === 203 && second === 0 && third === 113) return false;
|
|
9899
|
+
return true;
|
|
9900
|
+
}
|
|
9901
|
+
__name(isPublicIpv4Octets, "isPublicIpv4Octets");
|
|
9902
|
+
function parseIpv6Hextets(address) {
|
|
9903
|
+
let normalized = address;
|
|
9904
|
+
const lastColon = normalized.lastIndexOf(":");
|
|
9905
|
+
const finalPart = normalized.slice(lastColon + 1);
|
|
9906
|
+
if (finalPart.includes(".")) {
|
|
9907
|
+
const octets = parseIpv4(finalPart);
|
|
9908
|
+
if (octets === null) return null;
|
|
9909
|
+
const [first, second, third, fourth] = octets;
|
|
9910
|
+
normalized = `${normalized.slice(0, lastColon + 1)}${(first << 8 | second).toString(16)}:${(third << 8 | fourth).toString(16)}`;
|
|
9911
|
+
}
|
|
9912
|
+
const parts = normalized.split("::");
|
|
9913
|
+
if (parts.length > 2) return null;
|
|
9914
|
+
const left = parts[0] ? parts[0].split(":") : [];
|
|
9915
|
+
const right = parts[1] ? parts[1].split(":") : [];
|
|
9916
|
+
const explicit = [];
|
|
9917
|
+
for (const part of [
|
|
9918
|
+
...left,
|
|
9919
|
+
...right
|
|
9920
|
+
]) {
|
|
9921
|
+
if (!/^[\da-f]{1,4}$/i.test(part)) return null;
|
|
9922
|
+
explicit.push(Number.parseInt(part, 16));
|
|
9923
|
+
}
|
|
9924
|
+
if (parts.length === 1) return explicit.length === 8 ? explicit : null;
|
|
9925
|
+
if (explicit.length >= 8) return null;
|
|
9926
|
+
return [
|
|
9927
|
+
...explicit.slice(0, left.length),
|
|
9928
|
+
...Array(8 - explicit.length).fill(0),
|
|
9929
|
+
...explicit.slice(left.length)
|
|
9930
|
+
];
|
|
9931
|
+
}
|
|
9932
|
+
__name(parseIpv6Hextets, "parseIpv6Hextets");
|
|
9933
|
+
function matchesIpv6Prefix(hextets, prefix, prefixLength) {
|
|
9934
|
+
const completeHextets = Math.floor(prefixLength / 16);
|
|
9935
|
+
for (let index = 0; index < completeHextets; index += 1) {
|
|
9936
|
+
if (hextets[index] !== prefix[index]) return false;
|
|
9937
|
+
}
|
|
9938
|
+
const remainingBits = prefixLength % 16;
|
|
9939
|
+
if (remainingBits === 0) return true;
|
|
9940
|
+
const mask = 65535 << 16 - remainingBits & 65535;
|
|
9941
|
+
return ((hextets[completeHextets] ?? 0) & mask) === ((prefix[completeHextets] ?? 0) & mask);
|
|
9942
|
+
}
|
|
9943
|
+
__name(matchesIpv6Prefix, "matchesIpv6Prefix");
|
|
9944
|
+
function embeddedIpv4(hextets) {
|
|
9945
|
+
const seventh = hextets[6] ?? 0;
|
|
9946
|
+
const eighth = hextets[7] ?? 0;
|
|
9947
|
+
return [
|
|
9948
|
+
seventh >> 8,
|
|
9949
|
+
seventh & 255,
|
|
9950
|
+
eighth >> 8,
|
|
9951
|
+
eighth & 255
|
|
9952
|
+
];
|
|
9953
|
+
}
|
|
9954
|
+
__name(embeddedIpv4, "embeddedIpv4");
|
|
9955
|
+
function isPublicIpv6(address) {
|
|
9956
|
+
const hextets = parseIpv6Hextets(address);
|
|
9957
|
+
if (hextets === null) return false;
|
|
9958
|
+
const isIpv4Compatible = matchesIpv6Prefix(hextets, [
|
|
9959
|
+
0,
|
|
9960
|
+
0,
|
|
9961
|
+
0,
|
|
9962
|
+
0,
|
|
9963
|
+
0,
|
|
9964
|
+
0
|
|
9965
|
+
], 96);
|
|
9966
|
+
const isIpv4Mapped = matchesIpv6Prefix(hextets, [
|
|
9967
|
+
0,
|
|
9968
|
+
0,
|
|
9969
|
+
0,
|
|
9970
|
+
0,
|
|
9971
|
+
0,
|
|
9972
|
+
65535
|
|
9973
|
+
], 96);
|
|
9974
|
+
if (isIpv4Compatible || isIpv4Mapped) {
|
|
9975
|
+
return isPublicIpv4Octets(embeddedIpv4(hextets));
|
|
9976
|
+
}
|
|
9977
|
+
const first = hextets[0] ?? 0;
|
|
9978
|
+
if (first === 8194) {
|
|
9979
|
+
const second = hextets[1] ?? 0;
|
|
9980
|
+
const third = hextets[2] ?? 0;
|
|
9981
|
+
return isPublicIpv4Octets([
|
|
9982
|
+
second >> 8,
|
|
9983
|
+
second & 255,
|
|
9984
|
+
third >> 8,
|
|
9985
|
+
third & 255
|
|
9986
|
+
]);
|
|
9987
|
+
}
|
|
9988
|
+
const nonPublicPrefixes = [
|
|
9989
|
+
[
|
|
9990
|
+
[
|
|
9991
|
+
100,
|
|
9992
|
+
65435,
|
|
9993
|
+
1
|
|
9994
|
+
],
|
|
9995
|
+
48
|
|
9996
|
+
],
|
|
9997
|
+
[
|
|
9998
|
+
[
|
|
9999
|
+
256,
|
|
10000
|
+
0,
|
|
10001
|
+
0,
|
|
10002
|
+
0
|
|
10003
|
+
],
|
|
10004
|
+
64
|
|
10005
|
+
],
|
|
10006
|
+
[
|
|
10007
|
+
[
|
|
10008
|
+
8193,
|
|
10009
|
+
0
|
|
10010
|
+
],
|
|
10011
|
+
32
|
|
10012
|
+
],
|
|
10013
|
+
[
|
|
10014
|
+
[
|
|
10015
|
+
8193,
|
|
10016
|
+
2,
|
|
10017
|
+
0
|
|
10018
|
+
],
|
|
10019
|
+
48
|
|
10020
|
+
],
|
|
10021
|
+
[
|
|
10022
|
+
[
|
|
10023
|
+
8193,
|
|
10024
|
+
16
|
|
10025
|
+
],
|
|
10026
|
+
28
|
|
10027
|
+
],
|
|
10028
|
+
[
|
|
10029
|
+
[
|
|
10030
|
+
8193,
|
|
10031
|
+
32
|
|
10032
|
+
],
|
|
10033
|
+
28
|
|
10034
|
+
],
|
|
10035
|
+
[
|
|
10036
|
+
[
|
|
10037
|
+
8193,
|
|
10038
|
+
3512
|
|
10039
|
+
],
|
|
10040
|
+
32
|
|
10041
|
+
],
|
|
10042
|
+
[
|
|
10043
|
+
[
|
|
10044
|
+
16383
|
|
10045
|
+
],
|
|
10046
|
+
20
|
|
10047
|
+
],
|
|
10048
|
+
[
|
|
10049
|
+
[
|
|
10050
|
+
24320
|
|
10051
|
+
],
|
|
10052
|
+
16
|
|
10053
|
+
],
|
|
10054
|
+
[
|
|
10055
|
+
[
|
|
10056
|
+
64512
|
|
10057
|
+
],
|
|
10058
|
+
7
|
|
10059
|
+
],
|
|
10060
|
+
[
|
|
10061
|
+
[
|
|
10062
|
+
65152
|
|
10063
|
+
],
|
|
10064
|
+
10
|
|
10065
|
+
],
|
|
10066
|
+
[
|
|
10067
|
+
[
|
|
10068
|
+
65216
|
|
10069
|
+
],
|
|
10070
|
+
10
|
|
10071
|
+
],
|
|
10072
|
+
[
|
|
10073
|
+
[
|
|
10074
|
+
65280
|
|
10075
|
+
],
|
|
10076
|
+
8
|
|
10077
|
+
]
|
|
10078
|
+
];
|
|
10079
|
+
return matchesIpv6Prefix(hextets, [
|
|
10080
|
+
8192
|
|
10081
|
+
], 3) && !nonPublicPrefixes.some(([prefix, length]) => matchesIpv6Prefix(hextets, prefix, length));
|
|
10082
|
+
}
|
|
10083
|
+
__name(isPublicIpv6, "isPublicIpv6");
|
|
10084
|
+
function isPublicHost(value) {
|
|
10085
|
+
const hostname = normalizeHostname(value);
|
|
10086
|
+
if (hostname.length === 0 || hostname === "localhost" || hostname.endsWith(".localhost")) {
|
|
10087
|
+
return false;
|
|
10088
|
+
}
|
|
10089
|
+
const ipv4 = parseIpv4(hostname);
|
|
10090
|
+
if (ipv4 !== null) return isPublicIpv4Octets(ipv4);
|
|
10091
|
+
if (hostname.includes(":")) return isPublicIpv6(hostname);
|
|
10092
|
+
if (/^[\d.]+$/.test(hostname)) return false;
|
|
10093
|
+
return true;
|
|
10094
|
+
}
|
|
10095
|
+
__name(isPublicHost, "isPublicHost");
|
|
10096
|
+
function parsePublicHttpsOrigin(value) {
|
|
10097
|
+
let parsed;
|
|
10098
|
+
try {
|
|
10099
|
+
parsed = new URL(value);
|
|
10100
|
+
} catch {
|
|
10101
|
+
return null;
|
|
10102
|
+
}
|
|
10103
|
+
if (parsed.protocol !== "https:" || parsed.username || parsed.password || parsed.pathname !== "/" || parsed.search || parsed.hash || parsed.port !== "" || !isPublicHost(parsed.hostname)) {
|
|
10104
|
+
return null;
|
|
10105
|
+
}
|
|
10106
|
+
return parsed;
|
|
10107
|
+
}
|
|
10108
|
+
__name(parsePublicHttpsOrigin, "parsePublicHttpsOrigin");
|
|
10109
|
+
|
|
10110
|
+
// ../../packages/types/src/social-profile-source.ts
|
|
10111
|
+
var INSTAGRAM_HOSTS = /* @__PURE__ */ new Set([
|
|
10112
|
+
"instagram.com",
|
|
10113
|
+
"www.instagram.com",
|
|
10114
|
+
"m.instagram.com",
|
|
10115
|
+
"instagr.am",
|
|
10116
|
+
"www.instagr.am"
|
|
10117
|
+
]);
|
|
10118
|
+
var FACEBOOK_HOSTS = /* @__PURE__ */ new Set([
|
|
10119
|
+
"facebook.com",
|
|
10120
|
+
"www.facebook.com",
|
|
10121
|
+
"m.facebook.com",
|
|
10122
|
+
"mbasic.facebook.com",
|
|
10123
|
+
"web.facebook.com",
|
|
10124
|
+
"fb.com",
|
|
10125
|
+
"www.fb.com"
|
|
10126
|
+
]);
|
|
10127
|
+
var INSTAGRAM_RESERVED = /* @__PURE__ */ new Set([
|
|
10128
|
+
"accounts",
|
|
10129
|
+
"about",
|
|
10130
|
+
"api",
|
|
10131
|
+
"challenge",
|
|
10132
|
+
"developer",
|
|
10133
|
+
"developers",
|
|
10134
|
+
"direct",
|
|
10135
|
+
"directory",
|
|
10136
|
+
"emails",
|
|
10137
|
+
"explore",
|
|
10138
|
+
"legal",
|
|
10139
|
+
"p",
|
|
10140
|
+
"privacy",
|
|
10141
|
+
"reel",
|
|
10142
|
+
"reels",
|
|
10143
|
+
"stories",
|
|
10144
|
+
"tv",
|
|
10145
|
+
"web"
|
|
10146
|
+
]);
|
|
10147
|
+
var FACEBOOK_RESERVED = /* @__PURE__ */ new Set([
|
|
10148
|
+
"about",
|
|
10149
|
+
"ads",
|
|
10150
|
+
"business",
|
|
10151
|
+
"checkpoint",
|
|
10152
|
+
"dialog",
|
|
10153
|
+
"events",
|
|
10154
|
+
"friends",
|
|
10155
|
+
"gaming",
|
|
10156
|
+
"groups",
|
|
10157
|
+
"help",
|
|
10158
|
+
"home.php",
|
|
10159
|
+
"login",
|
|
10160
|
+
"login.php",
|
|
10161
|
+
"logout.php",
|
|
10162
|
+
"marketplace",
|
|
10163
|
+
"messages",
|
|
10164
|
+
"pages",
|
|
10165
|
+
"people",
|
|
10166
|
+
"p",
|
|
10167
|
+
"photo",
|
|
10168
|
+
"photo.php",
|
|
10169
|
+
"photos",
|
|
10170
|
+
"plugins",
|
|
10171
|
+
"policies",
|
|
10172
|
+
"privacy",
|
|
10173
|
+
"profile.php",
|
|
10174
|
+
"recover",
|
|
10175
|
+
"reel",
|
|
10176
|
+
"reels",
|
|
10177
|
+
"search",
|
|
10178
|
+
"settings",
|
|
10179
|
+
"share",
|
|
10180
|
+
"sharer",
|
|
10181
|
+
"sharer.php",
|
|
10182
|
+
"story.php",
|
|
10183
|
+
"video.php",
|
|
10184
|
+
"videos",
|
|
10185
|
+
"watch"
|
|
10186
|
+
]);
|
|
10187
|
+
function socialProfileRequest(input) {
|
|
10188
|
+
let parsed;
|
|
10189
|
+
try {
|
|
10190
|
+
parsed = new URL(input.trim());
|
|
10191
|
+
} catch {
|
|
10192
|
+
return null;
|
|
10193
|
+
}
|
|
10194
|
+
if (![
|
|
10195
|
+
"http:",
|
|
10196
|
+
"https:"
|
|
10197
|
+
].includes(parsed.protocol) || parsed.username || parsed.password || parsed.port || parsed.pathname.includes("%")) return null;
|
|
10198
|
+
const host = parsed.hostname.toLowerCase().replace(/\.$/, "");
|
|
10199
|
+
const segments = parsed.pathname.split("/").filter(Boolean);
|
|
10200
|
+
const first = segments[0]?.toLowerCase();
|
|
10201
|
+
if (INSTAGRAM_HOSTS.has(host)) {
|
|
10202
|
+
if (segments.length !== 1 || !first || !/^[a-z0-9._]{1,30}$/.test(first) || INSTAGRAM_RESERVED.has(first)) return null;
|
|
10203
|
+
return {
|
|
10204
|
+
platform: "instagram",
|
|
10205
|
+
handle: first,
|
|
10206
|
+
url: `https://www.instagram.com/${first}/`
|
|
10207
|
+
};
|
|
10208
|
+
}
|
|
10209
|
+
if (!FACEBOOK_HOSTS.has(host) || !first) return null;
|
|
10210
|
+
const numericId = first === "profile.php" && segments.length === 1 ? parsed.searchParams.get("id") : first === "people" && segments.length === 3 ? segments[2] : first === "p" && segments.length === 2 ? segments[1]?.match(/-(\d+)$/)?.[1] : void 0;
|
|
10211
|
+
if (numericId && /^[1-9]\d{0,29}$/.test(numericId)) {
|
|
10212
|
+
return {
|
|
10213
|
+
platform: "facebook",
|
|
10214
|
+
handle: numericId,
|
|
10215
|
+
url: `https://www.facebook.com/profile.php?id=${numericId}`
|
|
10216
|
+
};
|
|
10217
|
+
}
|
|
10218
|
+
if (segments.length !== 1 || !/^[a-z0-9._-]{1,100}$/.test(first) || FACEBOOK_RESERVED.has(first)) return null;
|
|
10219
|
+
return {
|
|
10220
|
+
platform: "facebook",
|
|
10221
|
+
handle: first,
|
|
10222
|
+
url: `https://www.facebook.com/${first}/`
|
|
10223
|
+
};
|
|
10224
|
+
}
|
|
10225
|
+
__name(socialProfileRequest, "socialProfileRequest");
|
|
10226
|
+
|
|
10227
|
+
// ../../packages/types/src/web-address.ts
|
|
10228
|
+
function normalizeWebAddress(input) {
|
|
10229
|
+
const trimmed = input.trim();
|
|
10230
|
+
if (!trimmed || /\s/.test(trimmed)) return null;
|
|
10231
|
+
const hasScheme = /^[a-z][a-z0-9+.-]*:\/\//i.test(trimmed);
|
|
10232
|
+
const hasOpaqueScheme = !hasScheme && /^[a-z][a-z0-9+.-]*:(?!\d)/i.test(trimmed);
|
|
10233
|
+
if (hasOpaqueScheme) return null;
|
|
10234
|
+
const candidate = hasScheme ? trimmed : `https://${trimmed}`;
|
|
10235
|
+
let parsed;
|
|
10236
|
+
try {
|
|
10237
|
+
parsed = new URL(candidate);
|
|
10238
|
+
} catch {
|
|
10239
|
+
return null;
|
|
10240
|
+
}
|
|
10241
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return null;
|
|
10242
|
+
if (parsed.username || parsed.password) return null;
|
|
10243
|
+
if (!isPublicHostname(parsed.hostname)) return null;
|
|
10244
|
+
return candidate;
|
|
10245
|
+
}
|
|
10246
|
+
__name(normalizeWebAddress, "normalizeWebAddress");
|
|
10247
|
+
var LOGIN_REDIRECTS = [
|
|
10248
|
+
{
|
|
10249
|
+
hosts: /* @__PURE__ */ new Set([
|
|
10250
|
+
"instagram.com",
|
|
10251
|
+
"m.instagram.com"
|
|
10252
|
+
]),
|
|
10253
|
+
loginPath: /^\/accounts\/login\/?$/i,
|
|
10254
|
+
canonicalOrigin: "https://www.instagram.com"
|
|
10255
|
+
},
|
|
10256
|
+
{
|
|
10257
|
+
hosts: /* @__PURE__ */ new Set([
|
|
10258
|
+
"facebook.com",
|
|
10259
|
+
"m.facebook.com"
|
|
10260
|
+
]),
|
|
10261
|
+
loginPath: /^\/login(?:\.php)?\/?$/i,
|
|
10262
|
+
canonicalOrigin: "https://www.facebook.com"
|
|
10263
|
+
}
|
|
10264
|
+
];
|
|
10265
|
+
function unwrapLoginRedirectUrl(input) {
|
|
10266
|
+
let parsed;
|
|
10267
|
+
try {
|
|
10268
|
+
parsed = new URL(input.trim());
|
|
10269
|
+
} catch {
|
|
10270
|
+
return input;
|
|
10271
|
+
}
|
|
10272
|
+
const host = parsed.hostname.toLowerCase().replace(/^www\./, "");
|
|
10273
|
+
const rule = LOGIN_REDIRECTS.find((candidate) => candidate.hosts.has(host) && candidate.loginPath.test(parsed.pathname));
|
|
10274
|
+
const next = rule ? parsed.searchParams.get("next") : null;
|
|
10275
|
+
if (!rule || !next) return input;
|
|
10276
|
+
let target;
|
|
10277
|
+
try {
|
|
10278
|
+
target = new URL(next, rule.canonicalOrigin);
|
|
10279
|
+
} catch {
|
|
10280
|
+
return input;
|
|
10281
|
+
}
|
|
10282
|
+
const targetHost = target.hostname.toLowerCase().replace(/^www\./, "");
|
|
10283
|
+
if (!rule.hosts.has(targetHost) || ![
|
|
10284
|
+
"http:",
|
|
10285
|
+
"https:"
|
|
10286
|
+
].includes(target.protocol) || rule.loginPath.test(target.pathname) || target.pathname === "/") return input;
|
|
10287
|
+
return `${rule.canonicalOrigin}${target.pathname}${target.search}`;
|
|
10288
|
+
}
|
|
10289
|
+
__name(unwrapLoginRedirectUrl, "unwrapLoginRedirectUrl");
|
|
10290
|
+
var LABEL = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i;
|
|
10291
|
+
var TLD = /^(?:[a-z]{2,}|xn--[a-z0-9-]+)$/i;
|
|
10292
|
+
function isPublicHostname(hostname) {
|
|
10293
|
+
const host = hostname.replace(/\.$/, "").toLowerCase();
|
|
10294
|
+
if (!host || host.startsWith("[")) return false;
|
|
10295
|
+
if (host === "localhost" || host.endsWith(".localhost")) return false;
|
|
10296
|
+
const labels = host.split(".");
|
|
10297
|
+
if (labels.length < 2) return false;
|
|
10298
|
+
if (!labels.every((label) => label.length <= 63 && LABEL.test(label))) return false;
|
|
10299
|
+
const tld = labels[labels.length - 1] ?? "";
|
|
10300
|
+
return TLD.test(tld);
|
|
10301
|
+
}
|
|
10302
|
+
__name(isPublicHostname, "isPublicHostname");
|
|
10303
|
+
|
|
9861
10304
|
// ../../packages/types/src/pricing.ts
|
|
9862
10305
|
var GEMINI_TTS_CREDIT_PRICING_ACTION = "tts-generation:gemini-3-1-flash-tts-percredit";
|
|
9863
10306
|
var CREDITS_PER_USD_CENT = 10;
|
|
9864
10307
|
var CREDITS_PER_DOLLAR = 100 * CREDITS_PER_USD_CENT;
|
|
10308
|
+
var APIFY_SOCIAL_PROFILE_MAX_RUN_COST_USD = 0.1;
|
|
10309
|
+
var APIFY_SOCIAL_PROFILE_EXPECTED_RUN_COST_CENTS = 0.23;
|
|
10310
|
+
function apifySocialProfileUserPriceCents(costCents) {
|
|
10311
|
+
if (!Number.isFinite(costCents * 2) || costCents < 0) {
|
|
10312
|
+
throw new RangeError("APIFY_SOCIAL_INVALID_COST");
|
|
10313
|
+
}
|
|
10314
|
+
return Number((costCents * 2).toFixed(10));
|
|
10315
|
+
}
|
|
10316
|
+
__name(apifySocialProfileUserPriceCents, "apifySocialProfileUserPriceCents");
|
|
10317
|
+
var APIFY_INSTAGRAM_PROFILE_PRICE_RANGE_CENTS = Object.freeze({
|
|
10318
|
+
min: apifySocialProfileUserPriceCents(APIFY_SOCIAL_PROFILE_EXPECTED_RUN_COST_CENTS),
|
|
10319
|
+
max: apifySocialProfileUserPriceCents(APIFY_SOCIAL_PROFILE_MAX_RUN_COST_USD * 100)
|
|
10320
|
+
});
|
|
10321
|
+
function estimateInstagramProfileAcquisitionCostCents(sourceUrl) {
|
|
10322
|
+
if (socialProfileRequest(unwrapLoginRedirectUrl(sourceUrl ?? ""))?.platform !== "instagram") {
|
|
10323
|
+
return {
|
|
10324
|
+
min: 0,
|
|
10325
|
+
max: 0
|
|
10326
|
+
};
|
|
10327
|
+
}
|
|
10328
|
+
return {
|
|
10329
|
+
...APIFY_INSTAGRAM_PROFILE_PRICE_RANGE_CENTS
|
|
10330
|
+
};
|
|
10331
|
+
}
|
|
10332
|
+
__name(estimateInstagramProfileAcquisitionCostCents, "estimateInstagramProfileAcquisitionCostCents");
|
|
9865
10333
|
var AD_CREATIVE_MODELS = {
|
|
9866
10334
|
STANDARD: IMAGE_WRITE_TOKENS["1k"],
|
|
9867
10335
|
PREMIUM: IMAGE_WRITE_TOKENS["2k"]
|
|
@@ -14359,9 +14827,17 @@ function motionGraphicsPriceCents(durationSec, options = {}) {
|
|
|
14359
14827
|
}
|
|
14360
14828
|
__name(motionGraphicsPriceCents, "motionGraphicsPriceCents");
|
|
14361
14829
|
function motionGraphicsPriceRangeCents(durationSec, options = {}) {
|
|
14830
|
+
const pending = options.socialAcquisitionPriceCents;
|
|
14831
|
+
if (pending !== void 0 && (!Number.isFinite(pending) || pending < 0)) {
|
|
14832
|
+
throw new RangeError("APIFY_SOCIAL_INVALID_PRICE");
|
|
14833
|
+
}
|
|
14834
|
+
const acquisition = pending !== void 0 ? {
|
|
14835
|
+
min: pending,
|
|
14836
|
+
max: pending
|
|
14837
|
+
} : estimateInstagramProfileAcquisitionCostCents(options.socialSourceUrl);
|
|
14362
14838
|
return {
|
|
14363
|
-
minCents: roundClipCents(motionGraphicsBasePriceCents(durationSec)),
|
|
14364
|
-
maxCents: motionGraphicsPriceCents(durationSec, options)
|
|
14839
|
+
minCents: roundClipCents(motionGraphicsBasePriceCents(durationSec) + acquisition.min),
|
|
14840
|
+
maxCents: roundClipCents(motionGraphicsPriceCents(durationSec, options) + acquisition.max)
|
|
14365
14841
|
};
|
|
14366
14842
|
}
|
|
14367
14843
|
__name(motionGraphicsPriceRangeCents, "motionGraphicsPriceRangeCents");
|
|
@@ -15621,42 +16097,8 @@ var MotionGraphicsTranscriptResponseSchema = z84.discriminatedUnion("status", [
|
|
|
15621
16097
|
z84.object({
|
|
15622
16098
|
status: z84.literal("skipped"),
|
|
15623
16099
|
reason: MotionGraphicsTranscriptSkipReasonSchema
|
|
15624
|
-
}).strict()
|
|
15625
|
-
]);
|
|
15626
|
-
|
|
15627
|
-
// ../../packages/types/src/web-address.ts
|
|
15628
|
-
function normalizeWebAddress(input) {
|
|
15629
|
-
const trimmed = input.trim();
|
|
15630
|
-
if (!trimmed || /\s/.test(trimmed)) return null;
|
|
15631
|
-
const hasScheme = /^[a-z][a-z0-9+.-]*:\/\//i.test(trimmed);
|
|
15632
|
-
const hasOpaqueScheme = !hasScheme && /^[a-z][a-z0-9+.-]*:(?!\d)/i.test(trimmed);
|
|
15633
|
-
if (hasOpaqueScheme) return null;
|
|
15634
|
-
const candidate = hasScheme ? trimmed : `https://${trimmed}`;
|
|
15635
|
-
let parsed;
|
|
15636
|
-
try {
|
|
15637
|
-
parsed = new URL(candidate);
|
|
15638
|
-
} catch {
|
|
15639
|
-
return null;
|
|
15640
|
-
}
|
|
15641
|
-
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return null;
|
|
15642
|
-
if (parsed.username || parsed.password) return null;
|
|
15643
|
-
if (!isPublicHostname(parsed.hostname)) return null;
|
|
15644
|
-
return candidate;
|
|
15645
|
-
}
|
|
15646
|
-
__name(normalizeWebAddress, "normalizeWebAddress");
|
|
15647
|
-
var LABEL = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i;
|
|
15648
|
-
var TLD = /^(?:[a-z]{2,}|xn--[a-z0-9-]+)$/i;
|
|
15649
|
-
function isPublicHostname(hostname) {
|
|
15650
|
-
const host = hostname.replace(/\.$/, "").toLowerCase();
|
|
15651
|
-
if (!host || host.startsWith("[")) return false;
|
|
15652
|
-
if (host === "localhost" || host.endsWith(".localhost")) return false;
|
|
15653
|
-
const labels = host.split(".");
|
|
15654
|
-
if (labels.length < 2) return false;
|
|
15655
|
-
if (!labels.every((label) => label.length <= 63 && LABEL.test(label))) return false;
|
|
15656
|
-
const tld = labels[labels.length - 1] ?? "";
|
|
15657
|
-
return TLD.test(tld);
|
|
15658
|
-
}
|
|
15659
|
-
__name(isPublicHostname, "isPublicHostname");
|
|
16100
|
+
}).strict()
|
|
16101
|
+
]);
|
|
15660
16102
|
|
|
15661
16103
|
// ../../packages/types/src/internal/deep-freeze.ts
|
|
15662
16104
|
function deepFreeze(value) {
|
|
@@ -19778,253 +20220,6 @@ var MCP_ANGLE_NARRATIVE_STYLES = [
|
|
|
19778
20220
|
];
|
|
19779
20221
|
var McpAngleNarrativeStyleSchema = NarrativeStyle.extract(MCP_ANGLE_NARRATIVE_STYLES);
|
|
19780
20222
|
|
|
19781
|
-
// ../../packages/types/src/public-host.ts
|
|
19782
|
-
function normalizeHostname(value) {
|
|
19783
|
-
const normalized = value.trim().toLowerCase().replace(/\.+$/, "");
|
|
19784
|
-
return normalized.startsWith("[") && normalized.endsWith("]") ? normalized.slice(1, -1) : normalized;
|
|
19785
|
-
}
|
|
19786
|
-
__name(normalizeHostname, "normalizeHostname");
|
|
19787
|
-
function parseIpv4(address) {
|
|
19788
|
-
const octets = address.split(".");
|
|
19789
|
-
if (octets.length !== 4 || !octets.every((octet) => /^(0|[1-9]\d{0,2})$/.test(octet))) {
|
|
19790
|
-
return null;
|
|
19791
|
-
}
|
|
19792
|
-
const parsedOctets = octets.map(Number);
|
|
19793
|
-
if (parsedOctets.some((octet) => octet > 255)) return null;
|
|
19794
|
-
const [first, second, third, fourth] = parsedOctets;
|
|
19795
|
-
if (first === void 0 || second === void 0 || third === void 0 || fourth === void 0) {
|
|
19796
|
-
return null;
|
|
19797
|
-
}
|
|
19798
|
-
return [
|
|
19799
|
-
first,
|
|
19800
|
-
second,
|
|
19801
|
-
third,
|
|
19802
|
-
fourth
|
|
19803
|
-
];
|
|
19804
|
-
}
|
|
19805
|
-
__name(parseIpv4, "parseIpv4");
|
|
19806
|
-
function isPublicIpv4Octets([first, second, third]) {
|
|
19807
|
-
if (first === 0 || first === 10 || first === 127 || first >= 224) return false;
|
|
19808
|
-
if (first === 100 && second >= 64 && second <= 127) return false;
|
|
19809
|
-
if (first === 169 && second === 254) return false;
|
|
19810
|
-
if (first === 172 && second >= 16 && second <= 31) return false;
|
|
19811
|
-
if (first === 192 && second === 0 && (third === 0 || third === 2)) return false;
|
|
19812
|
-
if (first === 192 && second === 88 && third === 99) return false;
|
|
19813
|
-
if (first === 192 && second === 168) return false;
|
|
19814
|
-
if (first === 198 && (second === 18 || second === 19)) return false;
|
|
19815
|
-
if (first === 198 && second === 51 && third === 100) return false;
|
|
19816
|
-
if (first === 203 && second === 0 && third === 113) return false;
|
|
19817
|
-
return true;
|
|
19818
|
-
}
|
|
19819
|
-
__name(isPublicIpv4Octets, "isPublicIpv4Octets");
|
|
19820
|
-
function parseIpv6Hextets(address) {
|
|
19821
|
-
let normalized = address;
|
|
19822
|
-
const lastColon = normalized.lastIndexOf(":");
|
|
19823
|
-
const finalPart = normalized.slice(lastColon + 1);
|
|
19824
|
-
if (finalPart.includes(".")) {
|
|
19825
|
-
const octets = parseIpv4(finalPart);
|
|
19826
|
-
if (octets === null) return null;
|
|
19827
|
-
const [first, second, third, fourth] = octets;
|
|
19828
|
-
normalized = `${normalized.slice(0, lastColon + 1)}${(first << 8 | second).toString(16)}:${(third << 8 | fourth).toString(16)}`;
|
|
19829
|
-
}
|
|
19830
|
-
const parts = normalized.split("::");
|
|
19831
|
-
if (parts.length > 2) return null;
|
|
19832
|
-
const left = parts[0] ? parts[0].split(":") : [];
|
|
19833
|
-
const right = parts[1] ? parts[1].split(":") : [];
|
|
19834
|
-
const explicit = [];
|
|
19835
|
-
for (const part of [
|
|
19836
|
-
...left,
|
|
19837
|
-
...right
|
|
19838
|
-
]) {
|
|
19839
|
-
if (!/^[\da-f]{1,4}$/i.test(part)) return null;
|
|
19840
|
-
explicit.push(Number.parseInt(part, 16));
|
|
19841
|
-
}
|
|
19842
|
-
if (parts.length === 1) return explicit.length === 8 ? explicit : null;
|
|
19843
|
-
if (explicit.length >= 8) return null;
|
|
19844
|
-
return [
|
|
19845
|
-
...explicit.slice(0, left.length),
|
|
19846
|
-
...Array(8 - explicit.length).fill(0),
|
|
19847
|
-
...explicit.slice(left.length)
|
|
19848
|
-
];
|
|
19849
|
-
}
|
|
19850
|
-
__name(parseIpv6Hextets, "parseIpv6Hextets");
|
|
19851
|
-
function matchesIpv6Prefix(hextets, prefix, prefixLength) {
|
|
19852
|
-
const completeHextets = Math.floor(prefixLength / 16);
|
|
19853
|
-
for (let index = 0; index < completeHextets; index += 1) {
|
|
19854
|
-
if (hextets[index] !== prefix[index]) return false;
|
|
19855
|
-
}
|
|
19856
|
-
const remainingBits = prefixLength % 16;
|
|
19857
|
-
if (remainingBits === 0) return true;
|
|
19858
|
-
const mask = 65535 << 16 - remainingBits & 65535;
|
|
19859
|
-
return ((hextets[completeHextets] ?? 0) & mask) === ((prefix[completeHextets] ?? 0) & mask);
|
|
19860
|
-
}
|
|
19861
|
-
__name(matchesIpv6Prefix, "matchesIpv6Prefix");
|
|
19862
|
-
function embeddedIpv4(hextets) {
|
|
19863
|
-
const seventh = hextets[6] ?? 0;
|
|
19864
|
-
const eighth = hextets[7] ?? 0;
|
|
19865
|
-
return [
|
|
19866
|
-
seventh >> 8,
|
|
19867
|
-
seventh & 255,
|
|
19868
|
-
eighth >> 8,
|
|
19869
|
-
eighth & 255
|
|
19870
|
-
];
|
|
19871
|
-
}
|
|
19872
|
-
__name(embeddedIpv4, "embeddedIpv4");
|
|
19873
|
-
function isPublicIpv6(address) {
|
|
19874
|
-
const hextets = parseIpv6Hextets(address);
|
|
19875
|
-
if (hextets === null) return false;
|
|
19876
|
-
const isIpv4Compatible = matchesIpv6Prefix(hextets, [
|
|
19877
|
-
0,
|
|
19878
|
-
0,
|
|
19879
|
-
0,
|
|
19880
|
-
0,
|
|
19881
|
-
0,
|
|
19882
|
-
0
|
|
19883
|
-
], 96);
|
|
19884
|
-
const isIpv4Mapped = matchesIpv6Prefix(hextets, [
|
|
19885
|
-
0,
|
|
19886
|
-
0,
|
|
19887
|
-
0,
|
|
19888
|
-
0,
|
|
19889
|
-
0,
|
|
19890
|
-
65535
|
|
19891
|
-
], 96);
|
|
19892
|
-
if (isIpv4Compatible || isIpv4Mapped) {
|
|
19893
|
-
return isPublicIpv4Octets(embeddedIpv4(hextets));
|
|
19894
|
-
}
|
|
19895
|
-
const first = hextets[0] ?? 0;
|
|
19896
|
-
if (first === 8194) {
|
|
19897
|
-
const second = hextets[1] ?? 0;
|
|
19898
|
-
const third = hextets[2] ?? 0;
|
|
19899
|
-
return isPublicIpv4Octets([
|
|
19900
|
-
second >> 8,
|
|
19901
|
-
second & 255,
|
|
19902
|
-
third >> 8,
|
|
19903
|
-
third & 255
|
|
19904
|
-
]);
|
|
19905
|
-
}
|
|
19906
|
-
const nonPublicPrefixes = [
|
|
19907
|
-
[
|
|
19908
|
-
[
|
|
19909
|
-
100,
|
|
19910
|
-
65435,
|
|
19911
|
-
1
|
|
19912
|
-
],
|
|
19913
|
-
48
|
|
19914
|
-
],
|
|
19915
|
-
[
|
|
19916
|
-
[
|
|
19917
|
-
256,
|
|
19918
|
-
0,
|
|
19919
|
-
0,
|
|
19920
|
-
0
|
|
19921
|
-
],
|
|
19922
|
-
64
|
|
19923
|
-
],
|
|
19924
|
-
[
|
|
19925
|
-
[
|
|
19926
|
-
8193,
|
|
19927
|
-
0
|
|
19928
|
-
],
|
|
19929
|
-
32
|
|
19930
|
-
],
|
|
19931
|
-
[
|
|
19932
|
-
[
|
|
19933
|
-
8193,
|
|
19934
|
-
2,
|
|
19935
|
-
0
|
|
19936
|
-
],
|
|
19937
|
-
48
|
|
19938
|
-
],
|
|
19939
|
-
[
|
|
19940
|
-
[
|
|
19941
|
-
8193,
|
|
19942
|
-
16
|
|
19943
|
-
],
|
|
19944
|
-
28
|
|
19945
|
-
],
|
|
19946
|
-
[
|
|
19947
|
-
[
|
|
19948
|
-
8193,
|
|
19949
|
-
32
|
|
19950
|
-
],
|
|
19951
|
-
28
|
|
19952
|
-
],
|
|
19953
|
-
[
|
|
19954
|
-
[
|
|
19955
|
-
8193,
|
|
19956
|
-
3512
|
|
19957
|
-
],
|
|
19958
|
-
32
|
|
19959
|
-
],
|
|
19960
|
-
[
|
|
19961
|
-
[
|
|
19962
|
-
16383
|
|
19963
|
-
],
|
|
19964
|
-
20
|
|
19965
|
-
],
|
|
19966
|
-
[
|
|
19967
|
-
[
|
|
19968
|
-
24320
|
|
19969
|
-
],
|
|
19970
|
-
16
|
|
19971
|
-
],
|
|
19972
|
-
[
|
|
19973
|
-
[
|
|
19974
|
-
64512
|
|
19975
|
-
],
|
|
19976
|
-
7
|
|
19977
|
-
],
|
|
19978
|
-
[
|
|
19979
|
-
[
|
|
19980
|
-
65152
|
|
19981
|
-
],
|
|
19982
|
-
10
|
|
19983
|
-
],
|
|
19984
|
-
[
|
|
19985
|
-
[
|
|
19986
|
-
65216
|
|
19987
|
-
],
|
|
19988
|
-
10
|
|
19989
|
-
],
|
|
19990
|
-
[
|
|
19991
|
-
[
|
|
19992
|
-
65280
|
|
19993
|
-
],
|
|
19994
|
-
8
|
|
19995
|
-
]
|
|
19996
|
-
];
|
|
19997
|
-
return matchesIpv6Prefix(hextets, [
|
|
19998
|
-
8192
|
|
19999
|
-
], 3) && !nonPublicPrefixes.some(([prefix, length]) => matchesIpv6Prefix(hextets, prefix, length));
|
|
20000
|
-
}
|
|
20001
|
-
__name(isPublicIpv6, "isPublicIpv6");
|
|
20002
|
-
function isPublicHost(value) {
|
|
20003
|
-
const hostname = normalizeHostname(value);
|
|
20004
|
-
if (hostname.length === 0 || hostname === "localhost" || hostname.endsWith(".localhost")) {
|
|
20005
|
-
return false;
|
|
20006
|
-
}
|
|
20007
|
-
const ipv4 = parseIpv4(hostname);
|
|
20008
|
-
if (ipv4 !== null) return isPublicIpv4Octets(ipv4);
|
|
20009
|
-
if (hostname.includes(":")) return isPublicIpv6(hostname);
|
|
20010
|
-
if (/^[\d.]+$/.test(hostname)) return false;
|
|
20011
|
-
return true;
|
|
20012
|
-
}
|
|
20013
|
-
__name(isPublicHost, "isPublicHost");
|
|
20014
|
-
function parsePublicHttpsOrigin(value) {
|
|
20015
|
-
let parsed;
|
|
20016
|
-
try {
|
|
20017
|
-
parsed = new URL(value);
|
|
20018
|
-
} catch {
|
|
20019
|
-
return null;
|
|
20020
|
-
}
|
|
20021
|
-
if (parsed.protocol !== "https:" || parsed.username || parsed.password || parsed.pathname !== "/" || parsed.search || parsed.hash || parsed.port !== "" || !isPublicHost(parsed.hostname)) {
|
|
20022
|
-
return null;
|
|
20023
|
-
}
|
|
20024
|
-
return parsed;
|
|
20025
|
-
}
|
|
20026
|
-
__name(parsePublicHttpsOrigin, "parsePublicHttpsOrigin");
|
|
20027
|
-
|
|
20028
20223
|
// ../../packages/types/src/whatsapp.ts
|
|
20029
20224
|
var WHATSAPP_MAX_AUDIO_BYTES = 16 * 1024 * 1024;
|
|
20030
20225
|
var WHATSAPP_MAX_AUDIO_DURATION_SECONDS = 10 * 60;
|
|
@@ -21831,6 +22026,9 @@ var ServicePagePublicReadSchema = z101.object({
|
|
|
21831
22026
|
assetsById: z101.record(AssetIdSchema, ServicePagePublicAssetSchema)
|
|
21832
22027
|
}).strict();
|
|
21833
22028
|
|
|
22029
|
+
// ../../packages/types/src/service-page-creation-status.ts
|
|
22030
|
+
var SERVICE_PAGE_CREATION_HANDOFF_GRACE_MS = 2 * 6e4;
|
|
22031
|
+
|
|
21834
22032
|
// ../../packages/types/src/service-landing-analytics.ts
|
|
21835
22033
|
import { z as z102 } from "zod";
|
|
21836
22034
|
var DimensionsSchema = z102.object({
|
|
@@ -22231,6 +22429,8 @@ var WebsiteCaptureResponseSchema = z109.object({
|
|
|
22231
22429
|
status: WebsiteCaptureStatusSchema,
|
|
22232
22430
|
expiresAt: z109.string().datetime(),
|
|
22233
22431
|
previewUrl: z109.string().url().optional(),
|
|
22432
|
+
/** Final customer amount for accepted Instagram acquisition still pending payment. */
|
|
22433
|
+
socialAcquisitionPriceCents: z109.number().finite().nonnegative().optional(),
|
|
22234
22434
|
cause: WebsiteCaptureCauseSchema.optional()
|
|
22235
22435
|
});
|
|
22236
22436
|
var WebsiteCaptureJobSchema = WebsiteCaptureRequestSchema.extend({
|
|
@@ -24186,7 +24386,7 @@ var ApiClient = class {
|
|
|
24186
24386
|
};
|
|
24187
24387
|
|
|
24188
24388
|
// src/index.ts
|
|
24189
|
-
var MCP_VERSION = "11.5.
|
|
24389
|
+
var MCP_VERSION = "11.5.5";
|
|
24190
24390
|
|
|
24191
24391
|
// src/meta-upload-app.ts
|
|
24192
24392
|
import { readFileSync } from "fs";
|
|
@@ -26783,15 +26983,11 @@ function centsToCredits(cents) {
|
|
|
26783
26983
|
return Math.round(cents * CREDITS_PER_USD_CENT);
|
|
26784
26984
|
}
|
|
26785
26985
|
__name(centsToCredits, "centsToCredits");
|
|
26786
|
-
function
|
|
26787
|
-
return centsToCredits(motionGraphicsPriceCents(durationSec, {
|
|
26788
|
-
hookVariants
|
|
26789
|
-
}));
|
|
26790
|
-
}
|
|
26791
|
-
__name(animatedVideoCredits, "animatedVideoCredits");
|
|
26792
|
-
function animatedVideoCreditRange(durationSec, hookVariants = DEFAULT_MOTION_GRAPHICS_HOOK_VARIANTS) {
|
|
26986
|
+
function animatedVideoCreditRange(durationSec, hookVariants = DEFAULT_MOTION_GRAPHICS_HOOK_VARIANTS, acquisition = {}) {
|
|
26793
26987
|
const { minCents, maxCents } = motionGraphicsPriceRangeCents(durationSec, {
|
|
26794
|
-
hookVariants
|
|
26988
|
+
hookVariants,
|
|
26989
|
+
socialSourceUrl: acquisition.sourceUrl,
|
|
26990
|
+
socialAcquisitionPriceCents: acquisition.priceCents
|
|
26795
26991
|
});
|
|
26796
26992
|
return {
|
|
26797
26993
|
minCredits: centsToCredits(minCents),
|
|
@@ -26820,8 +27016,8 @@ function describeAnimatedVideoPrices() {
|
|
|
26820
27016
|
return `${ranges}. Cada versi\xF3n extra del gancho suma ${centsToCredits(MOTION_GRAPHICS_VARIANT_PRICE_CENTS)} cr\xE9ditos (US$${usd(MOTION_GRAPHICS_VARIANT_PRICE_CENTS)})`;
|
|
26821
27017
|
}
|
|
26822
27018
|
__name(describeAnimatedVideoPrices, "describeAnimatedVideoPrices");
|
|
26823
|
-
function animatedVideoMaxPriceText(durationSec, hookVariants = DEFAULT_MOTION_GRAPHICS_HOOK_VARIANTS) {
|
|
26824
|
-
return `m\xE1ximo ${
|
|
27019
|
+
function animatedVideoMaxPriceText(durationSec, hookVariants = DEFAULT_MOTION_GRAPHICS_HOOK_VARIANTS, acquisition = {}) {
|
|
27020
|
+
return `m\xE1ximo ${animatedVideoCreditRange(durationSec, hookVariants, acquisition).maxCredits} cr\xE9ditos (se devuelve lo que no use: clips IA, voz en off, clips hablados con la voz del narrador y versiones no entregadas)`;
|
|
26825
27021
|
}
|
|
26826
27022
|
__name(animatedVideoMaxPriceText, "animatedVideoMaxPriceText");
|
|
26827
27023
|
function hookVariantsLabel(count) {
|
|
@@ -27053,7 +27249,7 @@ function renderAnimatedVideoStatus(result) {
|
|
|
27053
27249
|
__name(renderAnimatedVideoStatus, "renderAnimatedVideoStatus");
|
|
27054
27250
|
function registerAnimatedVideoTools(server, api) {
|
|
27055
27251
|
server.registerTool("create_animated_video", {
|
|
27056
|
-
description: guidance.summary + ` Hace un video animado de un SERVICIO a partir del enlace de su p\xE1gina, con la identidad visual del sitio, m\xFAsica y efectos, y voz en off si el agente la usa. El agente que lo crea decide libremente la narrativa y la mezcla: solo gr\xE1ficos animados, clips de video generados con IA (incluidas personas hablando a c\xE1mara) o ambos intercalados; como m\xE1ximo un clip IA por cada ${MOTION_GRAPHICS_BROLL_CLIP_SECONDS} s de video. Desde la web el video tambi\xE9n puede usar los videos y fotos propios del usuario; aqu\xED se hace desde el enlace (gr\xE1ficos, clips IA y voz en off), sin archivos subidos. Si el video lleva voz en off, una persona que habla a c\xE1mara (en el gancho y, si acaso, en el cierre) habla con la misma voz del narrador: una sola voz en todo el video. Con hookVariants (2 o 3, solo si el usuario lo pide) entrega adem\xE1s versiones del mismo video con otra apertura, para probar cu\xE1l funciona mejor. Con music=false (solo si el usuario lo pide) sale sin m\xFAsica de fondo, al mismo precio. Primero llama action=prepare con landingUrl e idioma: prepara gratis una captura y NUNCA crea un video aunque responda ready. Sigue con action=status y websiteCaptureId hasta ready o fallo; cancel permite cancelar. Si est\xE1 bloqueada, pide otro enlace p\xFAblico sin iniciar sesi\xF3n; no solicites contrase\xF1as. Muestra la vista previa y confirma formato, duraci\xF3n, idioma y precio con el usuario. \xDAnicamente despu\xE9s llama action=generate con websiteCaptureId y confirmed=true. La generaci\xF3n reutiliza esa captura; no cobra por preparar ni por una captura pendiente/bloqueada. Repite el mismo websiteCaptureId y las mismas opciones para recuperar una respuesta perdida, incluso si la captura ya expir\xF3; la API devolver\xE1 el mismo taskId sin cobrar de nuevo. Conserva tambi\xE9n generationRequestId si lo enviaste. La misma captura, opciones e identidad representan esa generaci\xF3n; si el usuario pide y confirma otro video, usa un generationRequestId nuevo. Nunca cambies esa identidad autom\xE1ticamente por un fallo de transporte. Precio por duraci\xF3n, seg\xFAn lo que el video necesite: ${describeAnimatedVideoPrices()}. Al crearlo se cobra el m\xE1ximo de ese rango; cuando termina, lo que no us\xF3 (cada clip IA que no qued\xF3 en el video, la voz en off si no la us\xF3, cada clip hablado con la voz del narrador que no us\xF3 y cada versi\xF3n del gancho que no se entregue) se devuelve autom\xE1ticamente. Si falla, se devuelve todo. Tarda varios minutos, m\xE1s si hay otros videos en la cola. Generate devuelve un taskId: sigue el avance con get_task_status (etapa, posici\xF3n en la cola, % de render, lo que va haciendo y vista previa) hasta done, que trae el enlace del MP4 (y el de cada versi\xF3n del gancho), o error, que trae el motivo. Es el \xFAnico video de servicio disponible por ahora: no ofrezcas un video con presentador. Puede no estar disponible para todas las cuentas: si responde que la funci\xF3n no est\xE1 disponible, dilo y no ofrezcas alternativas pagadas.`,
|
|
27252
|
+
description: guidance.summary + ` Hace un video animado de un SERVICIO a partir del enlace de su p\xE1gina, con la identidad visual del sitio, m\xFAsica y efectos, y voz en off si el agente la usa. El agente que lo crea decide libremente la narrativa y la mezcla: solo gr\xE1ficos animados, clips de video generados con IA (incluidas personas hablando a c\xE1mara) o ambos intercalados; como m\xE1ximo un clip IA por cada ${MOTION_GRAPHICS_BROLL_CLIP_SECONDS} s de video. Desde la web el video tambi\xE9n puede usar los videos y fotos propios del usuario; aqu\xED se hace desde el enlace (gr\xE1ficos, clips IA y voz en off), sin archivos subidos. Si el video lleva voz en off, una persona que habla a c\xE1mara (en el gancho y, si acaso, en el cierre) habla con la misma voz del narrador: una sola voz en todo el video. Con hookVariants (2 o 3, solo si el usuario lo pide) entrega adem\xE1s versiones del mismo video con otra apertura, para probar cu\xE1l funciona mejor. Con music=false (solo si el usuario lo pide) sale sin m\xFAsica de fondo, al mismo precio. Primero llama action=prepare con landingUrl e idioma: prepara gratis una captura y NUNCA crea un video aunque responda ready. Sigue con action=status y websiteCaptureId hasta ready o fallo; cancel permite cancelar. Si est\xE1 bloqueada, pide otro enlace p\xFAblico sin iniciar sesi\xF3n; no solicites contrase\xF1as. Muestra la vista previa y confirma formato, duraci\xF3n, idioma y precio con el usuario. \xDAnicamente despu\xE9s llama action=generate con websiteCaptureId y confirmed=true. La generaci\xF3n reutiliza esa captura; no cobra por preparar ni por una captura pendiente/bloqueada. En Instagram, el precio incluye una sola adquisici\xF3n de la fuente a 2x su costo real, usando socialAcquisitionPriceCents de la captura ready; si ya se pag\xF3, ese importe es cero. Repite el mismo websiteCaptureId y las mismas opciones para recuperar una respuesta perdida, incluso si la captura ya expir\xF3; la API devolver\xE1 el mismo taskId sin cobrar de nuevo. Conserva tambi\xE9n generationRequestId si lo enviaste. La misma captura, opciones e identidad representan esa generaci\xF3n; si el usuario pide y confirma otro video, usa un generationRequestId nuevo. Nunca cambies esa identidad autom\xE1ticamente por un fallo de transporte. Precio por duraci\xF3n, seg\xFAn lo que el video necesite: ${describeAnimatedVideoPrices()}. Al crearlo se cobra el m\xE1ximo de ese rango; cuando termina, lo que no us\xF3 (cada clip IA que no qued\xF3 en el video, la voz en off si no la us\xF3, cada clip hablado con la voz del narrador que no us\xF3 y cada versi\xF3n del gancho que no se entregue) se devuelve autom\xE1ticamente. Si falla, se devuelve todo. Tarda varios minutos, m\xE1s si hay otros videos en la cola. Generate devuelve un taskId: sigue el avance con get_task_status (etapa, posici\xF3n en la cola, % de render, lo que va haciendo y vista previa) hasta done, que trae el enlace del MP4 (y el de cada versi\xF3n del gancho), o error, que trae el motivo. Es el \xFAnico video de servicio disponible por ahora: no ofrezcas un video con presentador. Puede no estar disponible para todas las cuentas: si responde que la funci\xF3n no est\xE1 disponible, dilo y no ofrezcas alternativas pagadas.`,
|
|
27057
27253
|
inputSchema: CreateAnimatedVideoInput.shape,
|
|
27058
27254
|
annotations: CREATES_PAID_OPEN_WORLD
|
|
27059
27255
|
}, async (input) => {
|
|
@@ -27101,8 +27297,14 @@ function registerAnimatedVideoTools(server, api) {
|
|
|
27101
27297
|
const motionGraphics = request.data;
|
|
27102
27298
|
const hookVariants = motionGraphics.hookVariants ?? DEFAULT_MOTION_GRAPHICS_HOOK_VARIANTS;
|
|
27103
27299
|
const clipCount = motionGraphicsMaxClips(motionGraphics.durationSec);
|
|
27104
|
-
const
|
|
27105
|
-
const
|
|
27300
|
+
const captureId = input.websiteCaptureId;
|
|
27301
|
+
const captureQuote = socialProfileRequest(landingUrl)?.platform === "instagram" ? await Promise.resolve().then(() => api.getJson(`/v1/motion-graphics/captures/${encodeURIComponent(captureId)}`)).then((value) => WebsiteCaptureResponseSchema.safeParse(value), () => null) : null;
|
|
27302
|
+
const acquisition = {
|
|
27303
|
+
sourceUrl: landingUrl,
|
|
27304
|
+
priceCents: captureQuote?.success ? captureQuote.data.socialAcquisitionPriceCents : void 0
|
|
27305
|
+
};
|
|
27306
|
+
const { minCredits, maxCredits } = animatedVideoCreditRange(motionGraphics.durationSec, hookVariants, acquisition);
|
|
27307
|
+
const priceText = animatedVideoMaxPriceText(motionGraphics.durationSec, hookVariants, acquisition);
|
|
27106
27308
|
const versionsText = hookVariants > 1 ? ` y ${hookVariantsLabel(hookVariants)}` : "";
|
|
27107
27309
|
const music = motionGraphicsMusic(motionGraphics);
|
|
27108
27310
|
const musicText = music ? "" : ", sin m\xFAsica de fondo";
|
|
@@ -28736,7 +28938,9 @@ function registerBrandTools(server, api, options = {}) {
|
|
|
28736
28938
|
}
|
|
28737
28939
|
const estimate = await api.postJson("/v1/billing/estimate", {
|
|
28738
28940
|
type: "brand-extract",
|
|
28739
|
-
options: {
|
|
28941
|
+
options: {
|
|
28942
|
+
sourceUrl
|
|
28943
|
+
}
|
|
28740
28944
|
});
|
|
28741
28945
|
const priceCredits = Math.ceil(estimate.estimatedMaxCostCredits);
|
|
28742
28946
|
if (input.confirmed !== true) {
|
package/dist/stdio.js
CHANGED
|
@@ -8425,7 +8425,9 @@ var CampaignCopyEstimationOptions = z40.object({
|
|
|
8425
8425
|
/** Number of image creatives that can each use one vision analysis (0-10). */
|
|
8426
8426
|
nImages: z40.number().int().min(0).max(10).default(0)
|
|
8427
8427
|
});
|
|
8428
|
-
var BrandExtractEstimationOptions = z40.object({
|
|
8428
|
+
var BrandExtractEstimationOptions = z40.object({
|
|
8429
|
+
sourceUrl: z40.string().url().optional()
|
|
8430
|
+
}).strict();
|
|
8429
8431
|
var PersonaSuggestEstimationOptions = z40.object({}).strict();
|
|
8430
8432
|
var ChatParseEstimationOptions = z40.object({}).strict();
|
|
8431
8433
|
var CodeLandingEditEstimationOptions = z40.object({}).strict();
|
|
@@ -9880,10 +9882,229 @@ var TikTokCreateCampaignResponseSchema = z52.object({
|
|
|
9880
9882
|
var GEMINI_OMNI_FLAT_OUR_COST_CENTS = 84;
|
|
9881
9883
|
var GEMINI_OMNI_FLAT_USER_PRICE_CENTS = 168;
|
|
9882
9884
|
|
|
9885
|
+
// ../../packages/types/src/social-profile-source.ts
|
|
9886
|
+
var INSTAGRAM_HOSTS = /* @__PURE__ */ new Set([
|
|
9887
|
+
"instagram.com",
|
|
9888
|
+
"www.instagram.com",
|
|
9889
|
+
"m.instagram.com",
|
|
9890
|
+
"instagr.am",
|
|
9891
|
+
"www.instagr.am"
|
|
9892
|
+
]);
|
|
9893
|
+
var FACEBOOK_HOSTS = /* @__PURE__ */ new Set([
|
|
9894
|
+
"facebook.com",
|
|
9895
|
+
"www.facebook.com",
|
|
9896
|
+
"m.facebook.com",
|
|
9897
|
+
"mbasic.facebook.com",
|
|
9898
|
+
"web.facebook.com",
|
|
9899
|
+
"fb.com",
|
|
9900
|
+
"www.fb.com"
|
|
9901
|
+
]);
|
|
9902
|
+
var INSTAGRAM_RESERVED = /* @__PURE__ */ new Set([
|
|
9903
|
+
"accounts",
|
|
9904
|
+
"about",
|
|
9905
|
+
"api",
|
|
9906
|
+
"challenge",
|
|
9907
|
+
"developer",
|
|
9908
|
+
"developers",
|
|
9909
|
+
"direct",
|
|
9910
|
+
"directory",
|
|
9911
|
+
"emails",
|
|
9912
|
+
"explore",
|
|
9913
|
+
"legal",
|
|
9914
|
+
"p",
|
|
9915
|
+
"privacy",
|
|
9916
|
+
"reel",
|
|
9917
|
+
"reels",
|
|
9918
|
+
"stories",
|
|
9919
|
+
"tv",
|
|
9920
|
+
"web"
|
|
9921
|
+
]);
|
|
9922
|
+
var FACEBOOK_RESERVED = /* @__PURE__ */ new Set([
|
|
9923
|
+
"about",
|
|
9924
|
+
"ads",
|
|
9925
|
+
"business",
|
|
9926
|
+
"checkpoint",
|
|
9927
|
+
"dialog",
|
|
9928
|
+
"events",
|
|
9929
|
+
"friends",
|
|
9930
|
+
"gaming",
|
|
9931
|
+
"groups",
|
|
9932
|
+
"help",
|
|
9933
|
+
"home.php",
|
|
9934
|
+
"login",
|
|
9935
|
+
"login.php",
|
|
9936
|
+
"logout.php",
|
|
9937
|
+
"marketplace",
|
|
9938
|
+
"messages",
|
|
9939
|
+
"pages",
|
|
9940
|
+
"people",
|
|
9941
|
+
"p",
|
|
9942
|
+
"photo",
|
|
9943
|
+
"photo.php",
|
|
9944
|
+
"photos",
|
|
9945
|
+
"plugins",
|
|
9946
|
+
"policies",
|
|
9947
|
+
"privacy",
|
|
9948
|
+
"profile.php",
|
|
9949
|
+
"recover",
|
|
9950
|
+
"reel",
|
|
9951
|
+
"reels",
|
|
9952
|
+
"search",
|
|
9953
|
+
"settings",
|
|
9954
|
+
"share",
|
|
9955
|
+
"sharer",
|
|
9956
|
+
"sharer.php",
|
|
9957
|
+
"story.php",
|
|
9958
|
+
"video.php",
|
|
9959
|
+
"videos",
|
|
9960
|
+
"watch"
|
|
9961
|
+
]);
|
|
9962
|
+
function socialProfileRequest(input) {
|
|
9963
|
+
let parsed;
|
|
9964
|
+
try {
|
|
9965
|
+
parsed = new URL(input.trim());
|
|
9966
|
+
} catch {
|
|
9967
|
+
return null;
|
|
9968
|
+
}
|
|
9969
|
+
if (![
|
|
9970
|
+
"http:",
|
|
9971
|
+
"https:"
|
|
9972
|
+
].includes(parsed.protocol) || parsed.username || parsed.password || parsed.port || parsed.pathname.includes("%")) return null;
|
|
9973
|
+
const host = parsed.hostname.toLowerCase().replace(/\.$/, "");
|
|
9974
|
+
const segments = parsed.pathname.split("/").filter(Boolean);
|
|
9975
|
+
const first = segments[0]?.toLowerCase();
|
|
9976
|
+
if (INSTAGRAM_HOSTS.has(host)) {
|
|
9977
|
+
if (segments.length !== 1 || !first || !/^[a-z0-9._]{1,30}$/.test(first) || INSTAGRAM_RESERVED.has(first)) return null;
|
|
9978
|
+
return {
|
|
9979
|
+
platform: "instagram",
|
|
9980
|
+
handle: first,
|
|
9981
|
+
url: `https://www.instagram.com/${first}/`
|
|
9982
|
+
};
|
|
9983
|
+
}
|
|
9984
|
+
if (!FACEBOOK_HOSTS.has(host) || !first) return null;
|
|
9985
|
+
const numericId = first === "profile.php" && segments.length === 1 ? parsed.searchParams.get("id") : first === "people" && segments.length === 3 ? segments[2] : first === "p" && segments.length === 2 ? segments[1]?.match(/-(\d+)$/)?.[1] : void 0;
|
|
9986
|
+
if (numericId && /^[1-9]\d{0,29}$/.test(numericId)) {
|
|
9987
|
+
return {
|
|
9988
|
+
platform: "facebook",
|
|
9989
|
+
handle: numericId,
|
|
9990
|
+
url: `https://www.facebook.com/profile.php?id=${numericId}`
|
|
9991
|
+
};
|
|
9992
|
+
}
|
|
9993
|
+
if (segments.length !== 1 || !/^[a-z0-9._-]{1,100}$/.test(first) || FACEBOOK_RESERVED.has(first)) return null;
|
|
9994
|
+
return {
|
|
9995
|
+
platform: "facebook",
|
|
9996
|
+
handle: first,
|
|
9997
|
+
url: `https://www.facebook.com/${first}/`
|
|
9998
|
+
};
|
|
9999
|
+
}
|
|
10000
|
+
__name(socialProfileRequest, "socialProfileRequest");
|
|
10001
|
+
|
|
10002
|
+
// ../../packages/types/src/web-address.ts
|
|
10003
|
+
function normalizeWebAddress(input) {
|
|
10004
|
+
const trimmed = input.trim();
|
|
10005
|
+
if (!trimmed || /\s/.test(trimmed)) return null;
|
|
10006
|
+
const hasScheme = /^[a-z][a-z0-9+.-]*:\/\//i.test(trimmed);
|
|
10007
|
+
const hasOpaqueScheme = !hasScheme && /^[a-z][a-z0-9+.-]*:(?!\d)/i.test(trimmed);
|
|
10008
|
+
if (hasOpaqueScheme) return null;
|
|
10009
|
+
const candidate = hasScheme ? trimmed : `https://${trimmed}`;
|
|
10010
|
+
let parsed;
|
|
10011
|
+
try {
|
|
10012
|
+
parsed = new URL(candidate);
|
|
10013
|
+
} catch {
|
|
10014
|
+
return null;
|
|
10015
|
+
}
|
|
10016
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return null;
|
|
10017
|
+
if (parsed.username || parsed.password) return null;
|
|
10018
|
+
if (!isPublicHostname(parsed.hostname)) return null;
|
|
10019
|
+
return candidate;
|
|
10020
|
+
}
|
|
10021
|
+
__name(normalizeWebAddress, "normalizeWebAddress");
|
|
10022
|
+
var LOGIN_REDIRECTS = [
|
|
10023
|
+
{
|
|
10024
|
+
hosts: /* @__PURE__ */ new Set([
|
|
10025
|
+
"instagram.com",
|
|
10026
|
+
"m.instagram.com"
|
|
10027
|
+
]),
|
|
10028
|
+
loginPath: /^\/accounts\/login\/?$/i,
|
|
10029
|
+
canonicalOrigin: "https://www.instagram.com"
|
|
10030
|
+
},
|
|
10031
|
+
{
|
|
10032
|
+
hosts: /* @__PURE__ */ new Set([
|
|
10033
|
+
"facebook.com",
|
|
10034
|
+
"m.facebook.com"
|
|
10035
|
+
]),
|
|
10036
|
+
loginPath: /^\/login(?:\.php)?\/?$/i,
|
|
10037
|
+
canonicalOrigin: "https://www.facebook.com"
|
|
10038
|
+
}
|
|
10039
|
+
];
|
|
10040
|
+
function unwrapLoginRedirectUrl(input) {
|
|
10041
|
+
let parsed;
|
|
10042
|
+
try {
|
|
10043
|
+
parsed = new URL(input.trim());
|
|
10044
|
+
} catch {
|
|
10045
|
+
return input;
|
|
10046
|
+
}
|
|
10047
|
+
const host = parsed.hostname.toLowerCase().replace(/^www\./, "");
|
|
10048
|
+
const rule = LOGIN_REDIRECTS.find((candidate) => candidate.hosts.has(host) && candidate.loginPath.test(parsed.pathname));
|
|
10049
|
+
const next = rule ? parsed.searchParams.get("next") : null;
|
|
10050
|
+
if (!rule || !next) return input;
|
|
10051
|
+
let target;
|
|
10052
|
+
try {
|
|
10053
|
+
target = new URL(next, rule.canonicalOrigin);
|
|
10054
|
+
} catch {
|
|
10055
|
+
return input;
|
|
10056
|
+
}
|
|
10057
|
+
const targetHost = target.hostname.toLowerCase().replace(/^www\./, "");
|
|
10058
|
+
if (!rule.hosts.has(targetHost) || ![
|
|
10059
|
+
"http:",
|
|
10060
|
+
"https:"
|
|
10061
|
+
].includes(target.protocol) || rule.loginPath.test(target.pathname) || target.pathname === "/") return input;
|
|
10062
|
+
return `${rule.canonicalOrigin}${target.pathname}${target.search}`;
|
|
10063
|
+
}
|
|
10064
|
+
__name(unwrapLoginRedirectUrl, "unwrapLoginRedirectUrl");
|
|
10065
|
+
var LABEL = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i;
|
|
10066
|
+
var TLD = /^(?:[a-z]{2,}|xn--[a-z0-9-]+)$/i;
|
|
10067
|
+
function isPublicHostname(hostname) {
|
|
10068
|
+
const host = hostname.replace(/\.$/, "").toLowerCase();
|
|
10069
|
+
if (!host || host.startsWith("[")) return false;
|
|
10070
|
+
if (host === "localhost" || host.endsWith(".localhost")) return false;
|
|
10071
|
+
const labels = host.split(".");
|
|
10072
|
+
if (labels.length < 2) return false;
|
|
10073
|
+
if (!labels.every((label) => label.length <= 63 && LABEL.test(label))) return false;
|
|
10074
|
+
const tld = labels[labels.length - 1] ?? "";
|
|
10075
|
+
return TLD.test(tld);
|
|
10076
|
+
}
|
|
10077
|
+
__name(isPublicHostname, "isPublicHostname");
|
|
10078
|
+
|
|
9883
10079
|
// ../../packages/types/src/pricing.ts
|
|
9884
10080
|
var GEMINI_TTS_CREDIT_PRICING_ACTION = "tts-generation:gemini-3-1-flash-tts-percredit";
|
|
9885
10081
|
var CREDITS_PER_USD_CENT = 10;
|
|
9886
10082
|
var CREDITS_PER_DOLLAR = 100 * CREDITS_PER_USD_CENT;
|
|
10083
|
+
var APIFY_SOCIAL_PROFILE_MAX_RUN_COST_USD = 0.1;
|
|
10084
|
+
var APIFY_SOCIAL_PROFILE_EXPECTED_RUN_COST_CENTS = 0.23;
|
|
10085
|
+
function apifySocialProfileUserPriceCents(costCents) {
|
|
10086
|
+
if (!Number.isFinite(costCents * 2) || costCents < 0) {
|
|
10087
|
+
throw new RangeError("APIFY_SOCIAL_INVALID_COST");
|
|
10088
|
+
}
|
|
10089
|
+
return Number((costCents * 2).toFixed(10));
|
|
10090
|
+
}
|
|
10091
|
+
__name(apifySocialProfileUserPriceCents, "apifySocialProfileUserPriceCents");
|
|
10092
|
+
var APIFY_INSTAGRAM_PROFILE_PRICE_RANGE_CENTS = Object.freeze({
|
|
10093
|
+
min: apifySocialProfileUserPriceCents(APIFY_SOCIAL_PROFILE_EXPECTED_RUN_COST_CENTS),
|
|
10094
|
+
max: apifySocialProfileUserPriceCents(APIFY_SOCIAL_PROFILE_MAX_RUN_COST_USD * 100)
|
|
10095
|
+
});
|
|
10096
|
+
function estimateInstagramProfileAcquisitionCostCents(sourceUrl) {
|
|
10097
|
+
if (socialProfileRequest(unwrapLoginRedirectUrl(sourceUrl ?? ""))?.platform !== "instagram") {
|
|
10098
|
+
return {
|
|
10099
|
+
min: 0,
|
|
10100
|
+
max: 0
|
|
10101
|
+
};
|
|
10102
|
+
}
|
|
10103
|
+
return {
|
|
10104
|
+
...APIFY_INSTAGRAM_PROFILE_PRICE_RANGE_CENTS
|
|
10105
|
+
};
|
|
10106
|
+
}
|
|
10107
|
+
__name(estimateInstagramProfileAcquisitionCostCents, "estimateInstagramProfileAcquisitionCostCents");
|
|
9887
10108
|
var AD_CREATIVE_MODELS = {
|
|
9888
10109
|
STANDARD: IMAGE_WRITE_TOKENS["1k"],
|
|
9889
10110
|
PREMIUM: IMAGE_WRITE_TOKENS["2k"]
|
|
@@ -14381,9 +14602,17 @@ function motionGraphicsPriceCents(durationSec, options = {}) {
|
|
|
14381
14602
|
}
|
|
14382
14603
|
__name(motionGraphicsPriceCents, "motionGraphicsPriceCents");
|
|
14383
14604
|
function motionGraphicsPriceRangeCents(durationSec, options = {}) {
|
|
14605
|
+
const pending = options.socialAcquisitionPriceCents;
|
|
14606
|
+
if (pending !== void 0 && (!Number.isFinite(pending) || pending < 0)) {
|
|
14607
|
+
throw new RangeError("APIFY_SOCIAL_INVALID_PRICE");
|
|
14608
|
+
}
|
|
14609
|
+
const acquisition = pending !== void 0 ? {
|
|
14610
|
+
min: pending,
|
|
14611
|
+
max: pending
|
|
14612
|
+
} : estimateInstagramProfileAcquisitionCostCents(options.socialSourceUrl);
|
|
14384
14613
|
return {
|
|
14385
|
-
minCents: roundClipCents(motionGraphicsBasePriceCents(durationSec)),
|
|
14386
|
-
maxCents: motionGraphicsPriceCents(durationSec, options)
|
|
14614
|
+
minCents: roundClipCents(motionGraphicsBasePriceCents(durationSec) + acquisition.min),
|
|
14615
|
+
maxCents: roundClipCents(motionGraphicsPriceCents(durationSec, options) + acquisition.max)
|
|
14387
14616
|
};
|
|
14388
14617
|
}
|
|
14389
14618
|
__name(motionGraphicsPriceRangeCents, "motionGraphicsPriceRangeCents");
|
|
@@ -15646,40 +15875,6 @@ var MotionGraphicsTranscriptResponseSchema = z84.discriminatedUnion("status", [
|
|
|
15646
15875
|
}).strict()
|
|
15647
15876
|
]);
|
|
15648
15877
|
|
|
15649
|
-
// ../../packages/types/src/web-address.ts
|
|
15650
|
-
function normalizeWebAddress(input) {
|
|
15651
|
-
const trimmed = input.trim();
|
|
15652
|
-
if (!trimmed || /\s/.test(trimmed)) return null;
|
|
15653
|
-
const hasScheme = /^[a-z][a-z0-9+.-]*:\/\//i.test(trimmed);
|
|
15654
|
-
const hasOpaqueScheme = !hasScheme && /^[a-z][a-z0-9+.-]*:(?!\d)/i.test(trimmed);
|
|
15655
|
-
if (hasOpaqueScheme) return null;
|
|
15656
|
-
const candidate = hasScheme ? trimmed : `https://${trimmed}`;
|
|
15657
|
-
let parsed;
|
|
15658
|
-
try {
|
|
15659
|
-
parsed = new URL(candidate);
|
|
15660
|
-
} catch {
|
|
15661
|
-
return null;
|
|
15662
|
-
}
|
|
15663
|
-
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return null;
|
|
15664
|
-
if (parsed.username || parsed.password) return null;
|
|
15665
|
-
if (!isPublicHostname(parsed.hostname)) return null;
|
|
15666
|
-
return candidate;
|
|
15667
|
-
}
|
|
15668
|
-
__name(normalizeWebAddress, "normalizeWebAddress");
|
|
15669
|
-
var LABEL = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i;
|
|
15670
|
-
var TLD = /^(?:[a-z]{2,}|xn--[a-z0-9-]+)$/i;
|
|
15671
|
-
function isPublicHostname(hostname) {
|
|
15672
|
-
const host = hostname.replace(/\.$/, "").toLowerCase();
|
|
15673
|
-
if (!host || host.startsWith("[")) return false;
|
|
15674
|
-
if (host === "localhost" || host.endsWith(".localhost")) return false;
|
|
15675
|
-
const labels = host.split(".");
|
|
15676
|
-
if (labels.length < 2) return false;
|
|
15677
|
-
if (!labels.every((label) => label.length <= 63 && LABEL.test(label))) return false;
|
|
15678
|
-
const tld = labels[labels.length - 1] ?? "";
|
|
15679
|
-
return TLD.test(tld);
|
|
15680
|
-
}
|
|
15681
|
-
__name(isPublicHostname, "isPublicHostname");
|
|
15682
|
-
|
|
15683
15878
|
// ../../packages/types/src/internal/deep-freeze.ts
|
|
15684
15879
|
function deepFreeze(value) {
|
|
15685
15880
|
if (value !== null && typeof value === "object") {
|
|
@@ -21599,6 +21794,9 @@ var ServicePagePublicReadSchema = z101.object({
|
|
|
21599
21794
|
assetsById: z101.record(AssetIdSchema, ServicePagePublicAssetSchema)
|
|
21600
21795
|
}).strict();
|
|
21601
21796
|
|
|
21797
|
+
// ../../packages/types/src/service-page-creation-status.ts
|
|
21798
|
+
var SERVICE_PAGE_CREATION_HANDOFF_GRACE_MS = 2 * 6e4;
|
|
21799
|
+
|
|
21602
21800
|
// ../../packages/types/src/service-landing-analytics.ts
|
|
21603
21801
|
import { z as z102 } from "zod";
|
|
21604
21802
|
var DimensionsSchema = z102.object({
|
|
@@ -21999,6 +22197,8 @@ var WebsiteCaptureResponseSchema = z109.object({
|
|
|
21999
22197
|
status: WebsiteCaptureStatusSchema,
|
|
22000
22198
|
expiresAt: z109.string().datetime(),
|
|
22001
22199
|
previewUrl: z109.string().url().optional(),
|
|
22200
|
+
/** Final customer amount for accepted Instagram acquisition still pending payment. */
|
|
22201
|
+
socialAcquisitionPriceCents: z109.number().finite().nonnegative().optional(),
|
|
22002
22202
|
cause: WebsiteCaptureCauseSchema.optional()
|
|
22003
22203
|
});
|
|
22004
22204
|
var WebsiteCaptureJobSchema = WebsiteCaptureRequestSchema.extend({
|
|
@@ -22515,7 +22715,7 @@ var ApiClient = class {
|
|
|
22515
22715
|
};
|
|
22516
22716
|
|
|
22517
22717
|
// src/index.ts
|
|
22518
|
-
var MCP_VERSION = "11.5.
|
|
22718
|
+
var MCP_VERSION = "11.5.5";
|
|
22519
22719
|
|
|
22520
22720
|
// src/meta-upload-app.ts
|
|
22521
22721
|
import { readFileSync } from "fs";
|
|
@@ -25165,15 +25365,11 @@ function centsToCredits(cents) {
|
|
|
25165
25365
|
return Math.round(cents * CREDITS_PER_USD_CENT);
|
|
25166
25366
|
}
|
|
25167
25367
|
__name(centsToCredits, "centsToCredits");
|
|
25168
|
-
function
|
|
25169
|
-
return centsToCredits(motionGraphicsPriceCents(durationSec, {
|
|
25170
|
-
hookVariants
|
|
25171
|
-
}));
|
|
25172
|
-
}
|
|
25173
|
-
__name(animatedVideoCredits, "animatedVideoCredits");
|
|
25174
|
-
function animatedVideoCreditRange(durationSec, hookVariants = DEFAULT_MOTION_GRAPHICS_HOOK_VARIANTS) {
|
|
25368
|
+
function animatedVideoCreditRange(durationSec, hookVariants = DEFAULT_MOTION_GRAPHICS_HOOK_VARIANTS, acquisition = {}) {
|
|
25175
25369
|
const { minCents, maxCents } = motionGraphicsPriceRangeCents(durationSec, {
|
|
25176
|
-
hookVariants
|
|
25370
|
+
hookVariants,
|
|
25371
|
+
socialSourceUrl: acquisition.sourceUrl,
|
|
25372
|
+
socialAcquisitionPriceCents: acquisition.priceCents
|
|
25177
25373
|
});
|
|
25178
25374
|
return {
|
|
25179
25375
|
minCredits: centsToCredits(minCents),
|
|
@@ -25202,8 +25398,8 @@ function describeAnimatedVideoPrices() {
|
|
|
25202
25398
|
return `${ranges}. Cada versi\xF3n extra del gancho suma ${centsToCredits(MOTION_GRAPHICS_VARIANT_PRICE_CENTS)} cr\xE9ditos (US$${usd(MOTION_GRAPHICS_VARIANT_PRICE_CENTS)})`;
|
|
25203
25399
|
}
|
|
25204
25400
|
__name(describeAnimatedVideoPrices, "describeAnimatedVideoPrices");
|
|
25205
|
-
function animatedVideoMaxPriceText(durationSec, hookVariants = DEFAULT_MOTION_GRAPHICS_HOOK_VARIANTS) {
|
|
25206
|
-
return `m\xE1ximo ${
|
|
25401
|
+
function animatedVideoMaxPriceText(durationSec, hookVariants = DEFAULT_MOTION_GRAPHICS_HOOK_VARIANTS, acquisition = {}) {
|
|
25402
|
+
return `m\xE1ximo ${animatedVideoCreditRange(durationSec, hookVariants, acquisition).maxCredits} cr\xE9ditos (se devuelve lo que no use: clips IA, voz en off, clips hablados con la voz del narrador y versiones no entregadas)`;
|
|
25207
25403
|
}
|
|
25208
25404
|
__name(animatedVideoMaxPriceText, "animatedVideoMaxPriceText");
|
|
25209
25405
|
function hookVariantsLabel(count) {
|
|
@@ -25435,7 +25631,7 @@ function renderAnimatedVideoStatus(result) {
|
|
|
25435
25631
|
__name(renderAnimatedVideoStatus, "renderAnimatedVideoStatus");
|
|
25436
25632
|
function registerAnimatedVideoTools(server, api) {
|
|
25437
25633
|
server.registerTool("create_animated_video", {
|
|
25438
|
-
description: guidance.summary + ` Hace un video animado de un SERVICIO a partir del enlace de su p\xE1gina, con la identidad visual del sitio, m\xFAsica y efectos, y voz en off si el agente la usa. El agente que lo crea decide libremente la narrativa y la mezcla: solo gr\xE1ficos animados, clips de video generados con IA (incluidas personas hablando a c\xE1mara) o ambos intercalados; como m\xE1ximo un clip IA por cada ${MOTION_GRAPHICS_BROLL_CLIP_SECONDS} s de video. Desde la web el video tambi\xE9n puede usar los videos y fotos propios del usuario; aqu\xED se hace desde el enlace (gr\xE1ficos, clips IA y voz en off), sin archivos subidos. Si el video lleva voz en off, una persona que habla a c\xE1mara (en el gancho y, si acaso, en el cierre) habla con la misma voz del narrador: una sola voz en todo el video. Con hookVariants (2 o 3, solo si el usuario lo pide) entrega adem\xE1s versiones del mismo video con otra apertura, para probar cu\xE1l funciona mejor. Con music=false (solo si el usuario lo pide) sale sin m\xFAsica de fondo, al mismo precio. Primero llama action=prepare con landingUrl e idioma: prepara gratis una captura y NUNCA crea un video aunque responda ready. Sigue con action=status y websiteCaptureId hasta ready o fallo; cancel permite cancelar. Si est\xE1 bloqueada, pide otro enlace p\xFAblico sin iniciar sesi\xF3n; no solicites contrase\xF1as. Muestra la vista previa y confirma formato, duraci\xF3n, idioma y precio con el usuario. \xDAnicamente despu\xE9s llama action=generate con websiteCaptureId y confirmed=true. La generaci\xF3n reutiliza esa captura; no cobra por preparar ni por una captura pendiente/bloqueada. Repite el mismo websiteCaptureId y las mismas opciones para recuperar una respuesta perdida, incluso si la captura ya expir\xF3; la API devolver\xE1 el mismo taskId sin cobrar de nuevo. Conserva tambi\xE9n generationRequestId si lo enviaste. La misma captura, opciones e identidad representan esa generaci\xF3n; si el usuario pide y confirma otro video, usa un generationRequestId nuevo. Nunca cambies esa identidad autom\xE1ticamente por un fallo de transporte. Precio por duraci\xF3n, seg\xFAn lo que el video necesite: ${describeAnimatedVideoPrices()}. Al crearlo se cobra el m\xE1ximo de ese rango; cuando termina, lo que no us\xF3 (cada clip IA que no qued\xF3 en el video, la voz en off si no la us\xF3, cada clip hablado con la voz del narrador que no us\xF3 y cada versi\xF3n del gancho que no se entregue) se devuelve autom\xE1ticamente. Si falla, se devuelve todo. Tarda varios minutos, m\xE1s si hay otros videos en la cola. Generate devuelve un taskId: sigue el avance con get_task_status (etapa, posici\xF3n en la cola, % de render, lo que va haciendo y vista previa) hasta done, que trae el enlace del MP4 (y el de cada versi\xF3n del gancho), o error, que trae el motivo. Es el \xFAnico video de servicio disponible por ahora: no ofrezcas un video con presentador. Puede no estar disponible para todas las cuentas: si responde que la funci\xF3n no est\xE1 disponible, dilo y no ofrezcas alternativas pagadas.`,
|
|
25634
|
+
description: guidance.summary + ` Hace un video animado de un SERVICIO a partir del enlace de su p\xE1gina, con la identidad visual del sitio, m\xFAsica y efectos, y voz en off si el agente la usa. El agente que lo crea decide libremente la narrativa y la mezcla: solo gr\xE1ficos animados, clips de video generados con IA (incluidas personas hablando a c\xE1mara) o ambos intercalados; como m\xE1ximo un clip IA por cada ${MOTION_GRAPHICS_BROLL_CLIP_SECONDS} s de video. Desde la web el video tambi\xE9n puede usar los videos y fotos propios del usuario; aqu\xED se hace desde el enlace (gr\xE1ficos, clips IA y voz en off), sin archivos subidos. Si el video lleva voz en off, una persona que habla a c\xE1mara (en el gancho y, si acaso, en el cierre) habla con la misma voz del narrador: una sola voz en todo el video. Con hookVariants (2 o 3, solo si el usuario lo pide) entrega adem\xE1s versiones del mismo video con otra apertura, para probar cu\xE1l funciona mejor. Con music=false (solo si el usuario lo pide) sale sin m\xFAsica de fondo, al mismo precio. Primero llama action=prepare con landingUrl e idioma: prepara gratis una captura y NUNCA crea un video aunque responda ready. Sigue con action=status y websiteCaptureId hasta ready o fallo; cancel permite cancelar. Si est\xE1 bloqueada, pide otro enlace p\xFAblico sin iniciar sesi\xF3n; no solicites contrase\xF1as. Muestra la vista previa y confirma formato, duraci\xF3n, idioma y precio con el usuario. \xDAnicamente despu\xE9s llama action=generate con websiteCaptureId y confirmed=true. La generaci\xF3n reutiliza esa captura; no cobra por preparar ni por una captura pendiente/bloqueada. En Instagram, el precio incluye una sola adquisici\xF3n de la fuente a 2x su costo real, usando socialAcquisitionPriceCents de la captura ready; si ya se pag\xF3, ese importe es cero. Repite el mismo websiteCaptureId y las mismas opciones para recuperar una respuesta perdida, incluso si la captura ya expir\xF3; la API devolver\xE1 el mismo taskId sin cobrar de nuevo. Conserva tambi\xE9n generationRequestId si lo enviaste. La misma captura, opciones e identidad representan esa generaci\xF3n; si el usuario pide y confirma otro video, usa un generationRequestId nuevo. Nunca cambies esa identidad autom\xE1ticamente por un fallo de transporte. Precio por duraci\xF3n, seg\xFAn lo que el video necesite: ${describeAnimatedVideoPrices()}. Al crearlo se cobra el m\xE1ximo de ese rango; cuando termina, lo que no us\xF3 (cada clip IA que no qued\xF3 en el video, la voz en off si no la us\xF3, cada clip hablado con la voz del narrador que no us\xF3 y cada versi\xF3n del gancho que no se entregue) se devuelve autom\xE1ticamente. Si falla, se devuelve todo. Tarda varios minutos, m\xE1s si hay otros videos en la cola. Generate devuelve un taskId: sigue el avance con get_task_status (etapa, posici\xF3n en la cola, % de render, lo que va haciendo y vista previa) hasta done, que trae el enlace del MP4 (y el de cada versi\xF3n del gancho), o error, que trae el motivo. Es el \xFAnico video de servicio disponible por ahora: no ofrezcas un video con presentador. Puede no estar disponible para todas las cuentas: si responde que la funci\xF3n no est\xE1 disponible, dilo y no ofrezcas alternativas pagadas.`,
|
|
25439
25635
|
inputSchema: CreateAnimatedVideoInput.shape,
|
|
25440
25636
|
annotations: CREATES_PAID_OPEN_WORLD
|
|
25441
25637
|
}, async (input) => {
|
|
@@ -25483,8 +25679,14 @@ function registerAnimatedVideoTools(server, api) {
|
|
|
25483
25679
|
const motionGraphics = request.data;
|
|
25484
25680
|
const hookVariants = motionGraphics.hookVariants ?? DEFAULT_MOTION_GRAPHICS_HOOK_VARIANTS;
|
|
25485
25681
|
const clipCount = motionGraphicsMaxClips(motionGraphics.durationSec);
|
|
25486
|
-
const
|
|
25487
|
-
const
|
|
25682
|
+
const captureId = input.websiteCaptureId;
|
|
25683
|
+
const captureQuote = socialProfileRequest(landingUrl)?.platform === "instagram" ? await Promise.resolve().then(() => api.getJson(`/v1/motion-graphics/captures/${encodeURIComponent(captureId)}`)).then((value) => WebsiteCaptureResponseSchema.safeParse(value), () => null) : null;
|
|
25684
|
+
const acquisition = {
|
|
25685
|
+
sourceUrl: landingUrl,
|
|
25686
|
+
priceCents: captureQuote?.success ? captureQuote.data.socialAcquisitionPriceCents : void 0
|
|
25687
|
+
};
|
|
25688
|
+
const { minCredits, maxCredits } = animatedVideoCreditRange(motionGraphics.durationSec, hookVariants, acquisition);
|
|
25689
|
+
const priceText = animatedVideoMaxPriceText(motionGraphics.durationSec, hookVariants, acquisition);
|
|
25488
25690
|
const versionsText = hookVariants > 1 ? ` y ${hookVariantsLabel(hookVariants)}` : "";
|
|
25489
25691
|
const music = motionGraphicsMusic(motionGraphics);
|
|
25490
25692
|
const musicText = music ? "" : ", sin m\xFAsica de fondo";
|
|
@@ -27118,7 +27320,9 @@ function registerBrandTools(server, api, options = {}) {
|
|
|
27118
27320
|
}
|
|
27119
27321
|
const estimate = await api.postJson("/v1/billing/estimate", {
|
|
27120
27322
|
type: "brand-extract",
|
|
27121
|
-
options: {
|
|
27323
|
+
options: {
|
|
27324
|
+
sourceUrl
|
|
27325
|
+
}
|
|
27122
27326
|
});
|
|
27123
27327
|
const priceCredits = Math.ceil(estimate.estimatedMaxCostCredits);
|
|
27124
27328
|
if (input.confirmed !== true) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@productmaker/mcp",
|
|
3
|
-
"version": "11.5.
|
|
3
|
+
"version": "11.5.5",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"private": false,
|
|
6
6
|
"description": "ProductMaker MCP server — cost products, generate landing pages, UGC videos, and ad creatives, and publish to Shopify, Meta Ads, and TikTok Ads.",
|