mcp-scraper 0.79.2 → 0.79.3
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/CHANGELOG.md +4 -0
- package/README.md +1 -1
- package/dist/{analytics-repository-VEUDEPWI.js → analytics-repository-YOY5KMOD.js} +3 -3
- package/dist/bin/api-server.js +4 -4
- package/dist/bin/mcp-scraper-cli.js +1 -1
- package/dist/bin/mcp-scraper-core.js +7 -7
- package/dist/bin/mcp-scraper-install.js +2 -2
- package/dist/bin/mcp-stdio-server.js +7 -7
- package/dist/bin/paa-harvest.js +3 -3
- package/dist/{chunk-LB2GNACG.js → chunk-3423R23T.js} +3 -3
- package/dist/{chunk-NGLZ6UJR.js → chunk-5UAFBKJR.js} +1 -1
- package/dist/{chunk-B6L73AO4.js → chunk-6HAV7LCE.js} +3 -1
- package/dist/{chunk-JECPQBLZ.js → chunk-7UZAVM4J.js} +138 -11
- package/dist/{chunk-Q5L2OCOQ.js → chunk-C5Z4OFKW.js} +1 -1
- package/dist/{chunk-KAGHJQFO.js → chunk-EQGTEHLZ.js} +1 -1
- package/dist/{chunk-BTQ23KCT.js → chunk-F5GQJWZU.js} +1 -1
- package/dist/{chunk-DUWZQSSJ.js → chunk-HEUMDX4I.js} +1 -1
- package/dist/{chunk-2RTELL6M.js → chunk-IHYG52ZW.js} +1 -1
- package/dist/{chunk-OUBH6XMY.js → chunk-K3Z5AQYE.js} +1 -1
- package/dist/{chunk-WEEAXPWK.js → chunk-KNGTQD6U.js} +207 -36
- package/dist/{chunk-6FL7ZZCF.js → chunk-MVB25X55.js} +1 -1
- package/dist/{chunk-47ZCUBM5.js → chunk-NVUKO5NN.js} +12 -1
- package/dist/{chunk-XUM2LEGU.js → chunk-TCYZ7HMU.js} +3 -3
- package/dist/{chunk-SL7YVA4S.js → chunk-YBR32LOD.js} +1 -1
- package/dist/{chunk-OU4FV2RP.js → chunk-YXNDOQXN.js} +49 -0
- package/dist/{db-TA567JLA.js → db-Z34LPZNR.js} +5 -1
- package/dist/{extract-bundle-I2GNC25L.js → extract-bundle-S3ZGNFML.js} +3 -3
- package/dist/{gmail-service-GGQVJUC3.js → gmail-service-YFS2OV4S.js} +4 -4
- package/dist/index.cjs +205 -34
- package/dist/index.d.cts +35 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.js +3 -3
- package/dist/{lead-list-enrichment-repository-5LEWW4X7.js → lead-list-enrichment-repository-36RPVV6N.js} +2 -2
- package/dist/{location-data-repository-AHKDXTEQ.js → location-data-repository-WPRG62GE.js} +2 -2
- package/dist/{server-5OSHEEVT.js → server-MDWFOUTS.js} +73 -28
- package/dist/{site-extract-repository-KF736IR6.js → site-extract-repository-WAJMIDPJ.js} +2 -2
- package/dist/{worker-2TYCYFPM.js → worker-3LUFQWWJ.js} +5 -5
- package/package.json +1 -1
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
currentCostContext,
|
|
9
9
|
recordKernelSession,
|
|
10
10
|
runWithCostContext
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-5UAFBKJR.js";
|
|
12
12
|
import {
|
|
13
13
|
DEFAULT_PROXY_MODE,
|
|
14
14
|
HarvestOptionsSchema,
|
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
RECAPTCHA_INSTRUCTIONS,
|
|
25
25
|
RequestAbortedError,
|
|
26
26
|
sanitizeVendorName
|
|
27
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-YXNDOQXN.js";
|
|
28
28
|
|
|
29
29
|
// src/driver/BrowserDriver.ts
|
|
30
30
|
import { chromium } from "playwright-extra";
|
|
@@ -2263,17 +2263,22 @@ async function resolveGoogleOutboundLinks(page, values, options = {}) {
|
|
|
2263
2263
|
// src/extractor/PAAExtractor.ts
|
|
2264
2264
|
var PAA_ANSWER_PLACEHOLDER_PATTERN = String.raw`^(?:an error has occurred(?:[.!…]\s*)?(?:please try again later[.!…]?)?|loading|please wait|try again|something went wrong)[.!…\s]*$`;
|
|
2265
2265
|
var PAA_CLICK_EARLIEST_CONFIRMATION_MS = 700;
|
|
2266
|
+
var PAA_CLICK_MID_CONFIRMATION_MS = 1e3;
|
|
2266
2267
|
var PAA_CLICK_ATTEMPT_BUDGET_MS = 1400;
|
|
2268
|
+
var PAA_CLICK_DISPATCH_TIMEOUT_MS = 900;
|
|
2267
2269
|
var PAA_FRONTIER_BATCH_SIZE = 3;
|
|
2270
|
+
var MAX_RETAINED_INTERACTION_OBSERVATIONS = 250;
|
|
2268
2271
|
var PAAExtractor = class {
|
|
2269
|
-
constructor(driver, reporter, onPaaProgress) {
|
|
2272
|
+
constructor(driver, reporter, onPaaProgress, onProviderSession) {
|
|
2270
2273
|
this.driver = driver;
|
|
2271
2274
|
this.reporter = reporter;
|
|
2272
2275
|
this.onPaaProgress = onPaaProgress;
|
|
2276
|
+
this.onProviderSession = onProviderSession;
|
|
2273
2277
|
}
|
|
2274
2278
|
driver;
|
|
2275
2279
|
reporter;
|
|
2276
2280
|
onPaaProgress;
|
|
2281
|
+
onProviderSession;
|
|
2277
2282
|
completeness = {
|
|
2278
2283
|
paaWithoutAnswer: 0,
|
|
2279
2284
|
paaWithoutSource: 0,
|
|
@@ -2288,6 +2293,7 @@ var PAAExtractor = class {
|
|
|
2288
2293
|
clickedQuestions = /* @__PURE__ */ new Set();
|
|
2289
2294
|
paaLifecycle = this.emptyPaaLifecycle(0);
|
|
2290
2295
|
checkpointMaterial = null;
|
|
2296
|
+
interactionObservations = [];
|
|
2291
2297
|
emptyPaaLifecycle(requestedQuestions) {
|
|
2292
2298
|
return {
|
|
2293
2299
|
requestedQuestions,
|
|
@@ -2300,7 +2306,12 @@ var PAAExtractor = class {
|
|
|
2300
2306
|
knownQuestions: 0,
|
|
2301
2307
|
processedQuestions: 0,
|
|
2302
2308
|
failedInteractions: 0,
|
|
2303
|
-
noGrowthConfirmations: 0
|
|
2309
|
+
noGrowthConfirmations: 0,
|
|
2310
|
+
visibleControls: 0,
|
|
2311
|
+
eligibleControls: 0,
|
|
2312
|
+
dispatchedInteractions: 0,
|
|
2313
|
+
confirmedInteractions: 0,
|
|
2314
|
+
unconfirmedInteractions: 0
|
|
2304
2315
|
};
|
|
2305
2316
|
}
|
|
2306
2317
|
emptyLinkDiagnostics() {
|
|
@@ -2562,7 +2573,11 @@ var PAAExtractor = class {
|
|
|
2562
2573
|
phase,
|
|
2563
2574
|
records: this.collectedRows.map((row) => ({ ...row })),
|
|
2564
2575
|
lifecycle: { ...this.paaLifecycle },
|
|
2565
|
-
...this.checkpointMaterial ? { material: structuredClone(this.checkpointMaterial) } : {}
|
|
2576
|
+
...this.checkpointMaterial ? { material: structuredClone(this.checkpointMaterial) } : {},
|
|
2577
|
+
interactions: this.interactionObservations.map((observation) => ({
|
|
2578
|
+
...observation,
|
|
2579
|
+
confirmationSignals: [...observation.confirmationSignals]
|
|
2580
|
+
}))
|
|
2566
2581
|
};
|
|
2567
2582
|
try {
|
|
2568
2583
|
await this.onPaaProgress(snapshot);
|
|
@@ -2643,6 +2658,7 @@ var PAAExtractor = class {
|
|
|
2643
2658
|
const seenQs = /* @__PURE__ */ new Set();
|
|
2644
2659
|
const orderedQs = [];
|
|
2645
2660
|
this.clickedQuestions = /* @__PURE__ */ new Set();
|
|
2661
|
+
this.interactionObservations = [];
|
|
2646
2662
|
const abandonedNeverReclick = /* @__PURE__ */ new Set();
|
|
2647
2663
|
const clickAttempts = /* @__PURE__ */ new Map();
|
|
2648
2664
|
const processedQuestions = /* @__PURE__ */ new Set();
|
|
@@ -2650,6 +2666,7 @@ var PAAExtractor = class {
|
|
|
2650
2666
|
const capturedItems = /* @__PURE__ */ new Map();
|
|
2651
2667
|
const MAX_CLICK_ATTEMPTS = 2;
|
|
2652
2668
|
const EXHAUSTION_CONFIRMATIONS = 3;
|
|
2669
|
+
let batchNumber = 0;
|
|
2653
2670
|
const itemArgs = {
|
|
2654
2671
|
sel: PAASelectors.item,
|
|
2655
2672
|
dataQ: PAASelectors.itemDataQ,
|
|
@@ -2659,12 +2676,30 @@ var PAAExtractor = class {
|
|
|
2659
2676
|
expandedClass: PAASelectors.expandedClass,
|
|
2660
2677
|
clickTarget: PAASelectors.clickTarget,
|
|
2661
2678
|
answerPlaceholderPattern: PAA_ANSWER_PLACEHOLDER_PATTERN,
|
|
2662
|
-
questionsOnly: options.questionsOnly
|
|
2679
|
+
questionsOnly: options.questionsOnly,
|
|
2680
|
+
sourceSel: "a[href]"
|
|
2663
2681
|
};
|
|
2664
2682
|
const readItemStates = () => page.evaluate(
|
|
2665
2683
|
({ sel, dataQ, dataInitQ, questionEl, expandedClass, clickTarget, answerSel, answerPlaceholderPattern, questionsOnly }) => {
|
|
2684
|
+
const cleanVisibleMaterial = (element) => {
|
|
2685
|
+
if (!element) return "";
|
|
2686
|
+
const parts = [];
|
|
2687
|
+
for (const node of element.childNodes) {
|
|
2688
|
+
if (node.nodeType === Node.TEXT_NODE) {
|
|
2689
|
+
const text2 = node.textContent?.trim();
|
|
2690
|
+
if (text2) parts.push(text2);
|
|
2691
|
+
continue;
|
|
2692
|
+
}
|
|
2693
|
+
if (node.nodeType !== Node.ELEMENT_NODE) continue;
|
|
2694
|
+
const child = node;
|
|
2695
|
+
if (child.tagName === "STYLE" || child.tagName === "SCRIPT" || child.getAttribute("aria-hidden") === "true") continue;
|
|
2696
|
+
const text = cleanVisibleMaterial(child);
|
|
2697
|
+
if (text) parts.push(text);
|
|
2698
|
+
}
|
|
2699
|
+
return parts.join(" ").replace(/\s+/g, " ").trim();
|
|
2700
|
+
};
|
|
2666
2701
|
return Array.from(document.querySelectorAll(sel)).map((el, index) => {
|
|
2667
|
-
const answerText = (el.querySelector(answerSel)
|
|
2702
|
+
const answerText = cleanVisibleMaterial(el.querySelector(answerSel)).replace(/An AI Overview is not available for this search/gi, " ").replace(/Can't generate an AI overview right now\.?\s*Try again later\.?/gi, " ").replace(/\bAI Overview\b/gi, " ").replace(/\s+/g, " ").trim();
|
|
2668
2703
|
const expanded = el.classList.contains(expandedClass) || el.querySelector(clickTarget)?.getAttribute("aria-expanded") === "true";
|
|
2669
2704
|
const answerReady = answerText.length >= 10 && !new RegExp(answerPlaceholderPattern, "i").test(answerText);
|
|
2670
2705
|
return {
|
|
@@ -2677,34 +2712,133 @@ var PAAExtractor = class {
|
|
|
2677
2712
|
},
|
|
2678
2713
|
itemArgs
|
|
2679
2714
|
);
|
|
2680
|
-
const expandOneItemSerially = async (q, beforeCount, itemIndex, wasExpanded) => {
|
|
2715
|
+
const expandOneItemSerially = async (q, beforeCount, itemIndex, wasExpanded, controlAttempt, batchNumber2) => {
|
|
2681
2716
|
const clickStartedMs = Date.now();
|
|
2717
|
+
const baseObservation = {
|
|
2718
|
+
id: `${batchNumber2}:${questionIdFor(q)}:${controlAttempt}`,
|
|
2719
|
+
questionId: questionIdFor(q),
|
|
2720
|
+
question: q,
|
|
2721
|
+
batchNumber: batchNumber2,
|
|
2722
|
+
controlAttempt,
|
|
2723
|
+
observedAt: new Date(clickStartedMs).toISOString(),
|
|
2724
|
+
beforeQuestionCount: beforeCount,
|
|
2725
|
+
expandedBefore: wasExpanded
|
|
2726
|
+
};
|
|
2682
2727
|
const clickTarget = itemIndex >= 0 ? page.locator(itemArgs.sel).nth(itemIndex).locator(itemArgs.clickTarget) : null;
|
|
2728
|
+
let dispatchErrorCode = null;
|
|
2683
2729
|
const clicked = clickTarget ? await (async () => {
|
|
2684
2730
|
if (wasExpanded) {
|
|
2685
|
-
await clickTarget.click({ timeout:
|
|
2686
|
-
await page.waitForTimeout(
|
|
2731
|
+
await clickTarget.click({ timeout: PAA_CLICK_DISPATCH_TIMEOUT_MS, force: true });
|
|
2732
|
+
await page.waitForTimeout(120);
|
|
2687
2733
|
}
|
|
2688
|
-
await clickTarget.click({ timeout:
|
|
2734
|
+
await clickTarget.click({ timeout: PAA_CLICK_DISPATCH_TIMEOUT_MS, force: true });
|
|
2689
2735
|
return true;
|
|
2690
|
-
})().catch(() =>
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2736
|
+
})().catch((error) => {
|
|
2737
|
+
dispatchErrorCode = error instanceof Error && (error.name === "TimeoutError" || /timeout/i.test(error.message)) ? "click_ack_timeout" : "click_failed";
|
|
2738
|
+
return false;
|
|
2739
|
+
}) : false;
|
|
2740
|
+
const dispatchDurationMs = Date.now() - clickStartedMs;
|
|
2741
|
+
if (!clicked && dispatchErrorCode !== "click_ack_timeout") {
|
|
2742
|
+
const observation2 = {
|
|
2743
|
+
...baseObservation,
|
|
2744
|
+
dispatched: false,
|
|
2745
|
+
outcome: "dispatch_failed",
|
|
2746
|
+
confirmationSignals: [],
|
|
2747
|
+
confirmationCheckpointMs: null,
|
|
2748
|
+
confirmedAtMs: null,
|
|
2749
|
+
dispatchDurationMs,
|
|
2750
|
+
totalDurationMs: dispatchDurationMs,
|
|
2751
|
+
afterQuestionCount: beforeCount,
|
|
2752
|
+
newQuestionCount: 0,
|
|
2753
|
+
expandedAfter: wasExpanded,
|
|
2754
|
+
answerObserved: false,
|
|
2755
|
+
sourceCount: 0,
|
|
2756
|
+
errorCode: itemIndex < 0 ? "control_missing" : dispatchErrorCode ?? "click_failed"
|
|
2757
|
+
};
|
|
2758
|
+
return { dispatched: false, confirmed: false, observation: observation2 };
|
|
2759
|
+
}
|
|
2760
|
+
let latest = {
|
|
2761
|
+
exists: true,
|
|
2762
|
+
expanded: wasExpanded,
|
|
2763
|
+
answerReady: false,
|
|
2764
|
+
sourceCount: 0,
|
|
2765
|
+
questionCount: beforeCount
|
|
2766
|
+
};
|
|
2767
|
+
let signals = [];
|
|
2768
|
+
let confirmationCheckpointMs = null;
|
|
2769
|
+
let confirmedAtMs = null;
|
|
2770
|
+
let confirmed = false;
|
|
2771
|
+
for (const checkpointMs of [PAA_CLICK_EARLIEST_CONFIRMATION_MS, PAA_CLICK_MID_CONFIRMATION_MS, PAA_CLICK_ATTEMPT_BUDGET_MS]) {
|
|
2772
|
+
const remainingMs = clickStartedMs + checkpointMs - Date.now();
|
|
2773
|
+
if (remainingMs > 0) await page.waitForTimeout(remainingMs);
|
|
2774
|
+
latest = await page.evaluate(
|
|
2775
|
+
({ sel, dataQ, dataInitQ, questionEl, expandedClass, clickTarget: clickTarget2, answerSel, answerPlaceholderPattern, sourceSel, target }) => {
|
|
2776
|
+
const cleanVisibleMaterial = (element) => {
|
|
2777
|
+
if (!element) return "";
|
|
2778
|
+
const parts = [];
|
|
2779
|
+
for (const node of element.childNodes) {
|
|
2780
|
+
if (node.nodeType === Node.TEXT_NODE) {
|
|
2781
|
+
const text2 = node.textContent?.trim();
|
|
2782
|
+
if (text2) parts.push(text2);
|
|
2783
|
+
continue;
|
|
2784
|
+
}
|
|
2785
|
+
if (node.nodeType !== Node.ELEMENT_NODE) continue;
|
|
2786
|
+
const child = node;
|
|
2787
|
+
if (child.tagName === "STYLE" || child.tagName === "SCRIPT" || child.getAttribute("aria-hidden") === "true") continue;
|
|
2788
|
+
const text = cleanVisibleMaterial(child);
|
|
2789
|
+
if (text) parts.push(text);
|
|
2790
|
+
}
|
|
2791
|
+
return parts.join(" ").replace(/\s+/g, " ").trim();
|
|
2792
|
+
};
|
|
2793
|
+
const items = Array.from(document.querySelectorAll(sel));
|
|
2794
|
+
const item = items.find((element) => (element.getAttribute(dataQ) || element.getAttribute(dataInitQ) || element.querySelector(questionEl)?.innerText?.trim()) === target);
|
|
2795
|
+
if (!item) return { exists: false, expanded: false, answerReady: false, sourceCount: 0, questionCount: items.length };
|
|
2796
|
+
const expanded = item.classList.contains(expandedClass) || item.querySelector(clickTarget2)?.getAttribute("aria-expanded") === "true";
|
|
2797
|
+
const answerElement = item.querySelector(answerSel);
|
|
2798
|
+
const answerText = cleanVisibleMaterial(answerElement);
|
|
2799
|
+
const confirmationText = answerText.replace(/An AI Overview is not available for this search/gi, " ").replace(/Can't generate an AI overview right now\.?\s*Try again later\.?/gi, " ").replace(/\bAI Overview\b/gi, " ").replace(/\s+/g, " ").trim();
|
|
2800
|
+
const answerReady = confirmationText.length >= 10 && !new RegExp(answerPlaceholderPattern, "i").test(confirmationText);
|
|
2801
|
+
return {
|
|
2802
|
+
exists: true,
|
|
2803
|
+
expanded,
|
|
2804
|
+
answerReady,
|
|
2805
|
+
sourceCount: answerReady ? answerElement?.querySelectorAll(sourceSel).length ?? 0 : 0,
|
|
2806
|
+
questionCount: items.length
|
|
2807
|
+
};
|
|
2808
|
+
},
|
|
2809
|
+
{ ...itemArgs, target: q }
|
|
2810
|
+
).catch(() => latest);
|
|
2811
|
+
signals = [
|
|
2812
|
+
...latest.expanded ? ["expanded"] : [],
|
|
2813
|
+
...latest.answerReady ? ["answer"] : [],
|
|
2814
|
+
...latest.sourceCount > 0 ? ["source"] : [],
|
|
2815
|
+
...latest.questionCount > beforeCount ? ["question_growth"] : []
|
|
2816
|
+
];
|
|
2817
|
+
if (latest.answerReady || latest.sourceCount > 0 || latest.questionCount > beforeCount) {
|
|
2818
|
+
confirmed = true;
|
|
2819
|
+
confirmationCheckpointMs = checkpointMs;
|
|
2820
|
+
confirmedAtMs = Date.now() - clickStartedMs;
|
|
2821
|
+
break;
|
|
2822
|
+
}
|
|
2823
|
+
}
|
|
2824
|
+
const totalDurationMs = Date.now() - clickStartedMs;
|
|
2825
|
+
const observation = {
|
|
2826
|
+
...baseObservation,
|
|
2827
|
+
dispatched: clicked,
|
|
2828
|
+
outcome: confirmed ? "confirmed" : clicked ? "unconfirmed" : "dispatch_failed",
|
|
2829
|
+
confirmationSignals: signals,
|
|
2830
|
+
confirmationCheckpointMs,
|
|
2831
|
+
confirmedAtMs,
|
|
2832
|
+
dispatchDurationMs,
|
|
2833
|
+
totalDurationMs,
|
|
2834
|
+
afterQuestionCount: latest.questionCount,
|
|
2835
|
+
newQuestionCount: Math.max(0, latest.questionCount - beforeCount),
|
|
2836
|
+
expandedAfter: latest.expanded,
|
|
2837
|
+
answerObserved: latest.answerReady,
|
|
2838
|
+
sourceCount: latest.sourceCount,
|
|
2839
|
+
errorCode: !clicked ? dispatchErrorCode : confirmed ? null : latest.exists ? "confirmation_timeout" : "control_missing"
|
|
2840
|
+
};
|
|
2841
|
+
return { dispatched: clicked, confirmed, observation };
|
|
2708
2842
|
};
|
|
2709
2843
|
let round = 0;
|
|
2710
2844
|
expansion: while (true) {
|
|
@@ -2735,7 +2869,8 @@ var PAAExtractor = class {
|
|
|
2735
2869
|
targetStatus: orderedQs.length >= options.maxQuestions ? "met" : "not_met",
|
|
2736
2870
|
knownQuestions: orderedQs.length,
|
|
2737
2871
|
processedQuestions: processedQuestions.size,
|
|
2738
|
-
failedInteractions: failedInteractions.size
|
|
2872
|
+
failedInteractions: failedInteractions.size,
|
|
2873
|
+
visibleControls: states.length
|
|
2739
2874
|
};
|
|
2740
2875
|
if (discovered) await this.emitProgress("discovered");
|
|
2741
2876
|
if (options.questionsOnly && seenQs.size >= options.maxQuestions) {
|
|
@@ -2783,6 +2918,7 @@ var PAAExtractor = class {
|
|
|
2783
2918
|
const kept = new Set(orderedQs.slice(0, options.maxQuestions));
|
|
2784
2919
|
const clickable = states.filter((state) => kept.has(state.q) && !state.done && (clickAttempts.get(state.q) ?? 0) < MAX_CLICK_ATTEMPTS && !abandonedNeverReclick.has(state.q));
|
|
2785
2920
|
const targets = clickable.slice(-PAA_FRONTIER_BATCH_SIZE);
|
|
2921
|
+
this.paaLifecycle = { ...this.paaLifecycle, eligibleControls: clickable.length };
|
|
2786
2922
|
if (targets.length === 0) {
|
|
2787
2923
|
if (seenQs.size >= options.maxQuestions) {
|
|
2788
2924
|
this.paaLifecycle = {
|
|
@@ -2832,6 +2968,7 @@ var PAAExtractor = class {
|
|
|
2832
2968
|
break;
|
|
2833
2969
|
}
|
|
2834
2970
|
await this.throwIfCaptcha(page, "Google PAA expansion");
|
|
2971
|
+
batchNumber += 1;
|
|
2835
2972
|
for (const target of targets) {
|
|
2836
2973
|
this.reporter.onDepth(++round);
|
|
2837
2974
|
if ((clickAttempts.get(target.q) ?? 0) > 0) {
|
|
@@ -2844,10 +2981,21 @@ var PAAExtractor = class {
|
|
|
2844
2981
|
}
|
|
2845
2982
|
const expansionResults = await Promise.all(targets.map(async (target) => ({
|
|
2846
2983
|
target,
|
|
2847
|
-
result: await expandOneItemSerially(
|
|
2984
|
+
result: await expandOneItemSerially(
|
|
2985
|
+
target.q,
|
|
2986
|
+
states.length,
|
|
2987
|
+
target.index,
|
|
2988
|
+
target.expanded,
|
|
2989
|
+
clickAttempts.get(target.q) ?? 1,
|
|
2990
|
+
batchNumber
|
|
2991
|
+
)
|
|
2848
2992
|
})));
|
|
2849
2993
|
for (const { target, result } of expansionResults) {
|
|
2850
|
-
if (result.dispatched) this.clickedQuestions.add(target.q);
|
|
2994
|
+
if (result.dispatched || result.confirmed) this.clickedQuestions.add(target.q);
|
|
2995
|
+
this.interactionObservations.push(result.observation);
|
|
2996
|
+
}
|
|
2997
|
+
if (this.interactionObservations.length > MAX_RETAINED_INTERACTION_OBSERVATIONS) {
|
|
2998
|
+
this.interactionObservations.splice(0, this.interactionObservations.length - MAX_RETAINED_INTERACTION_OBSERVATIONS);
|
|
2851
2999
|
}
|
|
2852
3000
|
if (!options.questionsOnly) {
|
|
2853
3001
|
const targetQuestions = new Set(targets.map((target) => target.q));
|
|
@@ -2873,7 +3021,10 @@ var PAAExtractor = class {
|
|
|
2873
3021
|
...this.paaLifecycle,
|
|
2874
3022
|
processedQuestions: processedQuestions.size,
|
|
2875
3023
|
failedInteractions: failedInteractions.size,
|
|
2876
|
-
noGrowthConfirmations: 0
|
|
3024
|
+
noGrowthConfirmations: 0,
|
|
3025
|
+
dispatchedInteractions: this.interactionObservations.filter((value) => value.dispatched).length,
|
|
3026
|
+
confirmedInteractions: this.interactionObservations.filter((value) => value.outcome === "confirmed").length,
|
|
3027
|
+
unconfirmedInteractions: this.interactionObservations.filter((value) => value.outcome !== "confirmed").length
|
|
2877
3028
|
};
|
|
2878
3029
|
await this.emitProgress("click_observed");
|
|
2879
3030
|
}
|
|
@@ -3467,6 +3618,18 @@ var PAAExtractor = class {
|
|
|
3467
3618
|
try {
|
|
3468
3619
|
this.throwIfAborted(signal);
|
|
3469
3620
|
await this.driver.launch(config);
|
|
3621
|
+
const providerSession = this.driver.getProviderSessionMetadata?.();
|
|
3622
|
+
if (providerSession?.providerSessionId && this.onProviderSession) {
|
|
3623
|
+
try {
|
|
3624
|
+
await this.onProviderSession(providerSession);
|
|
3625
|
+
} catch (err) {
|
|
3626
|
+
console.warn(JSON.stringify({
|
|
3627
|
+
event: "paa_provider_session_sink_failed",
|
|
3628
|
+
provider: providerSession.provider,
|
|
3629
|
+
message: err instanceof Error ? err.message : String(err)
|
|
3630
|
+
}));
|
|
3631
|
+
}
|
|
3632
|
+
}
|
|
3470
3633
|
this.throwIfAborted(signal);
|
|
3471
3634
|
const locationResolution = options.location ? resolveLocation(options.location) : null;
|
|
3472
3635
|
const canonicalLocation = options.location ? locationResolution?.canonical ?? options.location : null;
|
|
@@ -4602,10 +4765,10 @@ async function cleanupDisposableProxy(kernelApiKey, proxyId) {
|
|
|
4602
4765
|
}));
|
|
4603
4766
|
}
|
|
4604
4767
|
}
|
|
4605
|
-
async function extractOnce(options, signal, forceManagedSerp = false, onPaaProgress) {
|
|
4768
|
+
async function extractOnce(options, signal, forceManagedSerp = false, onPaaProgress, onProviderSession) {
|
|
4606
4769
|
const driver = createSerpDriver(options, { forceManaged: forceManagedSerp });
|
|
4607
4770
|
const reporter = new ProgressReporter();
|
|
4608
|
-
const extractor = new PAAExtractor(driver, reporter, onPaaProgress);
|
|
4771
|
+
const extractor = new PAAExtractor(driver, reporter, onPaaProgress, onProviderSession);
|
|
4609
4772
|
if (signal?.aborted) {
|
|
4610
4773
|
return {
|
|
4611
4774
|
result: null,
|
|
@@ -4750,12 +4913,20 @@ async function harvest(rawOptions) {
|
|
|
4750
4913
|
max_questions: attemptOptions.maxQuestions
|
|
4751
4914
|
}));
|
|
4752
4915
|
const baseCtx = currentCostContext();
|
|
4916
|
+
const onProviderSession = (metadata) => emitAttemptEvent(onAttemptEvent, {
|
|
4917
|
+
type: "provider_session_started",
|
|
4918
|
+
attemptNumber,
|
|
4919
|
+
maxAttempts,
|
|
4920
|
+
provider: metadata.provider,
|
|
4921
|
+
providerSessionId: metadata.providerSessionId,
|
|
4922
|
+
observedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4923
|
+
});
|
|
4753
4924
|
const allowHeadlessTest = baseCtx?.harvestBrowserMode === "headless-test" && process.env[HEADLESS_HARVEST_TEST_ENV] === "true";
|
|
4754
4925
|
const attempt = baseCtx ? await runWithCostContext({
|
|
4755
4926
|
...baseCtx,
|
|
4756
4927
|
forceHeadless: allowHeadlessTest,
|
|
4757
4928
|
forceHeadful: !allowHeadlessTest
|
|
4758
|
-
}, () => extractOnce(attemptOptions, signal, forceManagedSerp, onPaaProgress)) : await extractOnce(attemptOptions, signal, forceManagedSerp, onPaaProgress);
|
|
4929
|
+
}, () => extractOnce(attemptOptions, signal, forceManagedSerp, onPaaProgress, onProviderSession)) : await extractOnce(attemptOptions, signal, forceManagedSerp, onPaaProgress, onProviderSession);
|
|
4759
4930
|
if (attempt.error) {
|
|
4760
4931
|
const err = attempt.error;
|
|
4761
4932
|
const outcome2 = classifyAttemptError(err);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// release-message.json
|
|
2
2
|
var release_message_default = {
|
|
3
|
-
message: "PAA harvests now
|
|
3
|
+
message: "PAA harvests now distinguish click acknowledgement from confirmed Google DOM effects, preserve every observed interaction at 0.7, 1.0, and 1.4-second checkpoints, reject hidden or scripted placeholder text as answers, correlate attempts to Bright Data sessions immediately, and recover interrupted work without claiming the question frontier ended."
|
|
4
4
|
};
|
|
5
5
|
|
|
6
6
|
// src/install-terminal.ts
|
|
@@ -6,11 +6,12 @@ import {
|
|
|
6
6
|
LocationMismatchError,
|
|
7
7
|
PUBLIC_SERVICE_UNAVAILABLE_MESSAGE,
|
|
8
8
|
RequestAbortedError,
|
|
9
|
+
attachHarvestAttemptProviderSession,
|
|
9
10
|
buildPublicErrorEnvelope,
|
|
10
11
|
finishHarvestAttempt,
|
|
11
12
|
publicErrorMessage,
|
|
12
13
|
startHarvestAttempt
|
|
13
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-YXNDOQXN.js";
|
|
14
15
|
|
|
15
16
|
// src/api/harvest-problems.ts
|
|
16
17
|
function errorMessage(err) {
|
|
@@ -186,6 +187,16 @@ function createHarvestAttemptRecorder(jobId, userId) {
|
|
|
186
187
|
});
|
|
187
188
|
return;
|
|
188
189
|
}
|
|
190
|
+
if (event.type === "provider_session_started") {
|
|
191
|
+
await attachHarvestAttemptProviderSession({
|
|
192
|
+
jobId,
|
|
193
|
+
attemptNumber: event.attemptNumber,
|
|
194
|
+
browserProvider: event.provider,
|
|
195
|
+
providerSessionId: event.providerSessionId,
|
|
196
|
+
observedAt: event.observedAt
|
|
197
|
+
});
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
189
200
|
await finishHarvestAttempt({
|
|
190
201
|
jobId,
|
|
191
202
|
attemptNumber: event.attemptNumber,
|
|
@@ -12,13 +12,13 @@ import {
|
|
|
12
12
|
registerScheduledResultsMcpTools,
|
|
13
13
|
registerSerpIntelligenceCaptureTools,
|
|
14
14
|
resolveDeploymentProfile
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-7UZAVM4J.js";
|
|
16
16
|
import {
|
|
17
17
|
renderInstallTerminal
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-MVB25X55.js";
|
|
19
19
|
import {
|
|
20
20
|
PACKAGE_VERSION
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-IHYG52ZW.js";
|
|
22
22
|
|
|
23
23
|
// src/mcp/stdio-runtime.ts
|
|
24
24
|
import { readFileSync } from "fs";
|
|
@@ -488,6 +488,7 @@ var CORE_SCHEMA_COLUMNS = {
|
|
|
488
488
|
"debug_json",
|
|
489
489
|
"browser_provider",
|
|
490
490
|
"provider_session_id",
|
|
491
|
+
"provider_session_observed_at",
|
|
491
492
|
"provider_lookup_status",
|
|
492
493
|
"provider_status",
|
|
493
494
|
"provider_error_code",
|
|
@@ -711,6 +712,7 @@ async function migrate(options = {}) {
|
|
|
711
712
|
debug_json TEXT,
|
|
712
713
|
browser_provider TEXT,
|
|
713
714
|
provider_session_id TEXT,
|
|
715
|
+
provider_session_observed_at TEXT,
|
|
714
716
|
provider_lookup_status TEXT,
|
|
715
717
|
provider_status TEXT,
|
|
716
718
|
provider_error_code TEXT,
|
|
@@ -737,6 +739,7 @@ async function migrate(options = {}) {
|
|
|
737
739
|
for (const column of [
|
|
738
740
|
"browser_provider TEXT",
|
|
739
741
|
"provider_session_id TEXT",
|
|
742
|
+
"provider_session_observed_at TEXT",
|
|
740
743
|
"provider_lookup_status TEXT",
|
|
741
744
|
"provider_status TEXT",
|
|
742
745
|
"provider_error_code TEXT",
|
|
@@ -1912,6 +1915,7 @@ function rowToHarvestAttemptLog(row) {
|
|
|
1912
1915
|
browser_close_error: row.browser_close_error != null ? String(row.browser_close_error) : null,
|
|
1913
1916
|
browser_provider: row.browser_provider != null ? String(row.browser_provider) : null,
|
|
1914
1917
|
provider_session_id: row.provider_session_id != null ? String(row.provider_session_id) : null,
|
|
1918
|
+
provider_session_observed_at: row.provider_session_observed_at != null ? String(row.provider_session_observed_at) : null,
|
|
1915
1919
|
provider_lookup_status: row.provider_lookup_status != null ? String(row.provider_lookup_status) : null,
|
|
1916
1920
|
provider_status: row.provider_status != null ? String(row.provider_status) : null,
|
|
1917
1921
|
provider_error_code: row.provider_error_code != null ? String(row.provider_error_code) : null,
|
|
@@ -1991,6 +1995,49 @@ async function startHarvestAttempt(input) {
|
|
|
1991
1995
|
]
|
|
1992
1996
|
});
|
|
1993
1997
|
}
|
|
1998
|
+
async function attachHarvestAttemptProviderSession(input) {
|
|
1999
|
+
const result = await getDb().execute({
|
|
2000
|
+
sql: `
|
|
2001
|
+
UPDATE harvest_attempts SET
|
|
2002
|
+
browser_provider = ?,
|
|
2003
|
+
provider_session_id = ?,
|
|
2004
|
+
provider_session_observed_at = COALESCE(provider_session_observed_at, ?),
|
|
2005
|
+
provider_lookup_status = CASE
|
|
2006
|
+
WHEN provider_lookup_status = 'complete' THEN provider_lookup_status
|
|
2007
|
+
ELSE 'pending'
|
|
2008
|
+
END
|
|
2009
|
+
WHERE job_id = ? AND attempt_number = ? AND status = 'running'
|
|
2010
|
+
`,
|
|
2011
|
+
args: [
|
|
2012
|
+
input.browserProvider,
|
|
2013
|
+
input.providerSessionId,
|
|
2014
|
+
input.observedAt,
|
|
2015
|
+
input.jobId,
|
|
2016
|
+
input.attemptNumber
|
|
2017
|
+
]
|
|
2018
|
+
});
|
|
2019
|
+
return result.rowsAffected === 1;
|
|
2020
|
+
}
|
|
2021
|
+
async function finalizeExpiredHarvestAttempts(jobId, willRetry, completedAt = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
2022
|
+
const result = await getDb().execute({
|
|
2023
|
+
sql: `
|
|
2024
|
+
UPDATE harvest_attempts SET
|
|
2025
|
+
status = 'finished',
|
|
2026
|
+
outcome = COALESCE(outcome, 'timeout'),
|
|
2027
|
+
question_count = COALESCE(question_count, 0),
|
|
2028
|
+
duration_ms = COALESCE(duration_ms, MAX(0, CAST((julianday(?) - julianday(created_at)) * 86400000 AS INTEGER))),
|
|
2029
|
+
error = COALESCE(error, 'Durable worker lease expired before attempt completion'),
|
|
2030
|
+
will_retry = ?,
|
|
2031
|
+
provider_error_code = COALESCE(provider_error_code, 'timeout'),
|
|
2032
|
+
provider_error_message = COALESCE(provider_error_message, 'Durable worker lease expired before attempt completion'),
|
|
2033
|
+
provider_reason_source = COALESCE(provider_reason_source, 'client_runtime'),
|
|
2034
|
+
completed_at = COALESCE(completed_at, ?)
|
|
2035
|
+
WHERE job_id = ? AND status = 'running'
|
|
2036
|
+
`,
|
|
2037
|
+
args: [completedAt, willRetry ? 1 : 0, completedAt, jobId]
|
|
2038
|
+
});
|
|
2039
|
+
return result.rowsAffected;
|
|
2040
|
+
}
|
|
1994
2041
|
async function finishHarvestAttempt(input) {
|
|
1995
2042
|
await getDb().execute({
|
|
1996
2043
|
sql: `
|
|
@@ -3869,6 +3916,8 @@ export {
|
|
|
3869
3916
|
createRunningJob,
|
|
3870
3917
|
getOrCreateRunningJob,
|
|
3871
3918
|
startHarvestAttempt,
|
|
3919
|
+
attachHarvestAttemptProviderSession,
|
|
3920
|
+
finalizeExpiredHarvestAttempts,
|
|
3872
3921
|
finishHarvestAttempt,
|
|
3873
3922
|
updateHarvestAttemptProviderTelemetry,
|
|
3874
3923
|
listHarvestAttemptsPendingProviderTelemetry,
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
SiteAuditPhaseLogRowSchema,
|
|
8
8
|
advanceWorkflowRunStep,
|
|
9
9
|
appendWorkflowArtifacts,
|
|
10
|
+
attachHarvestAttemptProviderSession,
|
|
10
11
|
cancelJob,
|
|
11
12
|
checkRateLimit,
|
|
12
13
|
checkpointDurablePaaJob,
|
|
@@ -49,6 +50,7 @@ import {
|
|
|
49
50
|
failRunningJob,
|
|
50
51
|
failStripeEvent,
|
|
51
52
|
failWorkflowRunRecord,
|
|
53
|
+
finalizeExpiredHarvestAttempts,
|
|
52
54
|
finishHarvestAttempt,
|
|
53
55
|
generateApiKey,
|
|
54
56
|
getBillingLedgerSummary,
|
|
@@ -137,7 +139,7 @@ import {
|
|
|
137
139
|
updateKpoJobState,
|
|
138
140
|
upsertPageSnapshot,
|
|
139
141
|
verifyPassword
|
|
140
|
-
} from "./chunk-
|
|
142
|
+
} from "./chunk-YXNDOQXN.js";
|
|
141
143
|
export {
|
|
142
144
|
CORE_SCHEMA_VERSION,
|
|
143
145
|
CREDIT_LOTS_EPOCH,
|
|
@@ -147,6 +149,7 @@ export {
|
|
|
147
149
|
SiteAuditPhaseLogRowSchema,
|
|
148
150
|
advanceWorkflowRunStep,
|
|
149
151
|
appendWorkflowArtifacts,
|
|
152
|
+
attachHarvestAttemptProviderSession,
|
|
150
153
|
cancelJob,
|
|
151
154
|
checkRateLimit,
|
|
152
155
|
checkpointDurablePaaJob,
|
|
@@ -189,6 +192,7 @@ export {
|
|
|
189
192
|
failRunningJob,
|
|
190
193
|
failStripeEvent,
|
|
191
194
|
failWorkflowRunRecord,
|
|
195
|
+
finalizeExpiredHarvestAttempts,
|
|
192
196
|
finishHarvestAttempt,
|
|
193
197
|
generateApiKey,
|
|
194
198
|
getBillingLedgerSummary,
|
|
@@ -4,13 +4,13 @@ import {
|
|
|
4
4
|
downloadAsset,
|
|
5
5
|
embedCommonsTexts,
|
|
6
6
|
publicizeExtractionFailure
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-F5GQJWZU.js";
|
|
8
8
|
import {
|
|
9
9
|
createSiteExtractBundleArtifactStream,
|
|
10
10
|
createSiteExtractContentReader,
|
|
11
11
|
createSiteExtractImageArtifact,
|
|
12
12
|
extractJobLimitInfo
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-YBR32LOD.js";
|
|
14
14
|
import "./chunk-OZJMVCDK.js";
|
|
15
15
|
import {
|
|
16
16
|
computeIssues,
|
|
@@ -23,7 +23,7 @@ import "./chunk-VFCUGTCF.js";
|
|
|
23
23
|
import "./chunk-GGZEC22A.js";
|
|
24
24
|
import {
|
|
25
25
|
getDb
|
|
26
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-YXNDOQXN.js";
|
|
27
27
|
|
|
28
28
|
// src/api/extract-bundle.ts
|
|
29
29
|
import { createWriteStream, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "fs";
|
|
@@ -7,12 +7,12 @@ import {
|
|
|
7
7
|
GmailServiceError,
|
|
8
8
|
normalizeGmailMessage,
|
|
9
9
|
parseGmailAddresses
|
|
10
|
-
} from "./chunk-
|
|
11
|
-
import "./chunk-
|
|
10
|
+
} from "./chunk-3423R23T.js";
|
|
11
|
+
import "./chunk-C5Z4OFKW.js";
|
|
12
12
|
import "./chunk-T3MZISOF.js";
|
|
13
13
|
import "./chunk-VFCUGTCF.js";
|
|
14
|
-
import "./chunk-
|
|
15
|
-
import "./chunk-
|
|
14
|
+
import "./chunk-5UAFBKJR.js";
|
|
15
|
+
import "./chunk-YXNDOQXN.js";
|
|
16
16
|
export {
|
|
17
17
|
GMAIL_ATTACHMENT_REF_TTL_MS,
|
|
18
18
|
GMAIL_INLINE_BODY_BYTES,
|