@webcrawl/mcp 1.4.2 → 1.4.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/README.md +1 -1
  2. package/dist/bin.js +5 -7
  3. package/dist/core/engine/awswaf.d.ts +59 -0
  4. package/dist/core/engine/awswaf.js +164 -0
  5. package/dist/core/engine/cache.d.ts +70 -0
  6. package/dist/core/engine/cache.js +297 -0
  7. package/dist/core/engine/chrome-cdp.d.ts +22 -0
  8. package/dist/core/engine/chrome-cdp.js +318 -0
  9. package/dist/core/engine/chrome-launch.d.ts +38 -0
  10. package/dist/core/engine/chrome-launch.js +214 -0
  11. package/dist/core/engine/chrome.d.ts +28 -0
  12. package/dist/core/engine/chrome.js +208 -0
  13. package/dist/core/engine/crawl-policy.d.ts +36 -0
  14. package/dist/core/engine/crawl-policy.js +134 -0
  15. package/dist/core/engine/crawler.d.ts +152 -0
  16. package/dist/core/engine/crawler.js +918 -0
  17. package/dist/core/engine/fetcher.d.ts +92 -0
  18. package/dist/core/engine/fetcher.js +494 -0
  19. package/dist/core/engine/index.d.ts +7 -0
  20. package/dist/core/engine/index.js +7 -0
  21. package/dist/core/engine/markdown.d.ts +33 -0
  22. package/dist/core/engine/markdown.js +334 -0
  23. package/dist/core/engine/path-scope.d.ts +21 -0
  24. package/dist/core/engine/path-scope.js +88 -0
  25. package/dist/core/engine/render.d.ts +41 -0
  26. package/dist/core/engine/render.js +138 -0
  27. package/dist/core/engine/safe-url.d.ts +29 -0
  28. package/dist/core/engine/safe-url.js +109 -0
  29. package/dist/core/engine/session.d.ts +93 -0
  30. package/dist/core/engine/session.js +250 -0
  31. package/dist/core/engine/stealth.d.ts +83 -0
  32. package/dist/core/engine/stealth.js +222 -0
  33. package/dist/core/engine.d.ts +1 -0
  34. package/dist/core/engine.js +1 -0
  35. package/dist/core/extract/business.d.ts +28 -0
  36. package/dist/core/extract/business.js +344 -0
  37. package/dist/core/extract/currency.d.ts +15 -0
  38. package/dist/core/extract/currency.js +259 -0
  39. package/dist/core/extract/cve-cache.d.ts +62 -0
  40. package/dist/core/extract/cve-cache.js +277 -0
  41. package/dist/core/extract/cve.d.ts +57 -0
  42. package/dist/core/extract/cve.js +397 -0
  43. package/dist/core/extract/dedupe-key.d.ts +12 -0
  44. package/dist/core/extract/dedupe-key.js +36 -0
  45. package/dist/core/extract/flights.d.ts +37 -0
  46. package/dist/core/extract/flights.js +373 -0
  47. package/dist/core/extract/hotels.d.ts +35 -0
  48. package/dist/core/extract/hotels.js +473 -0
  49. package/dist/core/extract/index.d.ts +9 -0
  50. package/dist/core/extract/index.js +9 -0
  51. package/dist/core/extract/real-estate.d.ts +53 -0
  52. package/dist/core/extract/real-estate.js +835 -0
  53. package/dist/core/extract/scaledown.d.ts +14 -0
  54. package/dist/core/extract/scaledown.js +484 -0
  55. package/dist/core/extract/shopping.d.ts +44 -0
  56. package/dist/core/extract/shopping.js +393 -0
  57. package/dist/core/extract/structured.d.ts +33 -0
  58. package/dist/core/extract/structured.js +316 -0
  59. package/dist/core/image-url.d.ts +5 -0
  60. package/dist/core/image-url.js +13 -0
  61. package/dist/core/interact/driver.d.ts +72 -0
  62. package/dist/core/interact/driver.js +384 -0
  63. package/dist/core/interact/remote.d.ts +41 -0
  64. package/dist/core/interact/remote.js +133 -0
  65. package/dist/core/interact/spans.d.ts +46 -0
  66. package/dist/core/interact/spans.js +32 -0
  67. package/dist/core/interact/validate.d.ts +34 -0
  68. package/dist/core/interact/validate.js +135 -0
  69. package/dist/core/interact/vm.d.ts +43 -0
  70. package/dist/core/interact/vm.js +338 -0
  71. package/dist/core/local-engine.d.ts +62 -0
  72. package/dist/core/local-engine.js +726 -0
  73. package/dist/core/parse/detect.d.ts +29 -0
  74. package/dist/core/parse/detect.js +152 -0
  75. package/dist/core/parse/fetch.d.ts +20 -0
  76. package/dist/core/parse/fetch.js +39 -0
  77. package/dist/core/parse/image.d.ts +13 -0
  78. package/dist/core/parse/image.js +100 -0
  79. package/dist/core/parse/index.d.ts +53 -0
  80. package/dist/core/parse/index.js +153 -0
  81. package/dist/core/parse/limits.d.ts +12 -0
  82. package/dist/core/parse/limits.js +29 -0
  83. package/dist/core/parse/media.d.ts +42 -0
  84. package/dist/core/parse/media.js +238 -0
  85. package/dist/core/parse/merge.d.ts +19 -0
  86. package/dist/core/parse/merge.js +48 -0
  87. package/dist/core/parse/ocr.d.ts +25 -0
  88. package/dist/core/parse/ocr.js +121 -0
  89. package/dist/core/parse/office.d.ts +28 -0
  90. package/dist/core/parse/office.js +83 -0
  91. package/dist/core/parse/pdf.d.ts +27 -0
  92. package/dist/core/parse/pdf.js +156 -0
  93. package/dist/core/parse/types.d.ts +38 -0
  94. package/dist/core/parse/types.js +1 -0
  95. package/dist/core/parse/youtube.d.ts +30 -0
  96. package/dist/core/parse/youtube.js +264 -0
  97. package/dist/core/ranker/decision.d.ts +28 -0
  98. package/dist/core/ranker/decision.js +151 -0
  99. package/dist/core/ranker/index.d.ts +5 -0
  100. package/dist/core/ranker/index.js +361 -0
  101. package/dist/core/ranker/lexical.d.ts +82 -0
  102. package/dist/core/ranker/lexical.js +643 -0
  103. package/dist/core/ranker/types.d.ts +90 -0
  104. package/dist/core/ranker/types.js +1 -0
  105. package/dist/core/ranker.d.ts +1 -0
  106. package/dist/core/ranker.js +1 -0
  107. package/dist/core/sandbox.d.ts +29 -0
  108. package/dist/core/sandbox.js +222 -0
  109. package/dist/core/search/catalog.d.ts +176 -0
  110. package/dist/core/search/catalog.js +488 -0
  111. package/dist/core/search/index.d.ts +3 -0
  112. package/dist/core/search/index.js +3 -0
  113. package/dist/core/search/pipeline.d.ts +7 -0
  114. package/dist/core/search/pipeline.js +721 -0
  115. package/dist/core/search/providers.d.ts +84 -0
  116. package/dist/core/search/providers.js +1408 -0
  117. package/dist/core/search.d.ts +1 -0
  118. package/dist/core/search.js +1 -0
  119. package/dist/core/targets/developer.d.ts +44 -0
  120. package/dist/core/targets/developer.js +338 -0
  121. package/dist/core/targets/index.d.ts +3 -0
  122. package/dist/core/targets/index.js +3 -0
  123. package/dist/core/targets/registry.d.ts +32 -0
  124. package/dist/core/targets/registry.js +411 -0
  125. package/dist/core/targets/types.d.ts +72 -0
  126. package/dist/core/targets/types.js +8 -0
  127. package/dist/core/targets.d.ts +1 -0
  128. package/dist/core/targets.js +1 -0
  129. package/dist/format.js +5 -1
  130. package/dist/index.d.ts +1 -0
  131. package/dist/index.js +1 -0
  132. package/dist/load-local-engine.d.ts +7 -0
  133. package/dist/load-local-engine.js +22 -0
  134. package/dist/server.js +6 -0
  135. package/package.json +11 -3
