mcp-scraper 0.3.27 → 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 +2475 -1154
- 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 +346 -7
- package/dist/bin/mcp-stdio-server.cjs.map +1 -1
- package/dist/bin/mcp-stdio-server.js +7 -4
- package/dist/bin/mcp-stdio-server.js.map +1 -1
- 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-6KHGYJIT.js → chunk-QLQYNE7E.js} +255 -14
- 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-KUNOK47B.js → server-HB7WJO3N.js} +1155 -274
- 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-6KHGYJIT.js.map +0 -1
- package/dist/chunk-DYV72F6A.js +0 -7
- package/dist/chunk-DYV72F6A.js.map +0 -1
- package/dist/chunk-H74L743B.js.map +0 -1
- package/dist/chunk-QSSH4RCX.js.map +0 -1
- package/dist/chunk-TRNSXR46.js.map +0 -1
- package/dist/server-KUNOK47B.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
|
@@ -3,14 +3,15 @@ import {
|
|
|
3
3
|
HttpMcpToolExecutor,
|
|
4
4
|
SERVER_INSTRUCTIONS,
|
|
5
5
|
registerBrowserAgentMcpTools,
|
|
6
|
-
registerPaaExtractorMcpTools
|
|
7
|
-
|
|
6
|
+
registerPaaExtractorMcpTools,
|
|
7
|
+
registerSerpIntelligenceCaptureTools
|
|
8
|
+
} from "../chunk-QLQYNE7E.js";
|
|
8
9
|
import {
|
|
9
10
|
renderInstallTerminal
|
|
10
11
|
} from "../chunk-T3VZD2I4.js";
|
|
11
12
|
import {
|
|
12
13
|
PACKAGE_VERSION
|
|
13
|
-
} from "../chunk-
|
|
14
|
+
} from "../chunk-I5EFBTYE.js";
|
|
14
15
|
import "../chunk-3PRO376E.js";
|
|
15
16
|
import "../chunk-BP27CZ5Q.js";
|
|
16
17
|
|
|
@@ -52,7 +53,9 @@ if (!apiKey) {
|
|
|
52
53
|
var baseUrl = process.env.MCP_SCRAPER_BASE_URL?.trim() || process.env.MCP_BASE_URL?.trim() || "https://mcpscraper.dev";
|
|
53
54
|
var consoleBaseUrl = process.env.BROWSER_AGENT_CONSOLE_URL?.trim() || baseUrl;
|
|
54
55
|
var server = new McpServer({ name: "mcp-scraper", version: PACKAGE_VERSION }, { instructions: SERVER_INSTRUCTIONS });
|
|
55
|
-
|
|
56
|
+
var httpExecutor = new HttpMcpToolExecutor(baseUrl, apiKey);
|
|
57
|
+
registerPaaExtractorMcpTools(server, httpExecutor);
|
|
58
|
+
registerSerpIntelligenceCaptureTools(server, httpExecutor);
|
|
56
59
|
registerBrowserAgentMcpTools(server, { baseUrl, apiKey, consoleBaseUrl });
|
|
57
60
|
var transport = new StdioServerTransport();
|
|
58
61
|
async function main() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../bin/mcp-stdio-server.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { readFileSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { join } from 'node:path'\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'\nimport { HttpMcpToolExecutor } from '../src/mcp/http-mcp-tool-executor.js'\nimport { registerBrowserAgentMcpTools } from '../src/mcp/browser-agent-mcp-server.js'\nimport { registerPaaExtractorMcpTools } from '../src/mcp/paa-mcp-server.js'\nimport { SERVER_INSTRUCTIONS } from '../src/mcp/server-instructions.js'\nimport { renderInstallTerminal } from '../src/install-terminal.js'\nimport { PACKAGE_VERSION } from '../src/version.js'\n\nconst forceStdio =\n process.argv.includes('--stdio') ||\n process.env.MCP_SCRAPER_FORCE_STDIO === '1'\nconst interactiveTerminal = Boolean(process.stdin.isTTY && process.stdout.isTTY)\nconst wantsHelp = process.argv.includes('--help') || process.argv.includes('-h')\n\nif (!forceStdio && (interactiveTerminal || wantsHelp)) {\n const noColor =\n process.argv.includes('--no-color') ||\n process.env.NO_COLOR !== undefined ||\n process.env.FORCE_COLOR === '0' ||\n !process.stdout.isTTY\n\n process.stdout.write(renderInstallTerminal({\n version: PACKAGE_VERSION,\n color: !noColor,\n apiKeyConfigured: Boolean(process.env.MCP_SCRAPER_API_KEY?.trim()),\n }))\n process.exit(0)\n}\n\nfunction readApiKeyFile(): string | undefined {\n const explicitPath = process.env.MCP_SCRAPER_KEY_PATH?.trim()\n const paths = [explicitPath, join(homedir(), '.mcp-scraper-key')].filter(Boolean) as string[]\n for (const path of paths) {\n try {\n const value = readFileSync(path, 'utf8').trim()\n if (value) return value\n } catch {\n void 0\n }\n }\n return undefined\n}\n\nconst apiKey = (\n process.env.MCP_SCRAPER_API_KEY ??\n process.env.MCP_SCRAPER_KEY ??\n process.env.MCP_API_KEY ??\n readApiKeyFile()\n)?.trim()\nif (!apiKey) {\n process.stderr.write('MCP_SCRAPER_API_KEY env var or ~/.mcp-scraper-key is required\\n')\n process.exit(1)\n}\n\nconst baseUrl = process.env.MCP_SCRAPER_BASE_URL?.trim() || process.env.MCP_BASE_URL?.trim() || 'https://mcpscraper.dev'\nconst consoleBaseUrl = process.env.BROWSER_AGENT_CONSOLE_URL?.trim() || baseUrl\nconst server = new McpServer({ name: 'mcp-scraper', version: PACKAGE_VERSION }, { instructions: SERVER_INSTRUCTIONS })\n\
|
|
1
|
+
{"version":3,"sources":["../../bin/mcp-stdio-server.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { readFileSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { join } from 'node:path'\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'\nimport { HttpMcpToolExecutor } from '../src/mcp/http-mcp-tool-executor.js'\nimport { registerBrowserAgentMcpTools } from '../src/mcp/browser-agent-mcp-server.js'\nimport { registerPaaExtractorMcpTools, registerSerpIntelligenceCaptureTools } from '../src/mcp/paa-mcp-server.js'\nimport { SERVER_INSTRUCTIONS } from '../src/mcp/server-instructions.js'\nimport { renderInstallTerminal } from '../src/install-terminal.js'\nimport { PACKAGE_VERSION } from '../src/version.js'\n\nconst forceStdio =\n process.argv.includes('--stdio') ||\n process.env.MCP_SCRAPER_FORCE_STDIO === '1'\nconst interactiveTerminal = Boolean(process.stdin.isTTY && process.stdout.isTTY)\nconst wantsHelp = process.argv.includes('--help') || process.argv.includes('-h')\n\nif (!forceStdio && (interactiveTerminal || wantsHelp)) {\n const noColor =\n process.argv.includes('--no-color') ||\n process.env.NO_COLOR !== undefined ||\n process.env.FORCE_COLOR === '0' ||\n !process.stdout.isTTY\n\n process.stdout.write(renderInstallTerminal({\n version: PACKAGE_VERSION,\n color: !noColor,\n apiKeyConfigured: Boolean(process.env.MCP_SCRAPER_API_KEY?.trim()),\n }))\n process.exit(0)\n}\n\nfunction readApiKeyFile(): string | undefined {\n const explicitPath = process.env.MCP_SCRAPER_KEY_PATH?.trim()\n const paths = [explicitPath, join(homedir(), '.mcp-scraper-key')].filter(Boolean) as string[]\n for (const path of paths) {\n try {\n const value = readFileSync(path, 'utf8').trim()\n if (value) return value\n } catch {\n void 0\n }\n }\n return undefined\n}\n\nconst apiKey = (\n process.env.MCP_SCRAPER_API_KEY ??\n process.env.MCP_SCRAPER_KEY ??\n process.env.MCP_API_KEY ??\n readApiKeyFile()\n)?.trim()\nif (!apiKey) {\n process.stderr.write('MCP_SCRAPER_API_KEY env var or ~/.mcp-scraper-key is required\\n')\n process.exit(1)\n}\n\nconst baseUrl = process.env.MCP_SCRAPER_BASE_URL?.trim() || process.env.MCP_BASE_URL?.trim() || 'https://mcpscraper.dev'\nconst consoleBaseUrl = process.env.BROWSER_AGENT_CONSOLE_URL?.trim() || baseUrl\nconst server = new McpServer({ name: 'mcp-scraper', version: PACKAGE_VERSION }, { instructions: SERVER_INSTRUCTIONS })\n\nconst httpExecutor = new HttpMcpToolExecutor(baseUrl, apiKey)\nregisterPaaExtractorMcpTools(server, httpExecutor)\nregisterSerpIntelligenceCaptureTools(server, httpExecutor)\nregisterBrowserAgentMcpTools(server, { baseUrl, apiKey, consoleBaseUrl })\n\nconst transport = new StdioServerTransport()\n\nasync function main() {\n await server.connect(transport)\n}\n\nmain().catch(err => {\n process.stderr.write(`${err instanceof Error ? err.message : String(err)}\\n`)\n process.exit(1)\n})\n"],"mappings":";;;;;;;;;;;;;;;;;;AACA,SAAS,oBAAoB;AAC7B,SAAS,eAAe;AACxB,SAAS,YAAY;AACrB,SAAS,iBAAiB;AAC1B,SAAS,4BAA4B;AAQrC,IAAM,aACJ,QAAQ,KAAK,SAAS,SAAS,KAC/B,QAAQ,IAAI,4BAA4B;AAC1C,IAAM,sBAAsB,QAAQ,QAAQ,MAAM,SAAS,QAAQ,OAAO,KAAK;AAC/E,IAAM,YAAY,QAAQ,KAAK,SAAS,QAAQ,KAAK,QAAQ,KAAK,SAAS,IAAI;AAE/E,IAAI,CAAC,eAAe,uBAAuB,YAAY;AACrD,QAAM,UACJ,QAAQ,KAAK,SAAS,YAAY,KAClC,QAAQ,IAAI,aAAa,UACzB,QAAQ,IAAI,gBAAgB,OAC5B,CAAC,QAAQ,OAAO;AAElB,UAAQ,OAAO,MAAM,sBAAsB;AAAA,IACzC,SAAS;AAAA,IACT,OAAO,CAAC;AAAA,IACR,kBAAkB,QAAQ,QAAQ,IAAI,qBAAqB,KAAK,CAAC;AAAA,EACnE,CAAC,CAAC;AACF,UAAQ,KAAK,CAAC;AAChB;AAEA,SAAS,iBAAqC;AAC5C,QAAM,eAAe,QAAQ,IAAI,sBAAsB,KAAK;AAC5D,QAAM,QAAQ,CAAC,cAAc,KAAK,QAAQ,GAAG,kBAAkB,CAAC,EAAE,OAAO,OAAO;AAChF,aAAW,QAAQ,OAAO;AACxB,QAAI;AACF,YAAM,QAAQ,aAAa,MAAM,MAAM,EAAE,KAAK;AAC9C,UAAI,MAAO,QAAO;AAAA,IACpB,QAAQ;AAAA,IAER;AAAA,EACF;AACA,SAAO;AACT;AAEA,IAAM,UACJ,QAAQ,IAAI,uBACZ,QAAQ,IAAI,mBACZ,QAAQ,IAAI,eACZ,eAAe,IACd,KAAK;AACR,IAAI,CAAC,QAAQ;AACX,UAAQ,OAAO,MAAM,iEAAiE;AACtF,UAAQ,KAAK,CAAC;AAChB;AAEA,IAAM,UAAU,QAAQ,IAAI,sBAAsB,KAAK,KAAK,QAAQ,IAAI,cAAc,KAAK,KAAK;AAChG,IAAM,iBAAiB,QAAQ,IAAI,2BAA2B,KAAK,KAAK;AACxE,IAAM,SAAS,IAAI,UAAU,EAAE,MAAM,eAAe,SAAS,gBAAgB,GAAG,EAAE,cAAc,oBAAoB,CAAC;AAErH,IAAM,eAAe,IAAI,oBAAoB,SAAS,MAAM;AAC5D,6BAA6B,QAAQ,YAAY;AACjD,qCAAqC,QAAQ,YAAY;AACzD,6BAA6B,QAAQ,EAAE,SAAS,QAAQ,eAAe,CAAC;AAExE,IAAM,YAAY,IAAI,qBAAqB;AAE3C,eAAe,OAAO;AACpB,QAAM,OAAO,QAAQ,SAAS;AAChC;AAEA,KAAK,EAAE,MAAM,SAAO;AAClB,UAAQ,OAAO,MAAM,GAAG,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,CAAI;AAC5E,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":[]}
|
package/dist/bin/paa-harvest.cjs
CHANGED
|
@@ -1587,6 +1587,13 @@ var PAAExtractor = class {
|
|
|
1587
1587
|
}
|
|
1588
1588
|
driver;
|
|
1589
1589
|
reporter;
|
|
1590
|
+
completeness = {
|
|
1591
|
+
paaWithoutAnswer: 0,
|
|
1592
|
+
paaWithoutSource: 0,
|
|
1593
|
+
paaAnswersRecovered: 0,
|
|
1594
|
+
aioShareCaptured: null
|
|
1595
|
+
};
|
|
1596
|
+
aioShareUrlEarly = null;
|
|
1590
1597
|
normalizeQuestion(q) {
|
|
1591
1598
|
return q.toLowerCase().replace(/[^\w\s]/g, "").replace(/\s+/g, " ").trim();
|
|
1592
1599
|
}
|
|
@@ -1715,6 +1722,7 @@ var PAAExtractor = class {
|
|
|
1715
1722
|
}
|
|
1716
1723
|
}
|
|
1717
1724
|
const itemMap = new Map((await this.extractVisibleItems(page)).map((i) => [i.question, i]));
|
|
1725
|
+
await this.fillIncompleteItems(page, orderedQs, itemMap, options);
|
|
1718
1726
|
for (const q of orderedQs) {
|
|
1719
1727
|
if (results.length >= options.maxQuestions) break;
|
|
1720
1728
|
const key = this.normalizeQuestion(q);
|
|
@@ -1728,8 +1736,52 @@ var PAAExtractor = class {
|
|
|
1728
1736
|
results.push(this.toFlatRow({ question: q, answer: void 0, sourceTitle: void 0, sourceSite: void 0, sourceCite: void 0 }, 1, null, options.query));
|
|
1729
1737
|
}
|
|
1730
1738
|
}
|
|
1739
|
+
this.completeness.paaWithoutAnswer = results.filter((r) => !r.answer).length;
|
|
1740
|
+
this.completeness.paaWithoutSource = results.filter((r) => !r.source_title && !r.source_site && !r.source_cite).length;
|
|
1731
1741
|
return results;
|
|
1732
1742
|
}
|
|
1743
|
+
async fillIncompleteItems(page, orderedQs, itemMap, options) {
|
|
1744
|
+
const kept = new Set(orderedQs.slice(0, options.maxQuestions));
|
|
1745
|
+
const missingBefore = new Set([...kept].filter((q) => !itemMap.get(q)?.answer));
|
|
1746
|
+
if (missingBefore.size === 0) return;
|
|
1747
|
+
for (let round = 0; round < 2; round++) {
|
|
1748
|
+
if (options.softDeadlineMs && Date.now() >= options.softDeadlineMs) break;
|
|
1749
|
+
const stillMissing = [...missingBefore].filter((q) => !itemMap.get(q)?.answer);
|
|
1750
|
+
if (stillMissing.length === 0) break;
|
|
1751
|
+
const reExpanded = await this.reExpandCollapsedItems(page).catch(() => false);
|
|
1752
|
+
await page.waitForTimeout(reExpanded ? 900 : 500);
|
|
1753
|
+
let improved = false;
|
|
1754
|
+
for (const item of await this.extractVisibleItems(page)) {
|
|
1755
|
+
if (!kept.has(item.question)) continue;
|
|
1756
|
+
const existing = itemMap.get(item.question);
|
|
1757
|
+
if (!item.answer || existing?.answer) continue;
|
|
1758
|
+
itemMap.set(item.question, {
|
|
1759
|
+
...item,
|
|
1760
|
+
sourceTitle: item.sourceTitle ?? existing?.sourceTitle,
|
|
1761
|
+
sourceSite: item.sourceSite ?? existing?.sourceSite,
|
|
1762
|
+
sourceCite: item.sourceCite ?? existing?.sourceCite
|
|
1763
|
+
});
|
|
1764
|
+
improved = true;
|
|
1765
|
+
}
|
|
1766
|
+
if (!improved) break;
|
|
1767
|
+
}
|
|
1768
|
+
this.completeness.paaAnswersRecovered = [...missingBefore].filter((q) => itemMap.get(q)?.answer).length;
|
|
1769
|
+
}
|
|
1770
|
+
async reExpandCollapsedItems(page) {
|
|
1771
|
+
const unexpandedSel = `${PAASelectors.item}:not(.${PAASelectors.expandedClass}) ${PAASelectors.clickTarget}`;
|
|
1772
|
+
const count = await page.locator(unexpandedSel).count().catch(() => 0);
|
|
1773
|
+
if (count === 0) return false;
|
|
1774
|
+
for (let i = 0; i < count; i++) {
|
|
1775
|
+
try {
|
|
1776
|
+
const btn = page.locator(unexpandedSel).first();
|
|
1777
|
+
await btn.scrollIntoViewIfNeeded();
|
|
1778
|
+
await btn.click({ force: true });
|
|
1779
|
+
await page.waitForTimeout(300);
|
|
1780
|
+
} catch {
|
|
1781
|
+
}
|
|
1782
|
+
}
|
|
1783
|
+
return true;
|
|
1784
|
+
}
|
|
1733
1785
|
async extractVideos(page) {
|
|
1734
1786
|
const vsels = VideoSelectors;
|
|
1735
1787
|
return page.evaluate((sels) => {
|
|
@@ -2064,12 +2116,58 @@ var PAAExtractor = class {
|
|
|
2064
2116
|
}
|
|
2065
2117
|
return { ...entityIds, entities: records, cids: [...cidSet] };
|
|
2066
2118
|
}
|
|
2067
|
-
async extractAISurfaces(page) {
|
|
2068
|
-
|
|
2119
|
+
async extractAISurfaces(page, options) {
|
|
2120
|
+
const surfaces = await page.evaluate(extractAISurfacesFromDocument, {
|
|
2069
2121
|
aio: AIOverviewSelectors,
|
|
2070
2122
|
aim: AIModeSelectors,
|
|
2071
2123
|
expandWaitMs: 1500
|
|
2072
2124
|
});
|
|
2125
|
+
if (!surfaces.aiOverview.detected) {
|
|
2126
|
+
this.completeness.aioShareCaptured = null;
|
|
2127
|
+
return surfaces;
|
|
2128
|
+
}
|
|
2129
|
+
const shareUrl = this.aioShareUrlEarly ?? await this.captureAIOverviewShareUrl(page, options).catch(() => null);
|
|
2130
|
+
this.completeness.aioShareCaptured = shareUrl !== null;
|
|
2131
|
+
return {
|
|
2132
|
+
...surfaces,
|
|
2133
|
+
aiOverview: { ...surfaces.aiOverview, shareUrl }
|
|
2134
|
+
};
|
|
2135
|
+
}
|
|
2136
|
+
async captureAIOverviewShareUrl(page, options) {
|
|
2137
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
2138
|
+
if (options?.softDeadlineMs && Date.now() >= options.softDeadlineMs) return null;
|
|
2139
|
+
const clicked = await page.evaluate(() => {
|
|
2140
|
+
const heads = Array.from(document.querySelectorAll('h1,h2,h3,[role="heading"],div')).filter((h) => (h.textContent || "").trim() === "AI Overview");
|
|
2141
|
+
for (const head of heads) {
|
|
2142
|
+
let sec = head;
|
|
2143
|
+
for (let up = 0; up < 8 && sec; up++) {
|
|
2144
|
+
const btn = sec.querySelector('[aria-label="Share"]');
|
|
2145
|
+
if (btn) {
|
|
2146
|
+
btn.scrollIntoView({ block: "center" });
|
|
2147
|
+
btn.click();
|
|
2148
|
+
return true;
|
|
2149
|
+
}
|
|
2150
|
+
sec = sec.parentElement;
|
|
2151
|
+
}
|
|
2152
|
+
}
|
|
2153
|
+
return false;
|
|
2154
|
+
}).catch(() => false);
|
|
2155
|
+
if (!clicked) return null;
|
|
2156
|
+
await page.waitForTimeout(4500);
|
|
2157
|
+
const url = await page.evaluate(() => {
|
|
2158
|
+
const w = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT);
|
|
2159
|
+
while (w.nextNode()) {
|
|
2160
|
+
const m = (w.currentNode.textContent || "").match(/https:\/\/share\.google\/aimode\/\S+/);
|
|
2161
|
+
if (m) return m[0];
|
|
2162
|
+
}
|
|
2163
|
+
return null;
|
|
2164
|
+
}).catch(() => null);
|
|
2165
|
+
await page.keyboard.press("Escape").catch(() => {
|
|
2166
|
+
});
|
|
2167
|
+
if (url) return url;
|
|
2168
|
+
await page.waitForTimeout(600);
|
|
2169
|
+
}
|
|
2170
|
+
return null;
|
|
2073
2171
|
}
|
|
2074
2172
|
buildTree(flat, _seed) {
|
|
2075
2173
|
const roots = [];
|
|
@@ -2122,6 +2220,8 @@ var PAAExtractor = class {
|
|
|
2122
2220
|
}
|
|
2123
2221
|
async extract(options, signal) {
|
|
2124
2222
|
const startMs = Date.now();
|
|
2223
|
+
this.completeness = { paaWithoutAnswer: 0, paaWithoutSource: 0, paaAnswersRecovered: 0, aioShareCaptured: null };
|
|
2224
|
+
this.aioShareUrlEarly = null;
|
|
2125
2225
|
const isMobile = options.device === "mobile";
|
|
2126
2226
|
const config = {
|
|
2127
2227
|
headless: options.headless,
|
|
@@ -2160,6 +2260,7 @@ var PAAExtractor = class {
|
|
|
2160
2260
|
this.throwIfAborted(signal);
|
|
2161
2261
|
const page = this.driver.getPage();
|
|
2162
2262
|
await this.throwIfCaptcha(page, "Google SERP");
|
|
2263
|
+
this.aioShareUrlEarly = await this.captureAIOverviewShareUrl(page, options).catch(() => null);
|
|
2163
2264
|
if (options.serpOnly) {
|
|
2164
2265
|
const [organicResults2, localPack2, rawEntityIds2] = await Promise.all([
|
|
2165
2266
|
this.extractOrganicResults(page),
|
|
@@ -2167,7 +2268,7 @@ var PAAExtractor = class {
|
|
|
2167
2268
|
this.extractEntityIds(page)
|
|
2168
2269
|
]);
|
|
2169
2270
|
const entityIds2 = this.mergeLocalPackIntoEntities(rawEntityIds2, localPack2);
|
|
2170
|
-
const aiSurfaces2 = await this.extractAISurfaces(page);
|
|
2271
|
+
const aiSurfaces2 = await this.extractAISurfaces(page, options);
|
|
2171
2272
|
let locationEvidence2 = options.debug ? inferSerpLocationEvidence(canonicalLocation, organicResults2, localPack2) : void 0;
|
|
2172
2273
|
let allOrganic2 = organicResults2;
|
|
2173
2274
|
if ((options.pages ?? 1) >= 2) {
|
|
@@ -2196,6 +2297,7 @@ var PAAExtractor = class {
|
|
|
2196
2297
|
diagnostics: {
|
|
2197
2298
|
completionStatus: "serp_only",
|
|
2198
2299
|
problem: null,
|
|
2300
|
+
completeness: { ...this.completeness },
|
|
2199
2301
|
...options.debug ? { debug: this.buildHarvestDebugSnapshot(options, canonicalLocation, uule, locationEvidence2) } : {}
|
|
2200
2302
|
},
|
|
2201
2303
|
totalQuestions: 0,
|
|
@@ -2239,7 +2341,7 @@ var PAAExtractor = class {
|
|
|
2239
2341
|
locationEvidence2 = inferSerpLocationEvidence(canonicalLocation, noPaaOrganic, localPack);
|
|
2240
2342
|
}
|
|
2241
2343
|
}
|
|
2242
|
-
const aiSurfaces2 = await this.extractAISurfaces(page);
|
|
2344
|
+
const aiSurfaces2 = await this.extractAISurfaces(page, options);
|
|
2243
2345
|
const stats2 = {
|
|
2244
2346
|
seed: options.query,
|
|
2245
2347
|
totalQuestions: 0,
|
|
@@ -2255,6 +2357,7 @@ var PAAExtractor = class {
|
|
|
2255
2357
|
diagnostics: {
|
|
2256
2358
|
completionStatus: "no_paa",
|
|
2257
2359
|
problem: null,
|
|
2360
|
+
completeness: { ...this.completeness },
|
|
2258
2361
|
...options.debug ? { debug: this.buildHarvestDebugSnapshot(options, canonicalLocation, uule, locationEvidence2) } : {}
|
|
2259
2362
|
},
|
|
2260
2363
|
totalQuestions: 0,
|
|
@@ -2274,7 +2377,7 @@ var PAAExtractor = class {
|
|
|
2274
2377
|
}
|
|
2275
2378
|
const flat = await this.runBFS(page, options, signal);
|
|
2276
2379
|
this.throwIfAborted(signal);
|
|
2277
|
-
const aiSurfaces = await this.extractAISurfaces(page);
|
|
2380
|
+
const aiSurfaces = await this.extractAISurfaces(page, options);
|
|
2278
2381
|
const shortVidsParams = new URLSearchParams({ q: options.query, gl: options.gl, hl: options.hl, pws: "0", udm: ShortVideoSelectors.udm });
|
|
2279
2382
|
if (uule) shortVidsParams.set("uule", uule);
|
|
2280
2383
|
let shortVideos = [];
|
|
@@ -2321,6 +2424,7 @@ var PAAExtractor = class {
|
|
|
2321
2424
|
diagnostics: {
|
|
2322
2425
|
completionStatus: "paa_found",
|
|
2323
2426
|
problem: null,
|
|
2427
|
+
completeness: { ...this.completeness },
|
|
2324
2428
|
...diagnosticWarnings.length > 0 ? { warnings: diagnosticWarnings } : {},
|
|
2325
2429
|
...options.debug ? { debug: this.buildHarvestDebugSnapshot(options, canonicalLocation, uule, locationEvidence) } : {}
|
|
2326
2430
|
},
|