webcrawl 1.3.2 → 1.3.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/dist/bin.js
CHANGED
|
File without changes
|
|
@@ -43,8 +43,11 @@ export declare function lastFetchTier(): "impit-tls" | "impersonate" | "proxy-im
|
|
|
43
43
|
export declare function lastProxyBytesCount(): number;
|
|
44
44
|
/**
|
|
45
45
|
* Residential proxy URL from env, or null when the tier is disabled.
|
|
46
|
-
*
|
|
47
|
-
* `
|
|
46
|
+
* Generic: a full `WEBCRAWL_PROXY_URL` (`http://user:pass@host:port`) or the
|
|
47
|
+
* split `WEBCRAWL_PROXY_USER` + `WEBCRAWL_PROXY_PWD` pair, with the gateway
|
|
48
|
+
* host/port overridable via `WEBCRAWL_PROXY_HOST` / `WEBCRAWL_PROXY_PORT`.
|
|
49
|
+
* `DECODO_*` equivalents are honored as a legacy fallback so existing
|
|
50
|
+
* deployments keep working (their default gateway is `gate.decodo.com:7000`).
|
|
48
51
|
*/
|
|
49
52
|
export declare function residentialProxyUrl(): string | null;
|
|
50
53
|
/**
|
|
@@ -134,17 +134,22 @@ export function lastProxyBytesCount() {
|
|
|
134
134
|
}
|
|
135
135
|
/**
|
|
136
136
|
* Residential proxy URL from env, or null when the tier is disabled.
|
|
137
|
-
*
|
|
138
|
-
* `
|
|
137
|
+
* Generic: a full `WEBCRAWL_PROXY_URL` (`http://user:pass@host:port`) or the
|
|
138
|
+
* split `WEBCRAWL_PROXY_USER` + `WEBCRAWL_PROXY_PWD` pair, with the gateway
|
|
139
|
+
* host/port overridable via `WEBCRAWL_PROXY_HOST` / `WEBCRAWL_PROXY_PORT`.
|
|
140
|
+
* `DECODO_*` equivalents are honored as a legacy fallback so existing
|
|
141
|
+
* deployments keep working (their default gateway is `gate.decodo.com:7000`).
|
|
139
142
|
*/
|
|
140
143
|
export function residentialProxyUrl() {
|
|
141
|
-
const direct = process.env.DECODO_URL?.trim();
|
|
144
|
+
const direct = process.env.WEBCRAWL_PROXY_URL?.trim() || process.env.DECODO_URL?.trim();
|
|
142
145
|
if (direct)
|
|
143
146
|
return direct;
|
|
144
|
-
const user = process.env.DECODO_USER?.trim();
|
|
145
|
-
const pwd = process.env.DECODO_PWD?.trim();
|
|
147
|
+
const user = process.env.WEBCRAWL_PROXY_USER?.trim() || process.env.DECODO_USER?.trim();
|
|
148
|
+
const pwd = process.env.WEBCRAWL_PROXY_PWD?.trim() || process.env.DECODO_PWD?.trim();
|
|
146
149
|
if (user && pwd) {
|
|
147
|
-
|
|
150
|
+
const host = process.env.WEBCRAWL_PROXY_HOST?.trim() || "gate.decodo.com";
|
|
151
|
+
const port = process.env.WEBCRAWL_PROXY_PORT?.trim() || "7000";
|
|
152
|
+
return `http://${encodeURIComponent(user)}:${encodeURIComponent(pwd)}@${host}:${port}`;
|
|
148
153
|
}
|
|
149
154
|
return null;
|
|
150
155
|
}
|
|
@@ -38,6 +38,15 @@ export type StepAction = Record<string, unknown> & {
|
|
|
38
38
|
*/
|
|
39
39
|
export declare function interactBrowserAvailable(): boolean;
|
|
40
40
|
export declare function interactBackend(): "local" | "sandbox" | "none";
|
|
41
|
+
/**
|
|
42
|
+
* Path to a real, user-installed Chrome when one exists, else null.
|
|
43
|
+
* Anti-bot vendors (DataDome, Akamai) fingerprint the automation build of
|
|
44
|
+
* Chromium and serve degraded pages to it, while the same engine driving a
|
|
45
|
+
* real installed Chrome gets full content — practitioner reports confirm
|
|
46
|
+
* this on DataDome-protected sites. Priority: explicit env override, then
|
|
47
|
+
* the standard per-OS install locations.
|
|
48
|
+
*/
|
|
49
|
+
export declare function installedChromePath(): string | null;
|
|
41
50
|
export declare function startBrowser(opts?: {
|
|
42
51
|
timeoutMs?: number;
|
|
43
52
|
userAgent?: string;
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* (Local World / `next dev`).
|
|
10
10
|
*/
|
|
11
11
|
import { execFile, spawn } from "node:child_process";
|
|
12
|
-
import { mkdtempSync, writeFileSync } from "node:fs";
|
|
12
|
+
import { existsSync, mkdtempSync, writeFileSync } from "node:fs";
|
|
13
13
|
import { tmpdir } from "node:os";
|
|
14
14
|
import path from "node:path";
|
|
15
15
|
import { Sandbox } from "@vercel/sandbox";
|
|
@@ -131,6 +131,38 @@ async function startWarmSandboxBrowser(domain) {
|
|
|
131
131
|
}
|
|
132
132
|
}
|
|
133
133
|
/* ------------------------------ local backend ----------------------------- */
|
|
134
|
+
/**
|
|
135
|
+
* Path to a real, user-installed Chrome when one exists, else null.
|
|
136
|
+
* Anti-bot vendors (DataDome, Akamai) fingerprint the automation build of
|
|
137
|
+
* Chromium and serve degraded pages to it, while the same engine driving a
|
|
138
|
+
* real installed Chrome gets full content — practitioner reports confirm
|
|
139
|
+
* this on DataDome-protected sites. Priority: explicit env override, then
|
|
140
|
+
* the standard per-OS install locations.
|
|
141
|
+
*/
|
|
142
|
+
export function installedChromePath() {
|
|
143
|
+
if (process.env.INTERACT_CHROME_PATH)
|
|
144
|
+
return process.env.INTERACT_CHROME_PATH;
|
|
145
|
+
const candidates = [];
|
|
146
|
+
if (process.platform === "darwin") {
|
|
147
|
+
candidates.push("/Applications/Google Chrome.app/Contents/MacOS/Google Chrome", `${process.env.HOME || ""}/Applications/Google Chrome.app/Contents/MacOS/Google Chrome`);
|
|
148
|
+
}
|
|
149
|
+
else if (process.platform === "win32") {
|
|
150
|
+
candidates.push(`${process.env["ProgramFiles"] || "C:\\Program Files"}\\Google\\Chrome\\Application\\chrome.exe`, `${process.env["ProgramFiles(x86)"] || "C:\\Program Files (x86)"}\\Google\\Chrome\\Application\\chrome.exe`);
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
candidates.push("/usr/bin/google-chrome", "/usr/bin/google-chrome-stable", "/usr/bin/chromium-browser", "/usr/bin/chromium", "/snap/bin/chromium");
|
|
154
|
+
}
|
|
155
|
+
for (const p of candidates) {
|
|
156
|
+
try {
|
|
157
|
+
if (existsSync(p))
|
|
158
|
+
return p;
|
|
159
|
+
}
|
|
160
|
+
catch {
|
|
161
|
+
/* not installed here */
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
134
166
|
async function resolveLocalPlaywright() {
|
|
135
167
|
const candidates = [
|
|
136
168
|
process.env.WC_PLAYWRIGHT,
|
|
@@ -140,7 +172,10 @@ async function resolveLocalPlaywright() {
|
|
|
140
172
|
for (const p of candidates) {
|
|
141
173
|
try {
|
|
142
174
|
const mod = (await import(/* webpackIgnore: true */ p));
|
|
143
|
-
|
|
175
|
+
// Prefer a real installed Chrome over playwright's bundled automation
|
|
176
|
+
// Chromium (see installedChromePath); fall back to the bundle when the
|
|
177
|
+
// host has no Chrome (CI, containers, minimal servers).
|
|
178
|
+
const chrome = installedChromePath() || mod.chromium.executablePath();
|
|
144
179
|
return { playwright: p, chrome };
|
|
145
180
|
}
|
|
146
181
|
catch {
|
package/dist/core/interact/vm.js
CHANGED
|
@@ -188,6 +188,13 @@ try {
|
|
|
188
188
|
// only needs the document, its scripts and the challenge sensor (the
|
|
189
189
|
// sensor host is never on the ad blocklist). A byte cap aborts the rest
|
|
190
190
|
// of the page once the billed total blows past the budget.
|
|
191
|
+
// Anti-bot sensor/challenge hosts are ALWAYS allowed, before any block
|
|
192
|
+
// rule: blocking them would make the DataDome / Cloudflare / AWS WAF
|
|
193
|
+
// challenge unsolvable — no cookie is minted, the interstitial loops
|
|
194
|
+
// forever, and the failed sensor POST is itself a bot signal. The
|
|
195
|
+
// sensor payload is a few KB of form-encoded text, so allowing it
|
|
196
|
+
// costs essentially nothing against the byte budget.
|
|
197
|
+
const allowRe = /(api-js\.datadome\.co|js\.datadome\.co|ct\.captcha-delivery\.com|geo\.captcha-delivery\.com|challenges\.cloudflare\.com|[a-z0-9-]+\.awswaf\.com)/i;
|
|
191
198
|
let proxyBilled = 0;
|
|
192
199
|
if (action.lightweight || action.proxyUser) {
|
|
193
200
|
const skip = new Set(action.proxyUser ? ["image", "media", "font", "stylesheet"] : ["image", "media", "font"]);
|
|
@@ -197,6 +204,7 @@ try {
|
|
|
197
204
|
await page.route("**/*", (r) => {
|
|
198
205
|
try {
|
|
199
206
|
const req = r.request();
|
|
207
|
+
if (allowRe.test(req.url())) return r.continue(); // never block a challenge sensor, even past the cap
|
|
200
208
|
if (proxyBilled > cap) return r.abort();
|
|
201
209
|
if (skip.has(req.resourceType())) return r.abort();
|
|
202
210
|
if (adRe.test(req.url())) return r.abort();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "webcrawl",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.3",
|
|
4
4
|
"description": "Webcrawl Anything CLI & Core Engine — crawl, search, and extract structured evidence from any website, SPA, PDF, Office file, YouTube video, GitHub repo, ArXiv paper, or CVE",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -127,8 +127,8 @@
|
|
|
127
127
|
"@ai-sdk/gateway": "^4.0.123",
|
|
128
128
|
"@firecrawl/pdf-inspector": "^1.25.2",
|
|
129
129
|
"@vercel/sandbox": "^1.0.0",
|
|
130
|
-
"@webcrawl/client": "^1.3.
|
|
131
|
-
"@webcrawl/curl-impersonate": "^1.3.
|
|
130
|
+
"@webcrawl/client": "^1.3.3",
|
|
131
|
+
"@webcrawl/curl-impersonate": "^1.3.3",
|
|
132
132
|
"ai": "^7.0.150",
|
|
133
133
|
"impit": "^0.14.7",
|
|
134
134
|
"mammoth": "^1.13.0",
|