@productmaker/mcp 11.5.4 → 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.
Files changed (3) hide show
  1. package/dist/http.js +503 -302
  2. package/dist/stdio.js +253 -52
  3. package/package.json +1 -1
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({}).strict();
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");
@@ -15620,43 +16096,9 @@ var MotionGraphicsTranscriptResponseSchema = z84.discriminatedUnion("status", [
15620
16096
  }).strict(),
15621
16097
  z84.object({
15622
16098
  status: z84.literal("skipped"),
15623
- 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");
16099
+ reason: MotionGraphicsTranscriptSkipReasonSchema
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;
@@ -22234,6 +22429,8 @@ var WebsiteCaptureResponseSchema = z109.object({
22234
22429
  status: WebsiteCaptureStatusSchema,
22235
22430
  expiresAt: z109.string().datetime(),
22236
22431
  previewUrl: z109.string().url().optional(),
22432
+ /** Final customer amount for accepted Instagram acquisition still pending payment. */
22433
+ socialAcquisitionPriceCents: z109.number().finite().nonnegative().optional(),
22237
22434
  cause: WebsiteCaptureCauseSchema.optional()
22238
22435
  });
22239
22436
  var WebsiteCaptureJobSchema = WebsiteCaptureRequestSchema.extend({
@@ -24189,7 +24386,7 @@ var ApiClient = class {
24189
24386
  };
24190
24387
 
24191
24388
  // src/index.ts
24192
- var MCP_VERSION = "11.5.4";
24389
+ var MCP_VERSION = "11.5.5";
24193
24390
 
24194
24391
  // src/meta-upload-app.ts
24195
24392
  import { readFileSync } from "fs";
@@ -26786,15 +26983,11 @@ function centsToCredits(cents) {
26786
26983
  return Math.round(cents * CREDITS_PER_USD_CENT);
26787
26984
  }
26788
26985
  __name(centsToCredits, "centsToCredits");
26789
- function animatedVideoCredits(durationSec, hookVariants = DEFAULT_MOTION_GRAPHICS_HOOK_VARIANTS) {
26790
- return centsToCredits(motionGraphicsPriceCents(durationSec, {
26791
- hookVariants
26792
- }));
26793
- }
26794
- __name(animatedVideoCredits, "animatedVideoCredits");
26795
- function animatedVideoCreditRange(durationSec, hookVariants = DEFAULT_MOTION_GRAPHICS_HOOK_VARIANTS) {
26986
+ function animatedVideoCreditRange(durationSec, hookVariants = DEFAULT_MOTION_GRAPHICS_HOOK_VARIANTS, acquisition = {}) {
26796
26987
  const { minCents, maxCents } = motionGraphicsPriceRangeCents(durationSec, {
26797
- hookVariants
26988
+ hookVariants,
26989
+ socialSourceUrl: acquisition.sourceUrl,
26990
+ socialAcquisitionPriceCents: acquisition.priceCents
26798
26991
  });
26799
26992
  return {
26800
26993
  minCredits: centsToCredits(minCents),
@@ -26823,8 +27016,8 @@ function describeAnimatedVideoPrices() {
26823
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)})`;
26824
27017
  }
26825
27018
  __name(describeAnimatedVideoPrices, "describeAnimatedVideoPrices");
26826
- function animatedVideoMaxPriceText(durationSec, hookVariants = DEFAULT_MOTION_GRAPHICS_HOOK_VARIANTS) {
26827
- return `m\xE1ximo ${animatedVideoCredits(durationSec, hookVariants)} cr\xE9ditos (se devuelve lo que no use: clips IA, voz en off, clips hablados con la voz del narrador y versiones no entregadas)`;
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)`;
26828
27021
  }
26829
27022
  __name(animatedVideoMaxPriceText, "animatedVideoMaxPriceText");
26830
27023
  function hookVariantsLabel(count) {
@@ -27056,7 +27249,7 @@ function renderAnimatedVideoStatus(result) {
27056
27249
  __name(renderAnimatedVideoStatus, "renderAnimatedVideoStatus");
27057
27250
  function registerAnimatedVideoTools(server, api) {
27058
27251
  server.registerTool("create_animated_video", {
27059
- 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.`,
27060
27253
  inputSchema: CreateAnimatedVideoInput.shape,
27061
27254
  annotations: CREATES_PAID_OPEN_WORLD
27062
27255
  }, async (input) => {
@@ -27104,8 +27297,14 @@ function registerAnimatedVideoTools(server, api) {
27104
27297
  const motionGraphics = request.data;
27105
27298
  const hookVariants = motionGraphics.hookVariants ?? DEFAULT_MOTION_GRAPHICS_HOOK_VARIANTS;
27106
27299
  const clipCount = motionGraphicsMaxClips(motionGraphics.durationSec);
27107
- const { minCredits, maxCredits } = animatedVideoCreditRange(motionGraphics.durationSec, hookVariants);
27108
- const priceText = animatedVideoMaxPriceText(motionGraphics.durationSec, hookVariants);
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);
27109
27308
  const versionsText = hookVariants > 1 ? ` y ${hookVariantsLabel(hookVariants)}` : "";
27110
27309
  const music = motionGraphicsMusic(motionGraphics);
27111
27310
  const musicText = music ? "" : ", sin m\xFAsica de fondo";
@@ -28739,7 +28938,9 @@ function registerBrandTools(server, api, options = {}) {
28739
28938
  }
28740
28939
  const estimate = await api.postJson("/v1/billing/estimate", {
28741
28940
  type: "brand-extract",
28742
- options: {}
28941
+ options: {
28942
+ sourceUrl
28943
+ }
28743
28944
  });
28744
28945
  const priceCredits = Math.ceil(estimate.estimatedMaxCostCredits);
28745
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({}).strict();
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") {
@@ -22002,6 +22197,8 @@ var WebsiteCaptureResponseSchema = z109.object({
22002
22197
  status: WebsiteCaptureStatusSchema,
22003
22198
  expiresAt: z109.string().datetime(),
22004
22199
  previewUrl: z109.string().url().optional(),
22200
+ /** Final customer amount for accepted Instagram acquisition still pending payment. */
22201
+ socialAcquisitionPriceCents: z109.number().finite().nonnegative().optional(),
22005
22202
  cause: WebsiteCaptureCauseSchema.optional()
22006
22203
  });
22007
22204
  var WebsiteCaptureJobSchema = WebsiteCaptureRequestSchema.extend({
@@ -22518,7 +22715,7 @@ var ApiClient = class {
22518
22715
  };
22519
22716
 
22520
22717
  // src/index.ts
22521
- var MCP_VERSION = "11.5.4";
22718
+ var MCP_VERSION = "11.5.5";
22522
22719
 
22523
22720
  // src/meta-upload-app.ts
22524
22721
  import { readFileSync } from "fs";
@@ -25168,15 +25365,11 @@ function centsToCredits(cents) {
25168
25365
  return Math.round(cents * CREDITS_PER_USD_CENT);
25169
25366
  }
25170
25367
  __name(centsToCredits, "centsToCredits");
25171
- function animatedVideoCredits(durationSec, hookVariants = DEFAULT_MOTION_GRAPHICS_HOOK_VARIANTS) {
25172
- return centsToCredits(motionGraphicsPriceCents(durationSec, {
25173
- hookVariants
25174
- }));
25175
- }
25176
- __name(animatedVideoCredits, "animatedVideoCredits");
25177
- function animatedVideoCreditRange(durationSec, hookVariants = DEFAULT_MOTION_GRAPHICS_HOOK_VARIANTS) {
25368
+ function animatedVideoCreditRange(durationSec, hookVariants = DEFAULT_MOTION_GRAPHICS_HOOK_VARIANTS, acquisition = {}) {
25178
25369
  const { minCents, maxCents } = motionGraphicsPriceRangeCents(durationSec, {
25179
- hookVariants
25370
+ hookVariants,
25371
+ socialSourceUrl: acquisition.sourceUrl,
25372
+ socialAcquisitionPriceCents: acquisition.priceCents
25180
25373
  });
25181
25374
  return {
25182
25375
  minCredits: centsToCredits(minCents),
@@ -25205,8 +25398,8 @@ function describeAnimatedVideoPrices() {
25205
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)})`;
25206
25399
  }
25207
25400
  __name(describeAnimatedVideoPrices, "describeAnimatedVideoPrices");
25208
- function animatedVideoMaxPriceText(durationSec, hookVariants = DEFAULT_MOTION_GRAPHICS_HOOK_VARIANTS) {
25209
- return `m\xE1ximo ${animatedVideoCredits(durationSec, hookVariants)} cr\xE9ditos (se devuelve lo que no use: clips IA, voz en off, clips hablados con la voz del narrador y versiones no entregadas)`;
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)`;
25210
25403
  }
25211
25404
  __name(animatedVideoMaxPriceText, "animatedVideoMaxPriceText");
25212
25405
  function hookVariantsLabel(count) {
@@ -25438,7 +25631,7 @@ function renderAnimatedVideoStatus(result) {
25438
25631
  __name(renderAnimatedVideoStatus, "renderAnimatedVideoStatus");
25439
25632
  function registerAnimatedVideoTools(server, api) {
25440
25633
  server.registerTool("create_animated_video", {
25441
- 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.`,
25442
25635
  inputSchema: CreateAnimatedVideoInput.shape,
25443
25636
  annotations: CREATES_PAID_OPEN_WORLD
25444
25637
  }, async (input) => {
@@ -25486,8 +25679,14 @@ function registerAnimatedVideoTools(server, api) {
25486
25679
  const motionGraphics = request.data;
25487
25680
  const hookVariants = motionGraphics.hookVariants ?? DEFAULT_MOTION_GRAPHICS_HOOK_VARIANTS;
25488
25681
  const clipCount = motionGraphicsMaxClips(motionGraphics.durationSec);
25489
- const { minCredits, maxCredits } = animatedVideoCreditRange(motionGraphics.durationSec, hookVariants);
25490
- const priceText = animatedVideoMaxPriceText(motionGraphics.durationSec, hookVariants);
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);
25491
25690
  const versionsText = hookVariants > 1 ? ` y ${hookVariantsLabel(hookVariants)}` : "";
25492
25691
  const music = motionGraphicsMusic(motionGraphics);
25493
25692
  const musicText = music ? "" : ", sin m\xFAsica de fondo";
@@ -27121,7 +27320,9 @@ function registerBrandTools(server, api, options = {}) {
27121
27320
  }
27122
27321
  const estimate = await api.postJson("/v1/billing/estimate", {
27123
27322
  type: "brand-extract",
27124
- options: {}
27323
+ options: {
27324
+ sourceUrl
27325
+ }
27125
27326
  });
27126
27327
  const priceCredits = Math.ceil(estimate.estimatedMaxCostCredits);
27127
27328
  if (input.confirmed !== true) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@productmaker/mcp",
3
- "version": "11.5.4",
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.",