mcp-scraper 0.3.28 → 0.3.40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/api-server.cjs +2459 -1137
- package/dist/bin/api-server.cjs.map +1 -1
- package/dist/bin/api-server.js +3 -3
- package/dist/bin/mcp-scraper-cli.cjs +1 -1
- package/dist/bin/mcp-scraper-cli.cjs.map +1 -1
- package/dist/bin/mcp-scraper-cli.js +1 -1
- package/dist/bin/mcp-scraper-install.cjs +1 -1
- package/dist/bin/mcp-scraper-install.cjs.map +1 -1
- package/dist/bin/mcp-scraper-install.js +1 -1
- package/dist/bin/mcp-stdio-server.cjs +237 -6
- package/dist/bin/mcp-stdio-server.cjs.map +1 -1
- package/dist/bin/mcp-stdio-server.js +2 -2
- package/dist/bin/paa-harvest.cjs +109 -5
- package/dist/bin/paa-harvest.cjs.map +1 -1
- package/dist/bin/paa-harvest.js +2 -2
- package/dist/{chunk-TRNSXR46.js → chunk-DVRPXPYH.js} +22 -3
- package/dist/chunk-DVRPXPYH.js.map +1 -0
- package/dist/{chunk-QSSH4RCX.js → chunk-GMTS35L6.js} +111 -7
- package/dist/chunk-GMTS35L6.js.map +1 -0
- package/dist/chunk-I5EFBTYE.js +7 -0
- package/dist/chunk-I5EFBTYE.js.map +1 -0
- package/dist/{chunk-H74L743B.js → chunk-KJWNVQFL.js} +39 -2
- package/dist/chunk-KJWNVQFL.js.map +1 -0
- package/dist/{chunk-NGD4ITAO.js → chunk-QLQYNE7E.js} +238 -7
- package/dist/chunk-QLQYNE7E.js.map +1 -0
- package/dist/{db-FP2ABUU4.js → db-LYQENFPW.js} +2 -2
- package/dist/index.cjs +109 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +2 -2
- package/dist/{server-IDDX7KGT.js → server-HB7WJO3N.js} +1154 -251
- package/dist/server-HB7WJO3N.js.map +1 -0
- package/dist/{worker-NV4GTIRG.js → worker-TL4J65DE.js} +4 -4
- package/package.json +1 -1
- package/dist/chunk-7MY2NSQX.js +0 -7
- package/dist/chunk-7MY2NSQX.js.map +0 -1
- package/dist/chunk-H74L743B.js.map +0 -1
- package/dist/chunk-NGD4ITAO.js.map +0 -1
- package/dist/chunk-QSSH4RCX.js.map +0 -1
- package/dist/chunk-TRNSXR46.js.map +0 -1
- package/dist/server-IDDX7KGT.js.map +0 -1
- /package/dist/{db-FP2ABUU4.js.map → db-LYQENFPW.js.map} +0 -0
- /package/dist/{worker-NV4GTIRG.js.map → worker-TL4J65DE.js.map} +0 -0
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
import {
|
|
7
7
|
finishHarvestAttempt,
|
|
8
8
|
startHarvestAttempt
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-DVRPXPYH.js";
|
|
10
10
|
|
|
11
11
|
// src/api/rates.ts
|
|
12
12
|
var MC_COSTS = {
|
|
@@ -23,6 +23,9 @@ var MC_COSTS = {
|
|
|
23
23
|
maps_review: 100,
|
|
24
24
|
fb_search: 600,
|
|
25
25
|
fb_transcribe: 200,
|
|
26
|
+
google_ads_search: 600,
|
|
27
|
+
google_ads_intel: 200,
|
|
28
|
+
google_ads_transcribe: 200,
|
|
26
29
|
instagram_profile: 400,
|
|
27
30
|
instagram_media: 400,
|
|
28
31
|
instagram_transcribe: 50,
|
|
@@ -122,6 +125,30 @@ var CREDIT_COST_CATALOG = [
|
|
|
122
125
|
unit: "per search",
|
|
123
126
|
notes: "Browser automation to search Facebook Ads Library by keyword."
|
|
124
127
|
},
|
|
128
|
+
{
|
|
129
|
+
key: "google_ads_search",
|
|
130
|
+
label: "Google Ads Transparency search",
|
|
131
|
+
aliases: ["google_ads_search", "google ads search", "ads transparency search"],
|
|
132
|
+
credits: mcToCredits(MC_COSTS.google_ads_search),
|
|
133
|
+
unit: "per search",
|
|
134
|
+
notes: "Browser automation to find advertisers in Google Ads Transparency Center by domain or name."
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
key: "google_ads_intel",
|
|
138
|
+
label: "Google Ads Transparency advertiser intel",
|
|
139
|
+
aliases: ["google_ads_page_intel", "google ads intel", "ads transparency intel"],
|
|
140
|
+
credits: mcToCredits(MC_COSTS.google_ads_intel),
|
|
141
|
+
unit: "per call",
|
|
142
|
+
notes: "Lists and hydrates an advertiser's creatives with image URLs and video references."
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
key: "google_ads_transcribe",
|
|
146
|
+
label: "Google ad video transcription",
|
|
147
|
+
aliases: ["google_ads_transcribe", "google ad transcript"],
|
|
148
|
+
credits: mcToCredits(MC_COSTS.google_ads_transcribe),
|
|
149
|
+
unit: "per minute",
|
|
150
|
+
notes: "A hold is taken, then reconciled to actual video duration."
|
|
151
|
+
},
|
|
125
152
|
{
|
|
126
153
|
key: "fb_transcribe",
|
|
127
154
|
label: "Facebook video / ad transcription",
|
|
@@ -223,6 +250,9 @@ var MEMORY_PLANS = {
|
|
|
223
250
|
var MEMORY_PLAN_QUOTA = { free: MEMORY_FREE_QUOTA_BYTES, pro: 5e9, team: 5e10 };
|
|
224
251
|
var MEMORY_MARGIN_MULTIPLE = 3;
|
|
225
252
|
var MEMORY_PRO_COST_BUDGET_USD = 19 / MEMORY_MARGIN_MULTIPLE;
|
|
253
|
+
var SCHEDULING_PLANS = {
|
|
254
|
+
"price_1ToXcHS8aAcsk3TGf3cW7zHx": { price_id: "price_1ToXcHS8aAcsk3TGf3cW7zHx", label: "Scheduled Actions", interval: "month", monthly_usd: 10, quota_per_period: 1e3 }
|
|
255
|
+
};
|
|
226
256
|
var LedgerOperation = {
|
|
227
257
|
TOPUP: "topup",
|
|
228
258
|
SUBSCRIPTION: "subscription",
|
|
@@ -253,6 +283,12 @@ var LedgerOperation = {
|
|
|
253
283
|
FB_TRANSCRIBE: "fb_transcribe",
|
|
254
284
|
FB_SEARCH_REFUND: "fb_search_refund",
|
|
255
285
|
FB_TRANSCRIBE_REFUND: "fb_transcribe_refund",
|
|
286
|
+
GOOGLE_ADS_SEARCH: "google_ads_search",
|
|
287
|
+
GOOGLE_ADS_SEARCH_REFUND: "google_ads_search_refund",
|
|
288
|
+
GOOGLE_ADS_INTEL: "google_ads_intel",
|
|
289
|
+
GOOGLE_ADS_INTEL_REFUND: "google_ads_intel_refund",
|
|
290
|
+
GOOGLE_ADS_TRANSCRIBE: "google_ads_transcribe",
|
|
291
|
+
GOOGLE_ADS_TRANSCRIBE_REFUND: "google_ads_transcribe_refund",
|
|
256
292
|
INSTAGRAM_PROFILE: "instagram_profile",
|
|
257
293
|
INSTAGRAM_PROFILE_REFUND: "instagram_profile_refund",
|
|
258
294
|
INSTAGRAM_MEDIA: "instagram_media",
|
|
@@ -419,10 +455,11 @@ export {
|
|
|
419
455
|
insufficientBalanceResponse,
|
|
420
456
|
MEMORY_PLANS,
|
|
421
457
|
MEMORY_PLAN_QUOTA,
|
|
458
|
+
SCHEDULING_PLANS,
|
|
422
459
|
LedgerOperation,
|
|
423
460
|
classifyHarvestProblem,
|
|
424
461
|
serializeHarvestProblem,
|
|
425
462
|
harvestProblemResponse,
|
|
426
463
|
createHarvestAttemptRecorder
|
|
427
464
|
};
|
|
428
|
-
//# sourceMappingURL=chunk-
|
|
465
|
+
//# sourceMappingURL=chunk-KJWNVQFL.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/api/rates.ts","../src/api/harvest-problems.ts","../src/api/harvest-attempt-events.ts"],"sourcesContent":["export const MC_COSTS = {\n serp: 100,\n paa: 200,\n paa_base: 1_000,\n page_scrape: 100,\n url_map: 500,\n yt_channel: 200,\n yt_transcription: 200,\n fb_ad: 200,\n maps_search: 500,\n maps_place: 3_000,\n maps_review: 100,\n fb_search: 600,\n fb_transcribe: 200,\n google_ads_search: 600,\n google_ads_intel: 200,\n google_ads_transcribe: 200,\n instagram_profile: 400,\n instagram_media: 400,\n instagram_transcribe: 50,\n browser_minute: 12_000,\n reddit_thread: 2_000,\n} as const\n\nexport type McCostKey = keyof typeof MC_COSTS\n\nexport const MC_PER_BROWSER_MS = MC_COSTS.browser_minute / 60_000\n\nexport function browserActiveCostMc(activeMs: number): number {\n return Math.round(activeMs * MC_PER_BROWSER_MS)\n}\n\nexport const BROWSER_OPEN_MIN_BALANCE_MC = 1_000\n\nexport const MC_PER_CREDIT = 1_000\n\nexport const CREDIT_COST_CATALOG: Array<{\n key: McCostKey\n label: string\n aliases: string[]\n credits: number\n unit: string\n notes?: string\n}> = [\n {\n key: 'serp',\n label: 'SERP search',\n aliases: ['search_serp', 'serp', 'google search', 'organic results'],\n credits: mcToCredits(MC_COSTS.serp),\n unit: 'per search',\n notes: 'Returns AI Overview, PAA snippet, videos, forums, and local pack.',\n },\n {\n key: 'paa',\n label: 'PAA harvest',\n aliases: ['harvest_paa', 'paa', 'people also ask', 'questions'],\n credits: mcToCredits(MC_COSTS.paa),\n unit: `per question (+${mcToCredits(MC_COSTS.paa_base)} credit base)`,\n notes: `Full SERP feature extraction. Billed ${mcToCredits(MC_COSTS.paa_base)} credit base + ${mcToCredits(MC_COSTS.paa)} per question actually returned (unused estimate refunded).`,\n },\n {\n key: 'page_scrape',\n label: 'Page crawl / extract',\n aliases: ['extract_url', 'extract_site', 'page scrape', 'url scrape', 'single page', 'site crawl'],\n credits: mcToCredits(MC_COSTS.page_scrape),\n unit: 'per page',\n notes: 'Applies to both single-URL extraction and per-page site crawls.',\n },\n {\n key: 'url_map',\n label: 'Site URL mapping',\n aliases: ['map_site_urls', 'url map', 'site map', 'crawl urls'],\n credits: mcToCredits(MC_COSTS.url_map),\n unit: 'per mapping operation',\n notes: 'Flat rate for the full /map-urls call regardless of URL count discovered.',\n },\n {\n key: 'yt_channel',\n label: 'YouTube search / channel harvest',\n aliases: ['youtube_harvest', 'youtube search', 'youtube channel', 'yt_channel'],\n credits: mcToCredits(MC_COSTS.yt_channel),\n unit: 'per call',\n },\n {\n key: 'yt_transcription',\n label: 'YouTube transcription',\n aliases: ['youtube_transcribe', 'youtube transcript', 'transcription', 'yt_transcription'],\n credits: mcToCredits(MC_COSTS.yt_transcription),\n unit: 'per minute',\n notes: 'A 5-minute hold is taken, then reconciled to actual video duration.',\n },\n {\n key: 'fb_ad',\n label: 'Facebook search / ad lookup',\n aliases: ['facebook_page_intel', 'facebook_ad_search', 'facebook_ad', 'facebook ads', 'fb ads'],\n credits: mcToCredits(MC_COSTS.fb_ad),\n unit: 'per call',\n },\n {\n key: 'maps_search',\n label: 'Maps business search',\n aliases: ['maps_search', 'google maps search', 'gmb search', 'gbp search', 'business profiles'],\n credits: mcToCredits(MC_COSTS.maps_search),\n unit: 'per search',\n notes: 'Returns up to 50 Google Maps business/profile candidates. Use maps_place_intel to hydrate selected businesses.',\n },\n {\n key: 'maps_place',\n label: 'Maps business lookup',\n aliases: ['maps_place_intel', 'google maps', 'maps place', 'place intel'],\n credits: mcToCredits(MC_COSTS.maps_place),\n unit: 'per business',\n notes: 'Base lookup. Reviews billed separately per card at maps_review rate.',\n },\n {\n key: 'maps_review',\n label: 'Maps review',\n aliases: ['maps_reviews', 'google reviews', 'review cards', 'reviews'],\n credits: mcToCredits(MC_COSTS.maps_review),\n unit: 'per review card',\n notes: 'Charged after extraction when includeReviews is true.',\n },\n {\n key: 'fb_search',\n label: 'Facebook ad library search',\n aliases: ['facebook_search', 'fb_search', 'fb ad search'],\n credits: mcToCredits(MC_COSTS.fb_search),\n unit: 'per search',\n notes: 'Browser automation to search Facebook Ads Library by keyword.',\n },\n {\n key: 'google_ads_search',\n label: 'Google Ads Transparency search',\n aliases: ['google_ads_search', 'google ads search', 'ads transparency search'],\n credits: mcToCredits(MC_COSTS.google_ads_search),\n unit: 'per search',\n notes: 'Browser automation to find advertisers in Google Ads Transparency Center by domain or name.',\n },\n {\n key: 'google_ads_intel',\n label: 'Google Ads Transparency advertiser intel',\n aliases: ['google_ads_page_intel', 'google ads intel', 'ads transparency intel'],\n credits: mcToCredits(MC_COSTS.google_ads_intel),\n unit: 'per call',\n notes: 'Lists and hydrates an advertiser\\'s creatives with image URLs and video references.',\n },\n {\n key: 'google_ads_transcribe',\n label: 'Google ad video transcription',\n aliases: ['google_ads_transcribe', 'google ad transcript'],\n credits: mcToCredits(MC_COSTS.google_ads_transcribe),\n unit: 'per minute',\n notes: 'A hold is taken, then reconciled to actual video duration.',\n },\n {\n key: 'fb_transcribe',\n label: 'Facebook video / ad transcription',\n aliases: ['facebook_transcribe', 'facebook_video_transcribe', 'fb_transcribe', 'fb ad transcript'],\n credits: mcToCredits(MC_COSTS.fb_transcribe),\n unit: 'per minute',\n notes: 'A hold is taken, then reconciled to actual video duration.',\n },\n {\n key: 'instagram_profile',\n label: 'Instagram profile content discovery',\n aliases: ['instagram_profile_content', 'instagram profile', 'instagram content list', 'ig profile'],\n credits: mcToCredits(MC_COSTS.instagram_profile),\n unit: 'per profile scan',\n notes: 'Browser extraction of public Instagram profile grid links. Complete history may require a logged-in profile.',\n },\n {\n key: 'instagram_media',\n label: 'Instagram media download',\n aliases: ['instagram_media_download', 'instagram reel download', 'instagram post download', 'ig media'],\n credits: mcToCredits(MC_COSTS.instagram_media),\n unit: 'per post or reel',\n notes: 'Extracts post text, image URLs, and reel audio/video tracks, with local downloads when the server can write files.',\n },\n {\n key: 'instagram_transcribe',\n label: 'Instagram media transcription',\n aliases: ['instagram transcript', 'instagram reel transcript', 'ig transcribe'],\n credits: mcToCredits(MC_COSTS.instagram_transcribe),\n unit: 'per call',\n notes: 'Whisper transcription of selected Instagram audio/video media via fal.ai.',\n },\n {\n key: 'browser_minute',\n label: 'Interactive browser session',\n aliases: ['browser_open', 'browser agent', 'browser_agent', 'live browser', 'browse', 'browser control', 'interactive browser'],\n credits: mcToCredits(MC_COSTS.browser_minute),\n unit: 'per minute of use',\n notes: 'Metered per minute of use for the whole time the browser session is open. Close the session to stop the meter; abandoned sessions are auto-closed after a short idle window.',\n },\n {\n key: 'reddit_thread',\n label: 'Reddit thread + comments',\n aliases: ['reddit', 'reddit_thread', 'reddit comments', 'subreddit', 'reddit post'],\n credits: mcToCredits(MC_COSTS.reddit_thread),\n unit: 'per thread',\n notes: 'Captures a Reddit post and its comment tree. Charged on success; refunded if the thread cannot be retrieved.',\n },\n]\n\nexport const CONCURRENCY_PRICE_ID = 'price_1Ta1NRS8aAcsk3TGwsRnYbix'\n\nexport interface SubscriptionTier { tier: string; label: string; price_id: string; monthly_usd: number; credits_mc: number; concurrency: number; intro_coupon: string | null }\n\nexport const SUBSCRIPTION_TIERS: Record<string, SubscriptionTier> = {\n 'price_1TmiHRS8aAcsk3TGwmSNfNIa': { tier: 'starter', label: 'Starter', price_id: 'price_1TmiHRS8aAcsk3TGwmSNfNIa', monthly_usd: 12, credits_mc: 8_000_000, concurrency: 3, intro_coupon: 'mcp-starter-1dollar-intro-12' },\n 'price_1Tmg1nS8aAcsk3TGe8zcnGTM': { tier: 'growth', label: 'Growth', price_id: 'price_1Tmg1nS8aAcsk3TGe8zcnGTM', monthly_usd: 100, credits_mc: 80_000_000, concurrency: 10, intro_coupon: null },\n 'price_1Tmg1nS8aAcsk3TGsZw34iXS': { tier: 'scale', label: 'Scale', price_id: 'price_1Tmg1nS8aAcsk3TGsZw34iXS', monthly_usd: 250, credits_mc: 240_000_000, concurrency: 20, intro_coupon: null },\n}\n\nexport const SUBSCRIPTION_TIER_BY_KEY: Record<string, SubscriptionTier> = Object.fromEntries(\n Object.values(SUBSCRIPTION_TIERS).map(t => [t.tier, t]),\n)\nexport const CONCURRENCY_SLOT_PRICE_USD = 5\nexport const CONCURRENCY_SLOT_PRICE_CURRENCY = 'usd'\nexport const CONCURRENCY_SLOT_PRICE_INTERVAL = 'month'\nexport const CONCURRENCY_SLOT_PRICE_LABEL = '$5/month'\nexport const CONCURRENCY_SLOT_TERMINAL_COMMAND = 'npx -y -p mcp-scraper@latest mcp-scraper-cli billing concurrency checkout'\n\nexport function concurrencySlotBillingInfo() {\n const billingUrl = process.env.CONCURRENCY_BILLING_URL ?? 'https://mcpscraper.dev/billing'\n return {\n product: 'Extra concurrency slot',\n price_label: CONCURRENCY_SLOT_PRICE_LABEL,\n unit_amount_usd: CONCURRENCY_SLOT_PRICE_USD,\n currency: CONCURRENCY_SLOT_PRICE_CURRENCY,\n interval: CONCURRENCY_SLOT_PRICE_INTERVAL,\n billing_url: billingUrl,\n terminal_command: CONCURRENCY_SLOT_TERMINAL_COMMAND,\n terminal_command_with_api_key_env: `MCP_SCRAPER_API_KEY=sk_live_your_key ${CONCURRENCY_SLOT_TERMINAL_COMMAND}`,\n }\n}\n\nexport const FREE_SIGNUP_MC = 100_000\nexport const FREE_MONTHLY_REFRESH_MC = 250_000\n\nexport const BALANCE_PRICE_IDS: Record<string, number> = {\n 'price_1TZx6rS8aAcsk3TGNMc1Vgpo': 11_000_000,\n 'price_1TZx6sS8aAcsk3TGxgqB7khO': 27_500_000,\n 'price_1TZx6tS8aAcsk3TG8PnJqHlG': 60_500_000,\n 'price_1TZx6tS8aAcsk3TGNgRMpy0e': 121_000_000,\n}\n\nexport const BALANCE_PACK_LABELS: Record<string, string> = {\n 'price_1TZx6rS8aAcsk3TGNMc1Vgpo': '$10',\n 'price_1TZx6sS8aAcsk3TGxgqB7khO': '$25',\n 'price_1TZx6tS8aAcsk3TG8PnJqHlG': '$50',\n 'price_1TZx6tS8aAcsk3TGNgRMpy0e': '$100',\n}\n\nexport function mcToCredits(mc: number): number {\n return mc / MC_PER_CREDIT\n}\n\nexport function insufficientBalanceResponse(balanceMc: number, requiredMc: number) {\n const topupUrl = process.env.TOPUP_URL ?? 'https://mcpscraper.dev/billing'\n const balanceCredits = mcToCredits(balanceMc)\n const requiredCredits = mcToCredits(requiredMc)\n return {\n error: 'insufficient_balance',\n error_code: 'insufficient_balance' as const,\n message: `Insufficient credits. Balance: ${balanceCredits} credits. This call requires ${requiredCredits} credits. Top up at ${topupUrl}`,\n balance_credits: balanceCredits,\n required_credits: requiredCredits,\n topup_url: topupUrl,\n }\n}\n\nexport interface MemoryPlan {\n plan: 'pro' | 'team'\n label: string\n price_id: string\n interval: 'month' | 'year'\n monthly_usd: number\n quota_bytes: number\n}\n\nexport const MEMORY_FREE_QUOTA_BYTES = 0.1e9 * 1\n\nexport const MEMORY_PLANS: Record<string, MemoryPlan> = {\n 'price_1TnMSTS8aAcsk3TGBgiwuvqL': { plan: 'pro', label: 'Pro', price_id: 'price_1TnMSTS8aAcsk3TGBgiwuvqL', interval: 'month', monthly_usd: 19, quota_bytes: 5e9 },\n 'price_1TnMSTS8aAcsk3TGWBVU2agY': { plan: 'pro', label: 'Pro', price_id: 'price_1TnMSTS8aAcsk3TGWBVU2agY', interval: 'year', monthly_usd: 19, quota_bytes: 5e9 },\n}\n\nexport const MEMORY_PLAN_QUOTA: Record<string, number> = { free: MEMORY_FREE_QUOTA_BYTES, pro: 5e9, team: 50e9 }\n\nexport const MEMORY_MARGIN_MULTIPLE = 3\nexport const MEMORY_FREE_COST_CAP_USD = 1.0\nexport const MEMORY_PRO_COST_BUDGET_USD = 19 / MEMORY_MARGIN_MULTIPLE\n\nexport interface SchedulingPlan {\n price_id: string\n label: string\n interval: 'month'\n monthly_usd: number\n quota_per_period: number\n}\n\nexport const SCHEDULING_PLANS: Record<string, SchedulingPlan> = {\n 'price_1ToXcHS8aAcsk3TGf3cW7zHx': { price_id: 'price_1ToXcHS8aAcsk3TGf3cW7zHx', label: 'Scheduled Actions', interval: 'month', monthly_usd: 10, quota_per_period: 1000 },\n}\n\nexport const LedgerOperation = {\n TOPUP: 'topup',\n SUBSCRIPTION: 'subscription',\n SIGNUP_GRANT: 'signup_grant',\n MONTHLY_REFRESH: 'monthly_free_refresh',\n PAA: 'paa',\n PAA_REFUND: 'paa_refund',\n SERP: 'serp',\n REFUND: 'refund',\n TRANSCRIPTION: 'transcription',\n TRANSCRIPTION_HOLD: 'transcription_hold',\n TRANSCRIPTION_REFUND: 'transcription_refund',\n YT_CHANNEL: 'yt_channel',\n FB_AD: 'fb_ad',\n MAPS_SEARCH: 'maps_search',\n MAPS_PLACE: 'maps_place',\n MAPS_REVIEW: 'maps_review',\n MAPS_REVIEW_REFUND: 'maps_review_refund',\n EXTRACT_SITE: 'extract_site',\n EXTRACT_SITE_REFUND: 'extract_site_refund',\n EXTRACT_URL: 'page_scrape',\n URL_MAP: 'url_map',\n EXTRACT_SITE_HOLD: 'extract_site_hold',\n YT_CHANNEL_REFUND: 'yt_channel_refund',\n FB_AD_REFUND: 'fb_ad_refund',\n URL_MAP_REFUND: 'url_map_refund',\n FB_SEARCH: 'fb_search',\n FB_TRANSCRIBE: 'fb_transcribe',\n FB_SEARCH_REFUND: 'fb_search_refund',\n FB_TRANSCRIBE_REFUND: 'fb_transcribe_refund',\n GOOGLE_ADS_SEARCH: 'google_ads_search',\n GOOGLE_ADS_SEARCH_REFUND: 'google_ads_search_refund',\n GOOGLE_ADS_INTEL: 'google_ads_intel',\n GOOGLE_ADS_INTEL_REFUND: 'google_ads_intel_refund',\n GOOGLE_ADS_TRANSCRIBE: 'google_ads_transcribe',\n GOOGLE_ADS_TRANSCRIBE_REFUND: 'google_ads_transcribe_refund',\n INSTAGRAM_PROFILE: 'instagram_profile',\n INSTAGRAM_PROFILE_REFUND: 'instagram_profile_refund',\n INSTAGRAM_MEDIA: 'instagram_media',\n INSTAGRAM_MEDIA_REFUND:'instagram_media_refund',\n INSTAGRAM_TRANSCRIBE: 'instagram_transcribe',\n INSTAGRAM_TRANSCRIBE_REFUND: 'instagram_transcribe_refund',\n BROWSER_SESSION: 'browser_session',\n REDDIT_THREAD: 'reddit_thread',\n REDDIT_THREAD_REFUND: 'reddit_thread_refund',\n} as const\n\nexport type LedgerOperation = typeof LedgerOperation[keyof typeof LedgerOperation]\n","import { CaptchaError, LocationMismatchError, RequestAbortedError } from '../errors.js'\n\nexport type HarvestProblemCode =\n | 'request_aborted'\n | 'captcha_exhausted'\n | 'location_mismatch'\n | 'proxy_tunnel_failed'\n | 'proxy_unavailable'\n | 'harvest_timeout'\n | 'extraction_failed'\n\nexport interface HarvestProblem {\n error_code: HarvestProblemCode\n error_type: string\n message: string\n retryable: boolean\n httpStatus: number\n terminalStatus: 'cancelled' | 'failed'\n}\n\nfunction errorMessage(err: unknown): string {\n return err instanceof Error ? err.message : String(err)\n}\n\nfunction looksLikeTimeout(err: unknown, message: string): boolean {\n if (err instanceof DOMException && (err.name === 'TimeoutError' || err.name === 'AbortError')) return true\n return /timeout|timed out|Timeout \\d+ms exceeded|deadline/i.test(message)\n}\n\nfunction looksLikeCaptcha(message: string): boolean {\n return /captcha|recaptcha|unusual traffic|google\\.com\\/sorry|blocked/i.test(message)\n}\n\nfunction looksLikeProxyTunnelFailure(message: string): boolean {\n return /ERR_TUNNEL_CONNECTION_FAILED|ERR_PROXY_CONNECTION_FAILED|ERR_SOCKS_CONNECTION_FAILED|tunnel connection failed|proxy connection failed|transport error: proxy/i.test(message)\n}\n\nfunction looksLikeProxyUnavailable(message: string): boolean {\n return /proxy unavailable|proxy_unavailable|connection_test_failed|did not return a proxy id|configured fallback/i.test(message)\n}\n\nexport function classifyHarvestProblem(err: unknown): HarvestProblem {\n const message = errorMessage(err)\n\n if (err instanceof RequestAbortedError) {\n return {\n error_code: 'request_aborted',\n error_type: 'request_aborted',\n message,\n retryable: true,\n httpStatus: 408,\n terminalStatus: 'cancelled',\n }\n }\n\n if (err instanceof CaptchaError || looksLikeCaptcha(message)) {\n return {\n error_code: 'captcha_exhausted',\n error_type: 'captcha',\n message,\n retryable: true,\n httpStatus: 503,\n terminalStatus: 'failed',\n }\n }\n\n if (err instanceof LocationMismatchError) {\n return {\n error_code: 'location_mismatch',\n error_type: 'location_mismatch',\n message,\n retryable: true,\n httpStatus: 503,\n terminalStatus: 'failed',\n }\n }\n\n if (looksLikeProxyTunnelFailure(message)) {\n return {\n error_code: 'proxy_tunnel_failed',\n error_type: 'proxy_tunnel_failed',\n message,\n retryable: true,\n httpStatus: 503,\n terminalStatus: 'failed',\n }\n }\n\n if (looksLikeProxyUnavailable(message)) {\n return {\n error_code: 'proxy_unavailable',\n error_type: 'proxy_unavailable',\n message,\n retryable: true,\n httpStatus: 503,\n terminalStatus: 'failed',\n }\n }\n\n if (looksLikeTimeout(err, message)) {\n return {\n error_code: 'harvest_timeout',\n error_type: 'timeout',\n message,\n retryable: true,\n httpStatus: 504,\n terminalStatus: 'failed',\n }\n }\n\n return {\n error_code: 'extraction_failed',\n error_type: 'extraction',\n message,\n retryable: false,\n httpStatus: 500,\n terminalStatus: 'failed',\n }\n}\n\nexport function serializeHarvestProblem(problem: HarvestProblem): string {\n return JSON.stringify({\n error_code: problem.error_code,\n error_type: problem.error_type,\n message: problem.message,\n retryable: problem.retryable,\n })\n}\n\nexport function harvestProblemResponse(problem: HarvestProblem): {\n error: string\n error_code: HarvestProblemCode\n error_type: string\n retryable: boolean\n} {\n return {\n error: problem.message,\n error_code: problem.error_code,\n error_type: problem.error_type,\n retryable: problem.retryable,\n }\n}\n","import type { HarvestAttemptLogEvent } from '../harvest.js'\nimport { finishHarvestAttempt, startHarvestAttempt } from './db.js'\n\nexport function createHarvestAttemptRecorder(jobId: string, userId: number | bigint) {\n return async (event: HarvestAttemptLogEvent): Promise<void> => {\n if (event.type === 'started') {\n await startHarvestAttempt({\n jobId,\n userId,\n attemptNumber: event.attemptNumber,\n maxAttempts: event.maxAttempts,\n query: event.query,\n location: event.location,\n maxQuestions: event.maxQuestions,\n startedAt: event.startedAt,\n })\n return\n }\n\n await finishHarvestAttempt({\n jobId,\n attemptNumber: event.attemptNumber,\n outcome: event.outcome,\n kernelSessionId: event.kernelSessionId,\n questionCount: event.questionCount,\n durationMs: event.durationMs,\n error: event.error,\n willRetry: event.willRetry,\n kernelDeleteStarted: event.cleanup.kernelDeleteStarted,\n kernelDeleteSucceeded: event.cleanup.kernelDeleteSucceeded,\n kernelDeleteError: event.cleanup.kernelDeleteError,\n browserCloseSucceeded: event.cleanup.browserCloseSucceeded,\n browserCloseError: event.cleanup.browserCloseError,\n debug: event.debug,\n completedAt: event.completedAt,\n })\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
PACKAGE_VERSION
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-I5EFBTYE.js";
|
|
4
4
|
import {
|
|
5
5
|
browserServiceProfileName,
|
|
6
6
|
browserServiceProfileSaveChanges,
|
|
@@ -127,6 +127,13 @@ seam is noted so you can chain them.
|
|
|
127
127
|
- Transcribe an ad video -> **facebook_ad_transcribe** (takes a videoUrl from \`facebook_page_intel\`).
|
|
128
128
|
- Transcribe an organic reel/video/post -> **facebook_video_transcribe** (takes the Facebook url directly).
|
|
129
129
|
|
|
130
|
+
## Google Ads (Ads Transparency Center)
|
|
131
|
+
- Find advertisers by domain or brand -> **google_ads_search** (returns \`advertisers[]\` with advertiserId).
|
|
132
|
+
- One advertiser's ads -> **google_ads_page_intel** (takes advertiserId or domain; returns \`ads[]\` with
|
|
133
|
+
format, imageUrls, youtubeVideoId, and videoUrl).
|
|
134
|
+
- Transcribe a video ad -> **google_ads_transcribe** (takes a googlevideo \`videoUrl\` from
|
|
135
|
+
\`google_ads_page_intel\`), or **youtube_transcribe** for a returned \`youtubeVideoId\`.
|
|
136
|
+
|
|
130
137
|
## Instagram
|
|
131
138
|
- Profile inventory -> **instagram_profile_content** (returns post/reel/tv urls).
|
|
132
139
|
- One post or reel -> **instagram_media_download** (takes a url from \`instagram_profile_content\`, or a
|
|
@@ -1012,7 +1019,9 @@ ${paaRows}` : "## People Also Ask\n*Google did not return a People Also Ask bloc
|
|
|
1012
1019
|
${serpRows}` : "";
|
|
1013
1020
|
const aiSection = aiOvw?.detected && aiOvw.text ? `
|
|
1014
1021
|
## AI Overview
|
|
1015
|
-
> ${truncate(aiOvw.text, 600)}`
|
|
1022
|
+
> ${truncate(aiOvw.text, 600)}` + (aiOvw.shareUrl ? `
|
|
1023
|
+
|
|
1024
|
+
**Shareable link:** ${aiOvw.shareUrl}` : "") : "";
|
|
1016
1025
|
const statsLine = durationMs ? `
|
|
1017
1026
|
## Stats
|
|
1018
1027
|
- Status: ${diagnostics?.completionStatus ?? (flat.length ? "paa_found" : "no_paa")} \xB7 Questions: ${flat.length} \xB7 Duration: ${(durationMs / 1e3).toFixed(1)}s` : "";
|
|
@@ -1045,7 +1054,7 @@ ${paaTable}${serpTable}${entityIdsSection(entityIds)}${aiSection}${statsLine}${d
|
|
|
1045
1054
|
domain: String(r.domain ?? ""),
|
|
1046
1055
|
snippet: r.snippet ?? null
|
|
1047
1056
|
})),
|
|
1048
|
-
aiOverview: aiOvw ? { detected: aiOvw.detected === true, text: aiOvw.text ?? null } : null,
|
|
1057
|
+
aiOverview: aiOvw ? { detected: aiOvw.detected === true, text: aiOvw.text ?? null, shareUrl: aiOvw.shareUrl ?? null } : null,
|
|
1049
1058
|
entityIds: entityIds ? { entities: entityIds.entities ?? [], kgIds: entityIds.kgIds ?? [], cids: entityIds.cids ?? [], gcids: entityIds.gcids ?? [] } : null,
|
|
1050
1059
|
durationMs: durationMs ?? null
|
|
1051
1060
|
}
|
|
@@ -1077,7 +1086,9 @@ ${serpRows}` : "## Organic Results\n*None found*";
|
|
|
1077
1086
|
${localRows}` : "";
|
|
1078
1087
|
const aiSection = aiOvw?.detected && aiOvw.text ? `
|
|
1079
1088
|
## AI Overview
|
|
1080
|
-
> ${truncate(aiOvw.text, 600)}`
|
|
1089
|
+
> ${truncate(aiOvw.text, 600)}` + (aiOvw.shareUrl ? `
|
|
1090
|
+
|
|
1091
|
+
**Shareable link:** ${aiOvw.shareUrl}` : "") : "";
|
|
1081
1092
|
const tips = `
|
|
1082
1093
|
---
|
|
1083
1094
|
\u{1F4A1} **Tips**
|
|
@@ -1106,7 +1117,7 @@ ${serpTable}${localSection}${entityIdsSection(entityIds)}${aiSection}${debugSect
|
|
|
1106
1117
|
reviewCount: b.reviewCount ?? null,
|
|
1107
1118
|
websiteUrl: b.websiteUrl ?? null
|
|
1108
1119
|
})),
|
|
1109
|
-
aiOverview: aiOvw ? { detected: aiOvw.detected === true, text: aiOvw.text ?? null } : null,
|
|
1120
|
+
aiOverview: aiOvw ? { detected: aiOvw.detected === true, text: aiOvw.text ?? null, shareUrl: aiOvw.shareUrl ?? null } : null,
|
|
1110
1121
|
entityIds: entityIds ? { entities: entityIds.entities ?? [], kgIds: entityIds.kgIds ?? [], cids: entityIds.cids ?? [], gcids: entityIds.gcids ?? [] } : null
|
|
1111
1122
|
}
|
|
1112
1123
|
};
|
|
@@ -1664,6 +1675,136 @@ ${rows}`,
|
|
|
1664
1675
|
}
|
|
1665
1676
|
};
|
|
1666
1677
|
}
|
|
1678
|
+
function formatGoogleAdsSearch(raw, input) {
|
|
1679
|
+
const parsed = parseData(raw);
|
|
1680
|
+
if ("error" in parsed) return { content: [{ type: "text", text: parsed.error }], isError: true };
|
|
1681
|
+
const d = parsed.data;
|
|
1682
|
+
const advertisers = d.advertisers ?? [];
|
|
1683
|
+
const rows = advertisers.map(
|
|
1684
|
+
(a, i) => `| ${i + 1} | ${cell(a.name)} | ${cell(a.domain)} | ${a.approxAdCount ?? "\u2014"} | \`${a.advertiserId ?? "\u2014"}\` |`
|
|
1685
|
+
).join("\n");
|
|
1686
|
+
const full = [
|
|
1687
|
+
`# Google Ads Transparency Search: "${input.query}"`,
|
|
1688
|
+
`**${advertisers.length} advertisers found**${d.region ? ` \xB7 ${d.region}` : ""}`,
|
|
1689
|
+
`
|
|
1690
|
+
## Advertisers
|
|
1691
|
+
| # | Name | Domain | ~Ads | Advertiser ID |
|
|
1692
|
+
|---|------|--------|------|---------------|
|
|
1693
|
+
${rows}`,
|
|
1694
|
+
`
|
|
1695
|
+
---
|
|
1696
|
+
\u{1F4A1} **Tips**
|
|
1697
|
+
- Pull an advertiser's ads: use \`google_ads_page_intel\` with \`advertiserId\`
|
|
1698
|
+
- Or pass a \`domain\` directly to \`google_ads_page_intel\``
|
|
1699
|
+
].join("\n");
|
|
1700
|
+
return {
|
|
1701
|
+
...oneBlock(full),
|
|
1702
|
+
structuredContent: {
|
|
1703
|
+
query: input.query,
|
|
1704
|
+
region: d.region ?? "US",
|
|
1705
|
+
advertiserCount: advertisers.length,
|
|
1706
|
+
advertisers: advertisers.map((a) => ({
|
|
1707
|
+
advertiserId: a.advertiserId ?? null,
|
|
1708
|
+
name: a.name ?? null,
|
|
1709
|
+
domain: a.domain ?? null,
|
|
1710
|
+
approxAdCount: typeof a.approxAdCount === "number" ? a.approxAdCount : null,
|
|
1711
|
+
detailUrl: a.detailUrl ?? null
|
|
1712
|
+
}))
|
|
1713
|
+
}
|
|
1714
|
+
};
|
|
1715
|
+
}
|
|
1716
|
+
function formatGoogleAdsPageIntel(raw, input) {
|
|
1717
|
+
const parsed = parseData(raw);
|
|
1718
|
+
if ("error" in parsed) return { content: [{ type: "text", text: parsed.error }], isError: true };
|
|
1719
|
+
const d = parsed.data;
|
|
1720
|
+
const ads = d.ads ?? [];
|
|
1721
|
+
const rows = ads.map((a, i) => {
|
|
1722
|
+
const media = a.youtubeVideoId ? `yt:${a.youtubeVideoId}` : a.videoUrl ? "video" : a.imageUrls.length ? `${a.imageUrls.length} img` : "\u2014";
|
|
1723
|
+
return `| ${i + 1} | ${cell(a.format)} | ${media} | \`${a.creativeId ?? "\u2014"}\` |`;
|
|
1724
|
+
}).join("\n");
|
|
1725
|
+
const label = d.advertiserName ?? input.advertiserId ?? input.domain ?? "";
|
|
1726
|
+
const full = [
|
|
1727
|
+
`# Google Ads Transparency: ${label}`,
|
|
1728
|
+
`**${ads.length} creatives** \xB7 ${d.videoCount ?? 0} video \xB7 ${d.imageCount ?? 0} image \xB7 ${d.textCount ?? 0} text${d.region ? ` \xB7 ${d.region}` : ""}`,
|
|
1729
|
+
`
|
|
1730
|
+
## Creatives
|
|
1731
|
+
| # | Format | Media | Creative ID |
|
|
1732
|
+
|---|--------|-------|-------------|
|
|
1733
|
+
${rows}`,
|
|
1734
|
+
`
|
|
1735
|
+
---
|
|
1736
|
+
\u{1F4A1} **Tips**
|
|
1737
|
+
- Transcribe a video ad: pass its \`videoUrl\` to \`google_ads_transcribe\`, or its \`youtubeVideoId\` to \`youtube_transcribe\`
|
|
1738
|
+
- Full ad detail is at each creative's \`detailUrl\``
|
|
1739
|
+
].join("\n");
|
|
1740
|
+
return {
|
|
1741
|
+
...oneBlock(full),
|
|
1742
|
+
structuredContent: {
|
|
1743
|
+
advertiserId: d.advertiserId ?? null,
|
|
1744
|
+
advertiserName: d.advertiserName ?? null,
|
|
1745
|
+
domain: d.domain ?? null,
|
|
1746
|
+
region: d.region ?? "US",
|
|
1747
|
+
totalCreatives: typeof d.totalCreatives === "number" ? d.totalCreatives : ads.length,
|
|
1748
|
+
videoCount: d.videoCount ?? 0,
|
|
1749
|
+
imageCount: d.imageCount ?? 0,
|
|
1750
|
+
textCount: d.textCount ?? 0,
|
|
1751
|
+
ads: ads.map((a) => ({
|
|
1752
|
+
creativeId: a.creativeId ?? null,
|
|
1753
|
+
advertiserId: a.advertiserId ?? null,
|
|
1754
|
+
format: a.format ?? null,
|
|
1755
|
+
lastShown: a.lastShown ?? null,
|
|
1756
|
+
detailUrl: a.detailUrl ?? null,
|
|
1757
|
+
landingDomain: a.landingDomain ?? null,
|
|
1758
|
+
imageUrls: Array.isArray(a.imageUrls) ? a.imageUrls : [],
|
|
1759
|
+
youtubeVideoId: a.youtubeVideoId ?? null,
|
|
1760
|
+
videoUrl: a.videoUrl ?? null,
|
|
1761
|
+
variations: typeof a.variations === "number" ? a.variations : null
|
|
1762
|
+
}))
|
|
1763
|
+
}
|
|
1764
|
+
};
|
|
1765
|
+
}
|
|
1766
|
+
function formatGoogleAdsTranscribe(raw, input) {
|
|
1767
|
+
const parsed = parseData(raw);
|
|
1768
|
+
if ("error" in parsed) return { content: [{ type: "text", text: parsed.error }], isError: true };
|
|
1769
|
+
const d = parsed.data;
|
|
1770
|
+
const text = d.text ?? "";
|
|
1771
|
+
const chunks = d.chunks ?? [];
|
|
1772
|
+
const durSec = d.durationMs ? (d.durationMs / 1e3).toFixed(0) : "\u2014";
|
|
1773
|
+
const words = wordCount(text);
|
|
1774
|
+
const chunkRows = chunks.slice(0, 50).map((c) => {
|
|
1775
|
+
const sec = Number.isFinite(c.timestamp[0]) ? Math.floor(c.timestamp[0]) : 0;
|
|
1776
|
+
const mm = String(Math.floor(sec / 60)).padStart(2, "0");
|
|
1777
|
+
const ss = String(sec % 60).padStart(2, "0");
|
|
1778
|
+
return `| ${mm}:${ss} | ${cell(truncate(c.text, 120))} |`;
|
|
1779
|
+
}).join("\n");
|
|
1780
|
+
const full = [
|
|
1781
|
+
`# Google Ad Transcript`,
|
|
1782
|
+
`**Duration:** ${durSec}s \xB7 **${words} words**`,
|
|
1783
|
+
`
|
|
1784
|
+
## Full Transcript
|
|
1785
|
+
${text}`,
|
|
1786
|
+
chunks.length ? `
|
|
1787
|
+
## Timestamped Chunks
|
|
1788
|
+
| Time | Text |
|
|
1789
|
+
|------|------|
|
|
1790
|
+
${chunkRows}` : "",
|
|
1791
|
+
`
|
|
1792
|
+
---
|
|
1793
|
+
\u{1F4A1} Get more ads from this advertiser: use \`google_ads_page_intel\`.`
|
|
1794
|
+
].filter(Boolean).join("\n");
|
|
1795
|
+
return {
|
|
1796
|
+
...oneBlock(full),
|
|
1797
|
+
structuredContent: {
|
|
1798
|
+
videoUrl: input.videoUrl,
|
|
1799
|
+
wordCount: words,
|
|
1800
|
+
chunkCount: chunks.length,
|
|
1801
|
+
durationMs: typeof d.durationMs === "number" ? d.durationMs : null,
|
|
1802
|
+
transcriptText: text,
|
|
1803
|
+
chunks: structuredTranscriptChunks(chunks),
|
|
1804
|
+
resolvedInputs: { videoUrl: input.videoUrl }
|
|
1805
|
+
}
|
|
1806
|
+
};
|
|
1807
|
+
}
|
|
1667
1808
|
function normalizeMapsAttempts(value) {
|
|
1668
1809
|
const attempts = Array.isArray(value) ? value : [];
|
|
1669
1810
|
return attempts.map((attempt, index) => ({
|
|
@@ -2719,6 +2860,20 @@ var FacebookVideoTranscribeInputSchema = {
|
|
|
2719
2860
|
url: z.string().url().describe("Organic Facebook reel, video, watch, post, or share URL from facebook.com, m.facebook.com, or fb.watch. The tool renders the page, extracts the best matching public Facebook CDN MP4 URL, then transcribes it. Use this when the user pastes a normal Facebook video page URL and asks for the transcript or downloadable MP4."),
|
|
2720
2861
|
quality: z.enum(["best", "hd", "sd"]).default("best").describe("Preferred progressive MP4 quality. Use best by default; hd prefers the highest HD progressive URL; sd forces the SD URL.")
|
|
2721
2862
|
};
|
|
2863
|
+
var GoogleAdsSearchInputSchema = {
|
|
2864
|
+
query: z.string().min(1).describe("A domain (e.g. getviktor.com) or advertiser/brand name to look up in the Google Ads Transparency Center. Domains resolve to the advertisers running ads that point to that site."),
|
|
2865
|
+
region: z.string().length(2).default("US").describe("Two-letter region code for where the ads are shown. Default US. Examples: US, CA, GB, AU."),
|
|
2866
|
+
maxResults: z.number().int().min(1).max(20).default(10).describe("Maximum advertisers to return. Default 10, maximum 20.")
|
|
2867
|
+
};
|
|
2868
|
+
var GoogleAdsPageIntelInputSchema = {
|
|
2869
|
+
advertiserId: z.string().optional().describe("Google Ads Transparency advertiser ID (starts with AR...). Use one returned by google_ads_search; do not construct one yourself."),
|
|
2870
|
+
domain: z.string().optional().describe("A domain (e.g. getviktor.com) whose primary advertiser to inspect when advertiserId is unknown. One of advertiserId or domain is required."),
|
|
2871
|
+
region: z.string().length(2).default("US").describe("Two-letter region code for where the ads are shown. Default US."),
|
|
2872
|
+
maxAds: z.number().int().min(1).max(200).default(50).describe("Maximum creatives to inspect and hydrate. Default 50, maximum 200. Prefer 25-50 for focused scans.")
|
|
2873
|
+
};
|
|
2874
|
+
var GoogleAdsTranscribeInputSchema = {
|
|
2875
|
+
videoUrl: z.string().url().describe("Direct googlevideo.com playback URL from a google_ads_page_intel video ad result (the videoUrl field). For YouTube-hosted ads use youtube_transcribe with the returned youtubeVideoId instead.")
|
|
2876
|
+
};
|
|
2722
2877
|
var InstagramProfileContentInputSchema = {
|
|
2723
2878
|
handle: z.string().min(1).optional().describe("Instagram handle, with or without @. Provide handle or url."),
|
|
2724
2879
|
url: z.string().url().optional().describe("Instagram profile URL, e.g. https://www.instagram.com/nasaartemis/. Provide handle or url."),
|
|
@@ -2929,7 +3084,8 @@ var OrganicResultOutput = z.object({
|
|
|
2929
3084
|
});
|
|
2930
3085
|
var AiOverviewOutput = z.object({
|
|
2931
3086
|
detected: z.boolean(),
|
|
2932
|
-
text: NullableString
|
|
3087
|
+
text: NullableString,
|
|
3088
|
+
shareUrl: NullableString.optional()
|
|
2933
3089
|
}).nullable();
|
|
2934
3090
|
var EntityIdsOutput = z.object({
|
|
2935
3091
|
entities: z.array(z.object({
|
|
@@ -3163,6 +3319,40 @@ var FacebookPageIntelOutputSchema = {
|
|
|
3163
3319
|
variations: z.number().int().nullable()
|
|
3164
3320
|
}))
|
|
3165
3321
|
};
|
|
3322
|
+
var GoogleAdsSearchOutputSchema = {
|
|
3323
|
+
query: z.string(),
|
|
3324
|
+
region: z.string(),
|
|
3325
|
+
advertiserCount: z.number().int().min(0),
|
|
3326
|
+
advertisers: z.array(z.object({
|
|
3327
|
+
advertiserId: NullableString,
|
|
3328
|
+
name: NullableString,
|
|
3329
|
+
domain: NullableString,
|
|
3330
|
+
approxAdCount: z.number().int().nullable(),
|
|
3331
|
+
detailUrl: NullableString
|
|
3332
|
+
}))
|
|
3333
|
+
};
|
|
3334
|
+
var GoogleAdsPageIntelOutputSchema = {
|
|
3335
|
+
advertiserId: NullableString,
|
|
3336
|
+
advertiserName: NullableString,
|
|
3337
|
+
domain: NullableString,
|
|
3338
|
+
region: z.string(),
|
|
3339
|
+
totalCreatives: z.number().int().min(0),
|
|
3340
|
+
videoCount: z.number().int().min(0),
|
|
3341
|
+
imageCount: z.number().int().min(0),
|
|
3342
|
+
textCount: z.number().int().min(0),
|
|
3343
|
+
ads: z.array(z.object({
|
|
3344
|
+
creativeId: NullableString,
|
|
3345
|
+
advertiserId: NullableString,
|
|
3346
|
+
format: NullableString,
|
|
3347
|
+
lastShown: NullableString,
|
|
3348
|
+
detailUrl: NullableString,
|
|
3349
|
+
landingDomain: NullableString,
|
|
3350
|
+
imageUrls: z.array(z.string()),
|
|
3351
|
+
youtubeVideoId: NullableString,
|
|
3352
|
+
videoUrl: NullableString,
|
|
3353
|
+
variations: z.number().int().nullable()
|
|
3354
|
+
}))
|
|
3355
|
+
};
|
|
3166
3356
|
var FacebookVideoTranscribeOutputSchema = {
|
|
3167
3357
|
sourceUrl: z.string().url(),
|
|
3168
3358
|
pageUrl: z.string().url(),
|
|
@@ -3305,6 +3495,17 @@ var FacebookAdTranscribeOutputSchema = {
|
|
|
3305
3495
|
videoUrl: z.string().url()
|
|
3306
3496
|
})
|
|
3307
3497
|
};
|
|
3498
|
+
var GoogleAdsTranscribeOutputSchema = {
|
|
3499
|
+
videoUrl: z.string().url(),
|
|
3500
|
+
wordCount: z.number().int().min(0),
|
|
3501
|
+
chunkCount: z.number().int().min(0),
|
|
3502
|
+
durationMs: z.number().nullable(),
|
|
3503
|
+
transcriptText: z.string(),
|
|
3504
|
+
chunks: z.array(TranscriptChunkOutput),
|
|
3505
|
+
resolvedInputs: z.object({
|
|
3506
|
+
videoUrl: z.string().url()
|
|
3507
|
+
})
|
|
3508
|
+
};
|
|
3308
3509
|
var CaptureSerpSnapshotOutputSchema = {
|
|
3309
3510
|
schemaVersion: z.literal("serp-intelligence.capture.v1"),
|
|
3310
3511
|
status: z.string(),
|
|
@@ -3954,6 +4155,27 @@ function registerPaaExtractorMcpTools(server, executor, options = {}) {
|
|
|
3954
4155
|
outputSchema: FacebookAdTranscribeOutputSchema,
|
|
3955
4156
|
annotations: liveWebToolAnnotations("Facebook Ad Transcription")
|
|
3956
4157
|
}, async (input) => formatFacebookAdTranscribe(await executor.facebookAdTranscribe(input), input));
|
|
4158
|
+
server.registerTool("google_ads_search", {
|
|
4159
|
+
title: "Google Ads Transparency Search",
|
|
4160
|
+
description: withReportNote("Search the Google Ads Transparency Center to find advertisers running Google ads (Search, Display, YouTube) by domain or brand/advertiser name. Pass a domain like getviktor.com to see every advertiser account pointing ads at that site, or an advertiser name. Returns advertisers with their advertiser ID and an approximate ad count. Use this to discover an advertiserId, then pass it (or the domain) to google_ads_page_intel for the actual ad creatives."),
|
|
4161
|
+
inputSchema: GoogleAdsSearchInputSchema,
|
|
4162
|
+
outputSchema: GoogleAdsSearchOutputSchema,
|
|
4163
|
+
annotations: liveWebToolAnnotations("Google Ads Transparency Search")
|
|
4164
|
+
}, async (input) => formatGoogleAdsSearch(await executor.googleAdsSearch(input), input));
|
|
4165
|
+
server.registerTool("google_ads_page_intel", {
|
|
4166
|
+
title: "Google Ads Advertiser Intel",
|
|
4167
|
+
description: withReportNote("Harvest an advertiser's ads from the Google Ads Transparency Center when the user wants a competitor's Google/YouTube ad creatives, copy angles, image URLs, or video ads. Accepts an advertiserId (from google_ads_search) or a domain directly. Returns each creative's format (text/image/video), image URLs, landing info, a detail URL, and \u2014 for video ads \u2014 the YouTube video ID and/or a direct video URL ready for transcription. Use google_ads_search first when you only have a brand name. Transcribe video ads with google_ads_transcribe (video URL) or youtube_transcribe (YouTube ID)."),
|
|
4168
|
+
inputSchema: GoogleAdsPageIntelInputSchema,
|
|
4169
|
+
outputSchema: GoogleAdsPageIntelOutputSchema,
|
|
4170
|
+
annotations: liveWebToolAnnotations("Google Ads Advertiser Intel")
|
|
4171
|
+
}, async (input) => formatGoogleAdsPageIntel(await executor.googleAdsPageIntel(input), input));
|
|
4172
|
+
server.registerTool("google_ads_transcribe", {
|
|
4173
|
+
title: "Google Ad Video Transcription",
|
|
4174
|
+
description: "Transcribe audio from a Google video ad. Use this when google_ads_page_intel returned a direct videoUrl (a googlevideo.com playback URL) for a video creative and the user asks what the ad says or wants its claims/messaging. Returns full transcript, timestamped chunks, and word count. For YouTube-hosted ads, pass the returned youtubeVideoId to youtube_transcribe instead; run google_ads_page_intel first when you only have an advertiser/domain.",
|
|
4175
|
+
inputSchema: GoogleAdsTranscribeInputSchema,
|
|
4176
|
+
outputSchema: GoogleAdsTranscribeOutputSchema,
|
|
4177
|
+
annotations: liveWebToolAnnotations("Google Ad Video Transcription")
|
|
4178
|
+
}, async (input) => formatGoogleAdsTranscribe(await executor.googleAdsTranscribe(input), input));
|
|
3957
4179
|
server.registerTool("facebook_video_transcribe", {
|
|
3958
4180
|
title: "Facebook Organic Video Transcription",
|
|
3959
4181
|
description: withReportNote("Transcribe audio from an organic Facebook reel, video, watch, post, or share URL, including fb.watch links. Use this when the user pastes a normal Facebook video page URL and wants the transcript or downloadable MP4. Renders the Facebook page in a browser, selects the best matching public Facebook CDN MP4 URL from page state, then returns sourceUrl, resolved pageUrl, videoId, ownerName, selectedQuality, bitrate, videoDurationSec, extracted MP4 URL, full transcript, and timestamped chunks."),
|
|
@@ -4273,6 +4495,15 @@ var HttpMcpToolExecutor = class {
|
|
|
4273
4495
|
facebookVideoTranscribe(input) {
|
|
4274
4496
|
return this.call("/facebook/video-transcribe", input, this.httpTimeoutOverrideMs ?? 24e4);
|
|
4275
4497
|
}
|
|
4498
|
+
googleAdsSearch(input) {
|
|
4499
|
+
return this.call("/google-ads/search", input, this.httpTimeoutOverrideMs ?? 24e4);
|
|
4500
|
+
}
|
|
4501
|
+
googleAdsPageIntel(input) {
|
|
4502
|
+
return this.call("/google-ads/page-intel", input, this.httpTimeoutOverrideMs ?? 3e5);
|
|
4503
|
+
}
|
|
4504
|
+
googleAdsTranscribe(input) {
|
|
4505
|
+
return this.call("/google-ads/transcribe", input, this.httpTimeoutOverrideMs ?? 24e4);
|
|
4506
|
+
}
|
|
4276
4507
|
instagramProfileContent(input) {
|
|
4277
4508
|
return this.call("/instagram/profile-content", withDefaultHostedProfile(input), this.httpTimeoutOverrideMs ?? 24e4);
|
|
4278
4509
|
}
|
|
@@ -5817,4 +6048,4 @@ export {
|
|
|
5817
6048
|
exportFanout,
|
|
5818
6049
|
registerBrowserAgentMcpTools
|
|
5819
6050
|
};
|
|
5820
|
-
//# sourceMappingURL=chunk-
|
|
6051
|
+
//# sourceMappingURL=chunk-QLQYNE7E.js.map
|