@@ -0,0 +1,208 @@
1
+ /**
2
+ * Local Chrome renderer (JS pages the native fetch ladder cannot hydrate).
3
+ *
4
+ * Primary driver is Chrome DevTools Protocol:
5
+ * launch headless Chrome, navigate, wait until the DOM is content-ready
6
+ * (load + 200ms mutation-idle), then read `documentElement.outerHTML`.
7
+ *
8
+ * dump-dom is a last-resort fallback: Chrome 156 hangs on dump-dom with a
9
+ * fresh profile, which used to look like a successful scrape of the
10
+ * unrendered nav shell. Every attempt has a hard timeout and a process-group
11
+ * kill. Playwright's chrome-headless-shell is discovered automatically when
12
+ * present.
13
+ */
14
+ import { spawn } from "node:child_process";
15
+ import { mkdtempSync, rmSync } from "node:fs";
16
+ import { tmpdir } from "node:os";
17
+ import { join } from "node:path";
18
+ import { renderUrlViaCdp } from "./chrome-cdp.js";
19
+ import { DEFAULT_TIMEOUT_MS, DUMP_MAX_BYTES, chromeDumpDomArgs, discoverChromeBinaries, killProcessGroup, } from "./chrome-launch.js";
20
+ export { DEFAULT_TIMEOUT_MS, VIRTUAL_TIME_MS, discoverChromeBinaries, findPlaywrightHeadlessShell, installedChromePath, isLocalChromeAvailable, parseDevToolsActivePort, chromeCdpArgs, chromeDumpDomArgs, killProcessGroup, } from "./chrome-launch.js";
21
+ function attemptPlan(binary) {
22
+ const isShell = /headless[-_]?shell/i.test(binary);
23
+ if (isShell) {
24
+ return [
25
+ { driver: "cdp", headless: "shell" },
26
+ { driver: "dump-dom", headless: "shell" },
27
+ ];
28
+ }
29
+ return [
30
+ { driver: "cdp", headless: "new" },
31
+ { driver: "cdp", headless: "old" },
32
+ { driver: "dump-dom", headless: "old" },
33
+ { driver: "dump-dom", headless: "new" },
34
+ ];
35
+ }
36
+ function methodName(binary, kind) {
37
+ const base = binary.split(/[/\\]/).pop() || binary;
38
+ return `${kind.driver}/${kind.headless} (${base})`;
39
+ }
40
+ /**
41
+ * Render `url` in local Chrome and return the hydrated HTML, or null when
42
+ * Chrome is missing, times out, or produces nothing.
43
+ */
44
+ export async function renderViaLocalChrome(url, opts = {}) {
45
+ const result = await renderViaLocalChromeDetailed(url, opts);
46
+ return result.html;
47
+ }
48
+ /**
49
+ * Same as `renderViaLocalChrome` but with the attempt log and an actionable
50
+ * error when every driver fails. Callers that need to report honest JS-render
51
+ * failures should use this.
52
+ */
53
+ export async function renderViaLocalChromeDetailed(url, opts = {}) {
54
+ const binaries = discoverChromeBinaries();
55
+ if (binaries.length === 0) {
56
+ return {
57
+ html: null,
58
+ error: "No local Chrome/Chromium binary found. Install Chrome, set WEBCRAWL_CHROME_PATH, " +
59
+ "or run `npx playwright install chromium-headless-shell`. See `webcrawl doctor`.",
60
+ attempts: [],
61
+ };
62
+ }
63
+ const budgetMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
64
+ const started = Date.now();
65
+ const attempts = [];
66
+ let lastError = "local Chrome produced no content";
67
+ for (const binary of binaries) {
68
+ for (const kind of attemptPlan(binary)) {
69
+ const remaining = budgetMs - (Date.now() - started);
70
+ if (remaining < 1_200)
71
+ break;
72
+ const method = methodName(binary, kind);
73
+ const attemptBudget = kind.driver === "cdp"
74
+ ? Math.min(remaining, kind.headless === "new" ? 12_000 : 8_000)
75
+ : Math.min(remaining, 6_000);
76
+ const t0 = Date.now();
77
+ try {
78
+ const html = kind.driver === "cdp"
79
+ ? (await renderUrlViaCdp(binary, url, {
80
+ timeoutMs: attemptBudget,
81
+ headless: kind.headless,
82
+ })).html
83
+ : await dumpDom(binary, url, kind.headless, attemptBudget);
84
+ const ms = Date.now() - t0;
85
+ if (html && html.length > 100) {
86
+ attempts.push({ ok: true, method, ms });
87
+ return { html, method, attempts };
88
+ }
89
+ lastError = `${method} returned empty HTML`;
90
+ attempts.push({ ok: false, method, ms, error: lastError });
91
+ }
92
+ catch (err) {
93
+ const ms = Date.now() - t0;
94
+ lastError = `${method}: ${err instanceof Error ? err.message : String(err)}`;
95
+ attempts.push({ ok: false, method, ms, error: lastError });
96
+ }
97
+ }
98
+ }
99
+ return {
100
+ html: null,
101
+ error: `JavaScript rendering failed (${lastError}). ` +
102
+ "Chrome hung, timed out, or produced no content. Install Chrome, point WEBCRAWL_CHROME_PATH " +
103
+ "at chrome-headless-shell (`npx playwright install chromium-headless-shell`), " +
104
+ "run `webcrawl login` and retry with `--mode cloud`, or set WEBCRAWL_RENDER_URL.",
105
+ attempts,
106
+ };
107
+ }
108
+ async function dumpDom(chrome, url, headless, timeoutMs) {
109
+ let profile = "";
110
+ try {
111
+ profile = mkdtempSync(join(tmpdir(), "wc-chrome-"));
112
+ }
113
+ catch {
114
+ throw new Error("could not create Chrome profile directory");
115
+ }
116
+ const args = chromeDumpDomArgs({
117
+ profile,
118
+ headless,
119
+ url,
120
+ timeoutMs,
121
+ binaryPath: chrome,
122
+ });
123
+ try {
124
+ return await new Promise((resolve, reject) => {
125
+ let settled = false;
126
+ const done = (err, value) => {
127
+ if (settled)
128
+ return;
129
+ settled = true;
130
+ if (err)
131
+ reject(err);
132
+ else
133
+ resolve(value ?? "");
134
+ };
135
+ let child;
136
+ try {
137
+ child = spawn(chrome, args, {
138
+ stdio: ["ignore", "pipe", "pipe"],
139
+ detached: true,
140
+ });
141
+ }
142
+ catch (err) {
143
+ done(err instanceof Error ? err : new Error(String(err)));
144
+ return;
145
+ }
146
+ const chunks = [];
147
+ let bytes = 0;
148
+ let idle = null;
149
+ const finish = (err) => {
150
+ clearTimeout(timer);
151
+ if (idle)
152
+ clearTimeout(idle);
153
+ killProcessGroup(child.pid, child);
154
+ if (err) {
155
+ done(err);
156
+ return;
157
+ }
158
+ const text = Buffer.concat(chunks).toString("utf8");
159
+ done(null, text.trim());
160
+ };
161
+ const timer = setTimeout(() => {
162
+ if (bytes > 100)
163
+ finish(null);
164
+ else
165
+ finish(new Error(`dump-dom timed out after ${timeoutMs}ms`));
166
+ }, timeoutMs);
167
+ child.stdout?.on("data", (c) => {
168
+ if (bytes >= DUMP_MAX_BYTES)
169
+ return;
170
+ const take = c.subarray(0, DUMP_MAX_BYTES - bytes);
171
+ chunks.push(take);
172
+ bytes += take.length;
173
+ // Chrome 156 often prints the DOM then never exits (`--dump-dom` hang).
174
+ if (idle)
175
+ clearTimeout(idle);
176
+ idle = setTimeout(() => {
177
+ if (bytes > 40)
178
+ finish(null);
179
+ }, 400);
180
+ });
181
+ child.on("error", (err) => {
182
+ clearTimeout(timer);
183
+ if (idle)
184
+ clearTimeout(idle);
185
+ done(err);
186
+ });
187
+ child.on("close", () => {
188
+ clearTimeout(timer);
189
+ if (idle)
190
+ clearTimeout(idle);
191
+ if (bytes === 0) {
192
+ done(new Error("dump-dom produced no stdout"));
193
+ return;
194
+ }
195
+ const text = Buffer.concat(chunks).toString("utf8");
196
+ done(null, text.trim().length > 0 ? text : "");
197
+ });
198
+ });
199
+ }
200
+ finally {
201
+ try {
202
+ rmSync(profile, { recursive: true, force: true });
203
+ }
204
+ catch {
205
+ /* leftover profile is in /tmp */
206
+ }
207
+ }
208
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Crawl politeness helpers: robots.txt rules, adaptive concurrency (AIMD) and retries.
3
+ * Pure and dependency-free so they are easy to test.
4
+ */
5
+ export interface RobotsRules {
6
+ allows(url: string): boolean;
7
+ /** Crawl-delay in ms for our agent (or `*`), 0 when unset. */
8
+ crawlDelayMs: number;
9
+ }
10
+ /** Parses robots.txt. Our own group wins over `*`; longest matching rule wins; Allow beats Disallow on ties. */
11
+ export declare function parseRobots(text: string | null | undefined): RobotsRules;
12
+ /** Additive-increase / multiplicative-decrease concurrency controller. */
13
+ export declare class AdaptiveConcurrency {
14
+ private readonly min;
15
+ private readonly max;
16
+ private target;
17
+ constructor(min?: number, max?: number, initial?: number);
18
+ get current(): number;
19
+ /** Feed the outcome of a finished wave. */
20
+ record(outcome: {
21
+ ok: number;
22
+ failed: number;
23
+ throttled: boolean;
24
+ }): number;
25
+ }
26
+ export declare const sleep: (ms: number) => Promise<void>;
27
+ export declare function backoffDelayMs(attempt: number, baseMs?: number, capMs?: number, rand?: () => number): number;
28
+ export declare function isRetryableStatus(status: number): boolean;
29
+ /** Retries transient failures (network errors, 408/425/429/5xx) with jittered exponential backoff. */
30
+ export declare function withRetries<T extends {
31
+ status: number;
32
+ }>(run: () => Promise<T>, opts?: {
33
+ retries?: number;
34
+ baseMs?: number;
35
+ sleepFn?: (ms: number) => Promise<void>;
36
+ }): Promise<T>;
@@ -0,0 +1,134 @@
1
+ /**
2
+ * Crawl politeness helpers: robots.txt rules, adaptive concurrency (AIMD) and retries.
3
+ * Pure and dependency-free so they are easy to test.
4
+ */
5
+ const ROBOT_TOKEN = "webcrawl";
6
+ function patternToRegExp(path) {
7
+ const anchored = path.endsWith("$");
8
+ const body = (anchored ? path.slice(0, -1) : path)
9
+ .replace(/[.+?^${}()|[\]\\]/g, "\\$&")
10
+ .replace(/\*/g, ".*");
11
+ return new RegExp(`^${body}${anchored ? "$" : ""}`);
12
+ }
13
+ /** Parses robots.txt. Our own group wins over `*`; longest matching rule wins; Allow beats Disallow on ties. */
14
+ export function parseRobots(text) {
15
+ const groups = [];
16
+ let current = null;
17
+ let lastWasAgent = false;
18
+ for (const rawLine of (text ?? "").split(/\r?\n/)) {
19
+ const line = rawLine.replace(/#.*$/, "").trim();
20
+ if (!line)
21
+ continue;
22
+ const idx = line.indexOf(":");
23
+ if (idx < 0)
24
+ continue;
25
+ const key = line.slice(0, idx).trim().toLowerCase();
26
+ const value = line.slice(idx + 1).trim();
27
+ if (key === "user-agent") {
28
+ if (!current || !lastWasAgent) {
29
+ current = { agents: [], rules: [], delay: 0 };
30
+ groups.push(current);
31
+ }
32
+ current.agents.push(value.toLowerCase());
33
+ lastWasAgent = true;
34
+ continue;
35
+ }
36
+ lastWasAgent = false;
37
+ if (!current)
38
+ continue;
39
+ if (key === "disallow" || key === "allow") {
40
+ if (!value && key === "disallow")
41
+ continue; // empty Disallow = allow all
42
+ current.rules.push({
43
+ allow: key === "allow",
44
+ pattern: patternToRegExp(value),
45
+ length: value.length,
46
+ });
47
+ }
48
+ else if (key === "crawl-delay") {
49
+ const n = Number(value);
50
+ if (Number.isFinite(n) && n > 0)
51
+ current.delay = Math.min(n, 10) * 1000;
52
+ }
53
+ }
54
+ const mine = groups.filter((g) => g.agents.some((a) => a !== "*" && ROBOT_TOKEN.includes(a)));
55
+ const chosen = mine.length ? mine : groups.filter((g) => g.agents.includes("*"));
56
+ const rules = chosen.flatMap((g) => g.rules);
57
+ const crawlDelayMs = Math.max(0, ...chosen.map((g) => g.delay));
58
+ return {
59
+ crawlDelayMs,
60
+ allows(url) {
61
+ let path;
62
+ try {
63
+ const u = new URL(url);
64
+ path = u.pathname + u.search;
65
+ }
66
+ catch {
67
+ return true;
68
+ }
69
+ let best = null;
70
+ for (const r of rules) {
71
+ if (!r.pattern.test(path))
72
+ continue;
73
+ if (!best || r.length > best.length || (r.length === best.length && r.allow))
74
+ best = r;
75
+ }
76
+ return best ? best.allow : true;
77
+ },
78
+ };
79
+ }
80
+ /** Additive-increase / multiplicative-decrease concurrency controller. */
81
+ export class AdaptiveConcurrency {
82
+ min;
83
+ max;
84
+ target;
85
+ constructor(min = 1, max = 16, initial = 4) {
86
+ this.min = min;
87
+ this.max = max;
88
+ this.target = Math.min(Math.max(initial, min), max);
89
+ }
90
+ get current() {
91
+ return this.target;
92
+ }
93
+ /** Feed the outcome of a finished wave. */
94
+ record(outcome) {
95
+ const total = outcome.ok + outcome.failed;
96
+ if (outcome.throttled || (total > 0 && outcome.failed / total > 0.3)) {
97
+ this.target = Math.max(this.min, Math.floor(this.target / 2));
98
+ }
99
+ else if (outcome.ok > 0) {
100
+ this.target = Math.min(this.max, this.target + 1);
101
+ }
102
+ return this.target;
103
+ }
104
+ }
105
+ export const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
106
+ export function backoffDelayMs(attempt, baseMs = 250, capMs = 4000, rand = Math.random) {
107
+ const exp = Math.min(capMs, baseMs * 2 ** attempt);
108
+ return Math.round(exp / 2 + rand() * (exp / 2));
109
+ }
110
+ export function isRetryableStatus(status) {
111
+ return status === 408 || status === 425 || status === 429 || status >= 500;
112
+ }
113
+ /** Retries transient failures (network errors, 408/425/429/5xx) with jittered exponential backoff. */
114
+ export async function withRetries(run, opts = {}) {
115
+ const retries = opts.retries ?? 2;
116
+ const wait = opts.sleepFn ?? sleep;
117
+ let lastErr;
118
+ for (let attempt = 0; attempt <= retries; attempt++) {
119
+ try {
120
+ const res = await run();
121
+ if (!isRetryableStatus(res.status) || attempt === retries)
122
+ return res;
123
+ }
124
+ catch (err) {
125
+ if (err?.name === "UnsafeUrlError")
126
+ throw err;
127
+ lastErr = err;
128
+ if (attempt === retries)
129
+ throw err;
130
+ }
131
+ await wait(backoffDelayMs(attempt, opts.baseMs));
132
+ }
133
+ throw lastErr;
134
+ }
@@ -0,0 +1,152 @@
1
+ export interface MapWebsiteOptions {
2
+ /** Only crawl the exact root/target domain (default: true when includeSubdomains is false and selectedDomains is empty) */
3
+ onlyRootDomain?: boolean;
4
+ /** Also crawl subdomains of the allowed domain(s), e.g. docs.example.com (default: false) */
5
+ includeSubdomains?: boolean;
6
+ /** Explicit allowlist of domains/subdomains (supports exact hosts like "docs.example.com" or wildcards like "*.example.com") */
7
+ selectedDomains?: string[];
8
+ /** Maximum number of discovered URLs to return (default: 5000) */
9
+ limit?: number;
10
+ /** Maximum number of HTML pages to fetch during multi-hop BFS crawl when sitemap is missing (default: 250) */
11
+ maxCrawlPages?: number;
12
+ /** Alias for maxCrawlPages */
13
+ maxCrawl?: number;
14
+ /** Maximum BFS hop depth from the entry URL (default: 8) */
15
+ maxDepth?: number;
16
+ /** Only keep URLs whose path matches one of these (prefix, `*` wildcards, trailing `$`). */
17
+ includePaths?: string[];
18
+ /** Drop URLs whose path matches any of these. Wins over includePaths. */
19
+ excludePaths?: string[];
20
+ /** Stay under the start URL's path when no includePaths are given (default: true). */
21
+ restrictToSubpath?: boolean;
22
+ /** Sitemaps to read first (e.g. a site whose sitemap is not at /sitemap.xml). */
23
+ sitemapUrls?: string[];
24
+ /** Overall map timeout in milliseconds (default: 25000) */
25
+ timeoutMs?: number;
26
+ /** Bypass 24-hour map cache (default: false) */
27
+ bypassCache?: boolean;
28
+ /** When a bot check blocks plain requests, find pages through a real browser (default: true) */
29
+ stealth?: boolean;
30
+ /** Time budget for the stealth browser session (default: 75000) */
31
+ stealthBudgetMs?: number;
32
+ /** Allow the paid remote stealth provider (default true). Off for unbilled runs. */
33
+ remoteStealth?: boolean;
34
+ }
35
+ export declare function mapWebsiteUrls(targetUrl: string, options?: MapWebsiteOptions): Promise<{
36
+ success: boolean;
37
+ links: string[];
38
+ latencyMs: number;
39
+ source: string;
40
+ cached?: boolean;
41
+ cacheLayer?: "memory" | "disk" | "postgres" | "miss";
42
+ pagesCrawled?: number;
43
+ /** The site answered plain requests with a bot check. */
44
+ blocked?: boolean;
45
+ /** Pages were found through the stealth browser. */
46
+ stealth?: boolean;
47
+ /** What a remote stealth provider charged for discovery, in USD. */
48
+ stealthCostUsd?: number;
49
+ scope?: {
50
+ onlyRootDomain: boolean;
51
+ includeSubdomains: boolean;
52
+ selectedDomains: string[];
53
+ maxCrawlPages: number;
54
+ maxDepth: number;
55
+ includePaths: string[];
56
+ excludePaths: string[];
57
+ };
58
+ }>;
59
+ /**
60
+ * Ultra-fast HTTP status batch check for `websiteDeletedPages.ts`.
61
+ * Checks if URLs return 404/410 without downloading/rendering full markdown!
62
+ */
63
+ export declare function checkUrlsStatusBatch(urls: string[], timeoutMs?: number): Promise<Array<{
64
+ url: string;
65
+ statusCode: number;
66
+ ok: boolean;
67
+ }>>;
68
+ /**
69
+ * Extracts structured JSON from a URL.
70
+ * Honours `prompt` / `schema` when provided; otherwise falls back to
71
+ * `{ app_name, product_features }` from title and headings.
72
+ */
73
+ export declare function extractWebsiteData(url: string, options?: {
74
+ prompt?: string;
75
+ schema?: Record<string, unknown>;
76
+ openaiApiKey?: string;
77
+ bypassCache?: boolean;
78
+ /** Already-extracted content (e.g. a parsed PDF); skips the HTML fetch and cache. */
79
+ markdown?: string;
80
+ /** Source HTML when the caller already fetched the page (quote cards, JSON-LD). */
81
+ html?: string;
82
+ }): Promise<Record<string, unknown> & {
83
+ cached?: boolean;
84
+ cacheLayer?: "memory" | "disk" | "postgres" | "miss";
85
+ }>;
86
+ /**
87
+ * Full-website multi-page crawler (`POST /v1/crawl`) backed by the shared 3-tier
88
+ * L1 Memory -> L2 Disk -> L3 Postgres URL store (`crawled_pages`).
89
+ * Pages already stored in Postgres for this website are served directly without re-crawling!
90
+ */
91
+ export declare function crawlWebsitePages(targetUrl: string, options?: {
92
+ maxPages?: number;
93
+ includeSubdomains?: boolean;
94
+ onlyRootDomain?: boolean;
95
+ bypassCache?: boolean;
96
+ extractStructured?: boolean;
97
+ timeoutMs?: number;
98
+ }): Promise<{
99
+ success: boolean;
100
+ rootUrl: string;
101
+ pagesCount: number;
102
+ cachedPagesCount: number;
103
+ postgresHits: number;
104
+ latencyMs: number;
105
+ pages: CrawledPage[];
106
+ }>;
107
+ export interface CrawledPage {
108
+ url: string;
109
+ title: string;
110
+ markdown: string;
111
+ linksCount: number;
112
+ cached: boolean;
113
+ cacheLayer: "memory" | "disk" | "postgres" | "miss";
114
+ structuredData?: Record<string, unknown> | null;
115
+ statusCode?: number;
116
+ error?: string;
117
+ /** The site served a bot check instead of this page. */
118
+ blocked?: boolean;
119
+ /** Read through the stealth browser. */
120
+ stealth?: boolean;
121
+ /** Remote provider that hosted the stealth browser (billed with a surcharge). */
122
+ stealthProvider?: string;
123
+ }
124
+ /** True for markdown converted from a bot-check page (old cache entries). */
125
+ export declare function isChallengeMarkdown(title: string, markdown: string): boolean;
126
+ /**
127
+ * Re-reads blocked pages through one stealth browser session. Pages that come
128
+ * back clean are cached and returned as content; pages still blocked keep
129
+ * `blocked: true` and an honest error.
130
+ */
131
+ export declare function crawlPagesStealth(entryUrl: string, urls: string[], options?: {
132
+ extractStructured?: boolean;
133
+ budgetMs?: number;
134
+ remote?: boolean;
135
+ }): Promise<{
136
+ pages: CrawledPage[];
137
+ solved: boolean;
138
+ durationMs: number;
139
+ error?: string;
140
+ provider?: string;
141
+ providerCostUsd?: number;
142
+ }>;
143
+ /**
144
+ * Fetches one page for a crawl: cache first (unless bypassed), then a
145
+ * TLS-impersonated fetch. Never throws; failures come back with `error`.
146
+ * Used by `crawlWebsitePages` and by the durable crawl workflow (one call per page).
147
+ */
148
+ export declare function crawlPage(pageUrl: string, options?: {
149
+ bypassCache?: boolean;
150
+ extractStructured?: boolean;
151
+ timeoutMs?: number;
152
+ }): Promise<CrawledPage>;