openchrome-mcp 1.7.16 → 1.8.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.
- package/dist/cdp/client.d.ts +36 -1
- package/dist/cdp/client.d.ts.map +1 -1
- package/dist/cdp/client.js +212 -8
- package/dist/cdp/client.js.map +1 -1
- package/dist/cdp/tab-health-monitor.d.ts +63 -0
- package/dist/cdp/tab-health-monitor.d.ts.map +1 -0
- package/dist/cdp/tab-health-monitor.js +131 -0
- package/dist/cdp/tab-health-monitor.js.map +1 -0
- package/dist/chrome/launcher.d.ts +11 -0
- package/dist/chrome/launcher.d.ts.map +1 -1
- package/dist/chrome/launcher.js +17 -0
- package/dist/chrome/launcher.js.map +1 -1
- package/dist/chrome/pool.d.ts +17 -0
- package/dist/chrome/pool.d.ts.map +1 -1
- package/dist/chrome/pool.js +85 -2
- package/dist/chrome/pool.js.map +1 -1
- package/dist/chrome/process-watchdog.d.ts +63 -0
- package/dist/chrome/process-watchdog.d.ts.map +1 -0
- package/dist/chrome/process-watchdog.js +121 -0
- package/dist/chrome/process-watchdog.js.map +1 -0
- package/dist/cli/index.js +0 -0
- package/dist/config/defaults.d.ts +59 -0
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/defaults.js +62 -1
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/tool-tiers.d.ts.map +1 -1
- package/dist/config/tool-tiers.js +3 -0
- package/dist/config/tool-tiers.js.map +1 -1
- package/dist/dom/dom-serializer.js +1 -1
- package/dist/dom/dom-serializer.js.map +1 -1
- package/dist/index.js +111 -0
- package/dist/index.js.map +1 -1
- package/dist/journal/task-journal.d.ts +79 -0
- package/dist/journal/task-journal.d.ts.map +1 -0
- package/dist/journal/task-journal.js +246 -0
- package/dist/journal/task-journal.js.map +1 -0
- package/dist/mcp-server.d.ts +3 -0
- package/dist/mcp-server.d.ts.map +1 -1
- package/dist/mcp-server.js +129 -7
- package/dist/mcp-server.js.map +1 -1
- package/dist/self-healing.d.ts +14 -0
- package/dist/self-healing.d.ts.map +1 -0
- package/dist/self-healing.js +17 -0
- package/dist/self-healing.js.map +1 -0
- package/dist/session-manager.d.ts +14 -3
- package/dist/session-manager.d.ts.map +1 -1
- package/dist/session-manager.js +98 -27
- package/dist/session-manager.js.map +1 -1
- package/dist/session-state-persistence.d.ts +92 -0
- package/dist/session-state-persistence.d.ts.map +1 -0
- package/dist/session-state-persistence.js +206 -0
- package/dist/session-state-persistence.js.map +1 -0
- package/dist/storage-state/storage-state-manager.d.ts +1 -1
- package/dist/storage-state/storage-state-manager.d.ts.map +1 -1
- package/dist/storage-state/storage-state-manager.js +40 -16
- package/dist/storage-state/storage-state-manager.js.map +1 -1
- package/dist/tools/click-element.d.ts.map +1 -1
- package/dist/tools/click-element.js +59 -2
- package/dist/tools/click-element.js.map +1 -1
- package/dist/tools/find.d.ts.map +1 -1
- package/dist/tools/find.js +27 -0
- package/dist/tools/find.js.map +1 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +8 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/interact.d.ts.map +1 -1
- package/dist/tools/interact.js +93 -2
- package/dist/tools/interact.js.map +1 -1
- package/dist/tools/journal.d.ts +8 -0
- package/dist/tools/journal.d.ts.map +1 -0
- package/dist/tools/journal.js +112 -0
- package/dist/tools/journal.js.map +1 -0
- package/dist/tools/list-profiles.d.ts.map +1 -1
- package/dist/tools/list-profiles.js +2 -1
- package/dist/tools/list-profiles.js.map +1 -1
- package/dist/tools/navigate.d.ts.map +1 -1
- package/dist/tools/navigate.js +42 -7
- package/dist/tools/navigate.js.map +1 -1
- package/dist/tools/profile-status.d.ts.map +1 -1
- package/dist/tools/profile-status.js +18 -0
- package/dist/tools/profile-status.js.map +1 -1
- package/dist/tools/session-resume.d.ts +42 -0
- package/dist/tools/session-resume.d.ts.map +1 -0
- package/dist/tools/session-resume.js +248 -0
- package/dist/tools/session-resume.js.map +1 -0
- package/dist/tools/session-snapshot.d.ts +35 -0
- package/dist/tools/session-snapshot.d.ts.map +1 -0
- package/dist/tools/session-snapshot.js +232 -0
- package/dist/tools/session-snapshot.js.map +1 -0
- package/dist/tools/tabs-create.d.ts.map +1 -1
- package/dist/tools/tabs-create.js +13 -2
- package/dist/tools/tabs-create.js.map +1 -1
- package/dist/tools/wait-and-click.d.ts.map +1 -1
- package/dist/tools/wait-and-click.js +47 -4
- package/dist/tools/wait-and-click.js.map +1 -1
- package/dist/types/session.d.ts +2 -0
- package/dist/types/session.d.ts.map +1 -1
- package/dist/utils/ax-element-resolver.d.ts +96 -0
- package/dist/utils/ax-element-resolver.d.ts.map +1 -0
- package/dist/utils/ax-element-resolver.js +256 -0
- package/dist/utils/ax-element-resolver.js.map +1 -0
- package/dist/utils/element-discovery.d.ts.map +1 -1
- package/dist/utils/element-discovery.js +4 -0
- package/dist/utils/element-discovery.js.map +1 -1
- package/dist/utils/ralph/circuit-breaker.d.ts +99 -0
- package/dist/utils/ralph/circuit-breaker.d.ts.map +1 -0
- package/dist/utils/ralph/circuit-breaker.js +234 -0
- package/dist/utils/ralph/circuit-breaker.js.map +1 -0
- package/dist/utils/ralph/hitl-escalation.d.ts +51 -0
- package/dist/utils/ralph/hitl-escalation.d.ts.map +1 -0
- package/dist/utils/ralph/hitl-escalation.js +114 -0
- package/dist/utils/ralph/hitl-escalation.js.map +1 -0
- package/dist/utils/ralph/outcome-classifier.d.ts +44 -0
- package/dist/utils/ralph/outcome-classifier.d.ts.map +1 -0
- package/dist/utils/ralph/outcome-classifier.js +123 -0
- package/dist/utils/ralph/outcome-classifier.js.map +1 -0
- package/dist/utils/ralph/ralph-engine.d.ts +50 -0
- package/dist/utils/ralph/ralph-engine.d.ts.map +1 -0
- package/dist/utils/ralph/ralph-engine.js +341 -0
- package/dist/utils/ralph/ralph-engine.js.map +1 -0
- package/dist/utils/ralph/strategy-learner.d.ts +40 -0
- package/dist/utils/ralph/strategy-learner.d.ts.map +1 -0
- package/dist/utils/ralph/strategy-learner.js +80 -0
- package/dist/utils/ralph/strategy-learner.js.map +1 -0
- package/dist/utils/ralph/timeout-budget.d.ts +43 -0
- package/dist/utils/ralph/timeout-budget.d.ts.map +1 -0
- package/dist/utils/ralph/timeout-budget.js +80 -0
- package/dist/utils/ralph/timeout-budget.js.map +1 -0
- package/dist/watchdog/event-loop-monitor.d.ts +58 -0
- package/dist/watchdog/event-loop-monitor.d.ts.map +1 -0
- package/dist/watchdog/event-loop-monitor.js +85 -0
- package/dist/watchdog/event-loop-monitor.js.map +1 -0
- package/dist/watchdog/health-endpoint.d.ts +44 -0
- package/dist/watchdog/health-endpoint.d.ts.map +1 -0
- package/dist/watchdog/health-endpoint.js +119 -0
- package/dist/watchdog/health-endpoint.js.map +1 -0
- package/package.json +3 -2
- package/dist/chrome/sqlite-cookie-copy.d.ts +0 -46
- package/dist/chrome/sqlite-cookie-copy.d.ts.map +0 -1
- package/dist/chrome/sqlite-cookie-copy.js +0 -151
- package/dist/chrome/sqlite-cookie-copy.js.map +0 -1
- package/dist/config/config-recovery.d.ts +0 -69
- package/dist/config/config-recovery.d.ts.map +0 -1
- package/dist/config/config-recovery.js +0 -302
- package/dist/config/config-recovery.js.map +0 -1
- package/dist/config/session-isolator.d.ts +0 -76
- package/dist/config/session-isolator.d.ts.map +0 -1
- package/dist/config/session-isolator.js +0 -268
- package/dist/config/session-isolator.js.map +0 -1
- package/dist/tools/selector-query.d.ts +0 -6
- package/dist/tools/selector-query.d.ts.map +0 -1
- package/dist/tools/selector-query.js +0 -214
- package/dist/tools/selector-query.js.map +0 -1
- package/dist/tools/worker-create.d.ts +0 -7
- package/dist/tools/worker-create.d.ts.map +0 -1
- package/dist/tools/worker-create.js +0 -62
- package/dist/tools/worker-create.js.map +0 -1
- package/dist/tools/worker-delete.d.ts +0 -6
- package/dist/tools/worker-delete.d.ts.map +0 -1
- package/dist/tools/worker-delete.js +0 -80
- package/dist/tools/worker-delete.js.map +0 -1
- package/dist/tools/worker-list.d.ts +0 -6
- package/dist/tools/worker-list.d.ts.map +0 -1
- package/dist/tools/worker-list.js +0 -67
- package/dist/tools/worker-list.js.map +0 -1
- package/dist/tools/xpath-query.d.ts +0 -6
- package/dist/tools/xpath-query.d.ts.map +0 -1
- package/dist/tools/xpath-query.js +0 -225
- package/dist/tools/xpath-query.js.map +0 -1
package/dist/cdp/client.d.ts
CHANGED
|
@@ -30,6 +30,7 @@ export declare class CDPClient {
|
|
|
30
30
|
private targetDestroyedListeners;
|
|
31
31
|
private reconnectAttempts;
|
|
32
32
|
private consecutiveHeartbeatFailures;
|
|
33
|
+
private consecutiveHeartbeatSuccesses;
|
|
33
34
|
private checkConnectionInFlight;
|
|
34
35
|
private autoLaunch;
|
|
35
36
|
private cookieSourceCache;
|
|
@@ -40,6 +41,12 @@ export declare class CDPClient {
|
|
|
40
41
|
private pendingConnect;
|
|
41
42
|
/** Timestamp of last successful connection verification (heartbeat or active probe). */
|
|
42
43
|
private lastVerifiedAt;
|
|
44
|
+
private heartbeatMode;
|
|
45
|
+
private lastCommandAt;
|
|
46
|
+
private heartbeatModeTimer;
|
|
47
|
+
private reconnectCount;
|
|
48
|
+
private pingLatencies;
|
|
49
|
+
private static readonly MAX_PING_SAMPLES;
|
|
43
50
|
private static readonly COOKIE_CACHE_TTL;
|
|
44
51
|
constructor(options?: CDPClientOptions);
|
|
45
52
|
/**
|
|
@@ -78,6 +85,33 @@ export declare class CDPClient {
|
|
|
78
85
|
* Stop heartbeat monitoring
|
|
79
86
|
*/
|
|
80
87
|
private stopHeartbeat;
|
|
88
|
+
/**
|
|
89
|
+
* Set heartbeat mode. Restarts the heartbeat timer with the new interval.
|
|
90
|
+
*/
|
|
91
|
+
setHeartbeatMode(mode: 'idle' | 'active' | 'heavy' | 'recovery'): void;
|
|
92
|
+
/**
|
|
93
|
+
* Get effective heartbeat interval based on current mode.
|
|
94
|
+
*/
|
|
95
|
+
private getEffectiveHeartbeatInterval;
|
|
96
|
+
/**
|
|
97
|
+
* Record that a command was executed (for idle detection).
|
|
98
|
+
*/
|
|
99
|
+
recordCommandActivity(): void;
|
|
100
|
+
/**
|
|
101
|
+
* Get current heartbeat mode.
|
|
102
|
+
*/
|
|
103
|
+
getHeartbeatMode(): 'idle' | 'active' | 'heavy' | 'recovery';
|
|
104
|
+
/**
|
|
105
|
+
* Get connection health metrics.
|
|
106
|
+
*/
|
|
107
|
+
getConnectionMetrics(): {
|
|
108
|
+
msSinceLastVerified: number;
|
|
109
|
+
reconnectCount: number;
|
|
110
|
+
avgPingLatencyMs: number;
|
|
111
|
+
heartbeatMode: string;
|
|
112
|
+
consecutiveSuccesses: number;
|
|
113
|
+
lastVerifiedAt: number;
|
|
114
|
+
};
|
|
81
115
|
/**
|
|
82
116
|
* Check connection health.
|
|
83
117
|
* Sends an active CDP probe (Browser.getVersion) to detect half-open WebSocket
|
|
@@ -206,7 +240,8 @@ export declare class CDPClient {
|
|
|
206
240
|
*/
|
|
207
241
|
getCDPSession(page: Page): Promise<CDPSession>;
|
|
208
242
|
/**
|
|
209
|
-
* Execute CDP command on a page
|
|
243
|
+
* Execute CDP command on a page.
|
|
244
|
+
* Wrapped with per-call timeout to prevent hung renderers from blocking indefinitely.
|
|
210
245
|
*/
|
|
211
246
|
send<T = unknown>(page: Page, method: string, params?: Record<string, unknown>): Promise<T>;
|
|
212
247
|
/**
|
package/dist/cdp/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/cdp/client.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAkB,EAAE,OAAO,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/cdp/client.ts"],"names":[],"mappings":"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"}
|
package/dist/cdp/client.js
CHANGED
|
@@ -49,6 +49,18 @@ const smart_goto_1 = require("../utils/smart-goto");
|
|
|
49
49
|
const puppeteer_helpers_1 = require("../utils/puppeteer-helpers");
|
|
50
50
|
const ref_id_manager_1 = require("../utils/ref-id-manager");
|
|
51
51
|
const defaults_1 = require("../config/defaults");
|
|
52
|
+
const with_timeout_1 = require("../utils/with-timeout");
|
|
53
|
+
function parseEnvInt(name, fallback) {
|
|
54
|
+
const raw = process.env[name];
|
|
55
|
+
if (raw === undefined)
|
|
56
|
+
return fallback;
|
|
57
|
+
const parsed = Number(raw);
|
|
58
|
+
if (!Number.isFinite(parsed) || parsed < 0) {
|
|
59
|
+
console.error(`[CDPClient] Invalid value for ${name}="${raw}", using default ${fallback}`);
|
|
60
|
+
return fallback;
|
|
61
|
+
}
|
|
62
|
+
return parsed;
|
|
63
|
+
}
|
|
52
64
|
class CDPClient {
|
|
53
65
|
browser = null;
|
|
54
66
|
sessions = new Map();
|
|
@@ -62,6 +74,7 @@ class CDPClient {
|
|
|
62
74
|
targetDestroyedListeners = [];
|
|
63
75
|
reconnectAttempts = 0;
|
|
64
76
|
consecutiveHeartbeatFailures = 0;
|
|
77
|
+
consecutiveHeartbeatSuccesses = 0;
|
|
65
78
|
checkConnectionInFlight = false;
|
|
66
79
|
autoLaunch;
|
|
67
80
|
cookieSourceCache = new Map();
|
|
@@ -72,13 +85,21 @@ class CDPClient {
|
|
|
72
85
|
pendingConnect = null;
|
|
73
86
|
/** Timestamp of last successful connection verification (heartbeat or active probe). */
|
|
74
87
|
lastVerifiedAt = 0;
|
|
88
|
+
// Adaptive heartbeat state
|
|
89
|
+
heartbeatMode = 'active';
|
|
90
|
+
lastCommandAt = 0;
|
|
91
|
+
heartbeatModeTimer = null;
|
|
92
|
+
// Connection health metrics
|
|
93
|
+
reconnectCount = 0;
|
|
94
|
+
pingLatencies = []; // rolling window
|
|
95
|
+
static MAX_PING_SAMPLES = 60; // ~5 min at 5s interval
|
|
75
96
|
static COOKIE_CACHE_TTL = 300000; // 5 minutes
|
|
76
97
|
constructor(options = {}) {
|
|
77
98
|
const globalConfig = (0, global_1.getGlobalConfig)();
|
|
78
99
|
this.port = options.port || globalConfig.port;
|
|
79
|
-
this.maxReconnectAttempts = options.maxReconnectAttempts
|
|
80
|
-
this.reconnectDelayMs = options.reconnectDelayMs
|
|
81
|
-
this.heartbeatIntervalMs = options.heartbeatIntervalMs
|
|
100
|
+
this.maxReconnectAttempts = options.maxReconnectAttempts ?? parseEnvInt('OPENCHROME_MAX_RECONNECT_ATTEMPTS', defaults_1.DEFAULT_MAX_RECONNECT_ATTEMPTS);
|
|
101
|
+
this.reconnectDelayMs = options.reconnectDelayMs ?? parseEnvInt('OPENCHROME_RECONNECT_DELAY_MS', defaults_1.DEFAULT_RECONNECT_DELAY_MS);
|
|
102
|
+
this.heartbeatIntervalMs = options.heartbeatIntervalMs ?? parseEnvInt('OPENCHROME_HEARTBEAT_INTERVAL_MS', defaults_1.DEFAULT_HEARTBEAT_INTERVAL_MS);
|
|
82
103
|
// Use explicit option if provided, otherwise use global config
|
|
83
104
|
this.autoLaunch = options.autoLaunch !== undefined ? options.autoLaunch : globalConfig.autoLaunch;
|
|
84
105
|
}
|
|
@@ -181,8 +202,15 @@ class CDPClient {
|
|
|
181
202
|
});
|
|
182
203
|
return;
|
|
183
204
|
}
|
|
205
|
+
// Check for idle transition: no commands for 5 minutes → switch to idle mode
|
|
206
|
+
if ((this.heartbeatMode === 'active' || this.heartbeatMode === 'heavy')
|
|
207
|
+
&& this.lastCommandAt > 0
|
|
208
|
+
&& now - this.lastCommandAt > 300000) {
|
|
209
|
+
this.setHeartbeatMode('idle');
|
|
210
|
+
return; // setHeartbeatMode restarts the timer with new interval
|
|
211
|
+
}
|
|
184
212
|
this.checkConnection();
|
|
185
|
-
}, this.
|
|
213
|
+
}, this.getEffectiveHeartbeatInterval());
|
|
186
214
|
}
|
|
187
215
|
/**
|
|
188
216
|
* Stop heartbeat monitoring
|
|
@@ -193,6 +221,76 @@ class CDPClient {
|
|
|
193
221
|
this.heartbeatTimer = null;
|
|
194
222
|
}
|
|
195
223
|
}
|
|
224
|
+
/**
|
|
225
|
+
* Set heartbeat mode. Restarts the heartbeat timer with the new interval.
|
|
226
|
+
*/
|
|
227
|
+
setHeartbeatMode(mode) {
|
|
228
|
+
if (this.heartbeatMode === mode)
|
|
229
|
+
return;
|
|
230
|
+
const oldMode = this.heartbeatMode;
|
|
231
|
+
this.heartbeatMode = mode;
|
|
232
|
+
console.error(`[CDPClient] Heartbeat mode: ${oldMode} → ${mode} (interval: ${this.getEffectiveHeartbeatInterval()}ms)`);
|
|
233
|
+
// Restart heartbeat with new interval
|
|
234
|
+
if (this.heartbeatTimer) {
|
|
235
|
+
this.startHeartbeat();
|
|
236
|
+
}
|
|
237
|
+
// Auto-transition from recovery to active after 30s
|
|
238
|
+
if (this.heartbeatModeTimer) {
|
|
239
|
+
clearTimeout(this.heartbeatModeTimer);
|
|
240
|
+
this.heartbeatModeTimer = null;
|
|
241
|
+
}
|
|
242
|
+
if (mode === 'recovery') {
|
|
243
|
+
this.heartbeatModeTimer = setTimeout(() => {
|
|
244
|
+
this.heartbeatModeTimer = null;
|
|
245
|
+
if (this.heartbeatMode === 'recovery') {
|
|
246
|
+
this.setHeartbeatMode('active');
|
|
247
|
+
}
|
|
248
|
+
}, 30000);
|
|
249
|
+
this.heartbeatModeTimer.unref();
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Get effective heartbeat interval based on current mode.
|
|
254
|
+
*/
|
|
255
|
+
getEffectiveHeartbeatInterval() {
|
|
256
|
+
switch (this.heartbeatMode) {
|
|
257
|
+
case 'idle': return Math.max(this.heartbeatIntervalMs * 3, 15000); // 3x base or 15s min
|
|
258
|
+
case 'active': return this.heartbeatIntervalMs; // default (5s)
|
|
259
|
+
case 'heavy': return Math.max(Math.floor(this.heartbeatIntervalMs / 2), 2000); // half or 2s min
|
|
260
|
+
case 'recovery': return 1000; // 1s fixed during recovery
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Record that a command was executed (for idle detection).
|
|
265
|
+
*/
|
|
266
|
+
recordCommandActivity() {
|
|
267
|
+
this.lastCommandAt = Date.now();
|
|
268
|
+
if (this.heartbeatMode === 'idle') {
|
|
269
|
+
this.setHeartbeatMode('active');
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Get current heartbeat mode.
|
|
274
|
+
*/
|
|
275
|
+
getHeartbeatMode() {
|
|
276
|
+
return this.heartbeatMode;
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Get connection health metrics.
|
|
280
|
+
*/
|
|
281
|
+
getConnectionMetrics() {
|
|
282
|
+
const avgLatency = this.pingLatencies.length > 0
|
|
283
|
+
? Math.round(this.pingLatencies.reduce((a, b) => a + b, 0) / this.pingLatencies.length)
|
|
284
|
+
: 0;
|
|
285
|
+
return {
|
|
286
|
+
msSinceLastVerified: this.lastVerifiedAt > 0 ? Date.now() - this.lastVerifiedAt : 0,
|
|
287
|
+
reconnectCount: this.reconnectCount,
|
|
288
|
+
avgPingLatencyMs: avgLatency,
|
|
289
|
+
heartbeatMode: this.heartbeatMode,
|
|
290
|
+
consecutiveSuccesses: this.consecutiveHeartbeatSuccesses,
|
|
291
|
+
lastVerifiedAt: this.lastVerifiedAt,
|
|
292
|
+
};
|
|
293
|
+
}
|
|
196
294
|
/**
|
|
197
295
|
* Check connection health.
|
|
198
296
|
* Sends an active CDP probe (Browser.getVersion) to detect half-open WebSocket
|
|
@@ -215,6 +313,7 @@ class CDPClient {
|
|
|
215
313
|
// Active probe: round-trip CDP command to detect dead WebSocket connections.
|
|
216
314
|
// browser.isConnected() only checks a local flag — half-open TCP connections
|
|
217
315
|
// (macOS sleep/wake, Chrome crash) pass the flag check but hang on real commands.
|
|
316
|
+
const pingStart = Date.now();
|
|
218
317
|
let pingTid;
|
|
219
318
|
await Promise.race([
|
|
220
319
|
this.browser.version().finally(() => clearTimeout(pingTid)),
|
|
@@ -223,10 +322,17 @@ class CDPClient {
|
|
|
223
322
|
}),
|
|
224
323
|
]);
|
|
225
324
|
this.lastVerifiedAt = Date.now();
|
|
325
|
+
const pingLatency = Date.now() - pingStart;
|
|
326
|
+
this.pingLatencies.push(pingLatency);
|
|
327
|
+
if (this.pingLatencies.length > CDPClient.MAX_PING_SAMPLES) {
|
|
328
|
+
this.pingLatencies.shift();
|
|
329
|
+
}
|
|
330
|
+
this.consecutiveHeartbeatSuccesses++;
|
|
226
331
|
this.consecutiveHeartbeatFailures = 0;
|
|
227
332
|
return true;
|
|
228
333
|
}
|
|
229
334
|
catch (error) {
|
|
335
|
+
this.consecutiveHeartbeatSuccesses = 0;
|
|
230
336
|
this.consecutiveHeartbeatFailures++;
|
|
231
337
|
if (this.consecutiveHeartbeatFailures < 2) {
|
|
232
338
|
// First failure: warn but don't disconnect. Chrome may be under heavy load.
|
|
@@ -256,6 +362,11 @@ class CDPClient {
|
|
|
256
362
|
type: 'disconnected',
|
|
257
363
|
timestamp: Date.now(),
|
|
258
364
|
});
|
|
365
|
+
// Clear heartbeat mode timer to prevent 30s recovery timer from leaking
|
|
366
|
+
if (this.heartbeatModeTimer) {
|
|
367
|
+
clearTimeout(this.heartbeatModeTimer);
|
|
368
|
+
this.heartbeatModeTimer = null;
|
|
369
|
+
}
|
|
259
370
|
// Clear existing sessions and stale state
|
|
260
371
|
this.sessions.clear();
|
|
261
372
|
this.targetIdIndex.clear();
|
|
@@ -275,6 +386,10 @@ class CDPClient {
|
|
|
275
386
|
while (this.reconnectAttempts < this.maxReconnectAttempts) {
|
|
276
387
|
this.reconnectAttempts++;
|
|
277
388
|
console.error(`[CDPClient] Reconnect attempt ${this.reconnectAttempts}/${this.maxReconnectAttempts}...`);
|
|
389
|
+
// Invalidate launcher cache at start of each attempt so ensureChrome()
|
|
390
|
+
// re-probes Chrome's HTTP endpoint to discover the new WebSocket UUID
|
|
391
|
+
// after a relaunch by the process watchdog
|
|
392
|
+
(0, launcher_1.getChromeLauncher)(this.port).invalidateInstance();
|
|
278
393
|
this.emitConnectionEvent({
|
|
279
394
|
type: 'reconnecting',
|
|
280
395
|
timestamp: Date.now(),
|
|
@@ -284,6 +399,8 @@ class CDPClient {
|
|
|
284
399
|
await this.connectInternal({ autoLaunch: false });
|
|
285
400
|
console.error('[CDPClient] Reconnection successful');
|
|
286
401
|
this.reconnectAttempts = 0;
|
|
402
|
+
this.reconnectCount++;
|
|
403
|
+
this.setHeartbeatMode('recovery');
|
|
287
404
|
this.emitConnectionEvent({
|
|
288
405
|
type: 'reconnected',
|
|
289
406
|
timestamp: Date.now(),
|
|
@@ -293,7 +410,10 @@ class CDPClient {
|
|
|
293
410
|
catch (error) {
|
|
294
411
|
console.error(`[CDPClient] Reconnect attempt ${this.reconnectAttempts} failed:`, error);
|
|
295
412
|
if (this.reconnectAttempts < this.maxReconnectAttempts) {
|
|
296
|
-
|
|
413
|
+
// Exponential backoff with jitter: baseDelay * 2^(attempt-1) + random(0..baseDelay/2)
|
|
414
|
+
const backoffDelay = Math.min(this.reconnectDelayMs * Math.pow(2, this.reconnectAttempts - 1) + Math.floor(Math.random() * this.reconnectDelayMs / 2), 30000);
|
|
415
|
+
console.error(`[CDPClient] Waiting ${backoffDelay}ms before next attempt (exponential backoff)...`);
|
|
416
|
+
await new Promise(resolve => setTimeout(resolve, backoffDelay));
|
|
297
417
|
}
|
|
298
418
|
}
|
|
299
419
|
}
|
|
@@ -514,6 +634,10 @@ class CDPClient {
|
|
|
514
634
|
// Invalidate any in-flight connect() — we're replacing the connection entirely
|
|
515
635
|
this.pendingConnect = null;
|
|
516
636
|
this.stopHeartbeat();
|
|
637
|
+
if (this.heartbeatModeTimer) {
|
|
638
|
+
clearTimeout(this.heartbeatModeTimer);
|
|
639
|
+
this.heartbeatModeTimer = null;
|
|
640
|
+
}
|
|
517
641
|
if (this.browser) {
|
|
518
642
|
try {
|
|
519
643
|
this.browser.removeAllListeners('disconnected');
|
|
@@ -556,6 +680,10 @@ class CDPClient {
|
|
|
556
680
|
*/
|
|
557
681
|
async disconnect() {
|
|
558
682
|
this.stopHeartbeat();
|
|
683
|
+
if (this.heartbeatModeTimer) {
|
|
684
|
+
clearTimeout(this.heartbeatModeTimer);
|
|
685
|
+
this.heartbeatModeTimer = null;
|
|
686
|
+
}
|
|
559
687
|
if (this.browser) {
|
|
560
688
|
try {
|
|
561
689
|
this.browser.removeAllListeners('disconnected');
|
|
@@ -980,7 +1108,7 @@ class CDPClient {
|
|
|
980
1108
|
* @param settleMs Milliseconds to wait before attaching CDP (default 5000, range 1000-30000)
|
|
981
1109
|
* @returns The Puppeteer Page and its targetId
|
|
982
1110
|
*/
|
|
983
|
-
async createTargetStealth(url, settleMs =
|
|
1111
|
+
async createTargetStealth(url, settleMs = 8000) {
|
|
984
1112
|
const browser = this.getBrowser();
|
|
985
1113
|
// Step 1: Create target via CDP Target.createTarget.
|
|
986
1114
|
// Puppeteer's ChromeTargetManager uses Target.setAutoAttach with { exclude: true }
|
|
@@ -998,6 +1126,16 @@ class CDPClient {
|
|
|
998
1126
|
throw new Error(`Stealth navigation: failed to create target: ${createErr instanceof Error ? createErr.message : String(createErr)}`);
|
|
999
1127
|
}
|
|
1000
1128
|
console.error(`[CDPClient] Stealth tab created: ${targetId}, settling for ${settleMs}ms`);
|
|
1129
|
+
// Warn if headless — Turnstile detection is nearly guaranteed in headless mode
|
|
1130
|
+
try {
|
|
1131
|
+
const version = await browser.version();
|
|
1132
|
+
if (version.toLowerCase().includes('headless')) {
|
|
1133
|
+
console.error('[CDPClient] WARNING: Stealth mode in headless Chrome is unlikely to bypass Turnstile. Use headed Chrome (--visible) for anti-bot pages.');
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
catch {
|
|
1137
|
+
// Version check failed — continue
|
|
1138
|
+
}
|
|
1001
1139
|
// Step 2: Wait for the page to load without CDP observation (Turnstile runs here)
|
|
1002
1140
|
await new Promise(resolve => setTimeout(resolve, settleMs));
|
|
1003
1141
|
// Step 3: Attach to the target via CDP to bring it into Puppeteer's attached set
|
|
@@ -1101,6 +1239,37 @@ class CDPClient {
|
|
|
1101
1239
|
configurable: true,
|
|
1102
1240
|
});
|
|
1103
1241
|
}
|
|
1242
|
+
// 6. window dimensions — headless Chrome returns 0
|
|
1243
|
+
if (window.outerWidth === 0) {
|
|
1244
|
+
Object.defineProperty(window, 'outerWidth', { get: () => window.innerWidth, configurable: true });
|
|
1245
|
+
}
|
|
1246
|
+
if (window.outerHeight === 0) {
|
|
1247
|
+
Object.defineProperty(window, 'outerHeight', { get: () => window.innerHeight + 85, configurable: true });
|
|
1248
|
+
}
|
|
1249
|
+
// 7. navigator.mimeTypes — headless has 0 mimeTypes
|
|
1250
|
+
if (navigator.mimeTypes.length === 0) {
|
|
1251
|
+
Object.defineProperty(navigator, 'mimeTypes', {
|
|
1252
|
+
get: () => {
|
|
1253
|
+
const mt = typeof MimeTypeArray !== 'undefined' ? Object.create(MimeTypeArray.prototype) : [];
|
|
1254
|
+
mt[0] = { type: 'application/pdf', suffixes: 'pdf', description: 'Portable Document Format' };
|
|
1255
|
+
Object.defineProperty(mt, 'length', { value: 1 });
|
|
1256
|
+
mt.item = (i) => mt[i] || null;
|
|
1257
|
+
mt.namedItem = (name) => name === 'application/pdf' ? mt[0] : null;
|
|
1258
|
+
return mt;
|
|
1259
|
+
},
|
|
1260
|
+
configurable: true,
|
|
1261
|
+
});
|
|
1262
|
+
}
|
|
1263
|
+
// 8. chrome.app and chrome.loadTimes stubs
|
|
1264
|
+
if (window.chrome) {
|
|
1265
|
+
const c = window.chrome;
|
|
1266
|
+
if (!c.app) {
|
|
1267
|
+
c.app = { isInstalled: false, getDetails: () => null, getIsInstalled: () => false, installState: () => 'disabled' };
|
|
1268
|
+
}
|
|
1269
|
+
if (!c.loadTimes) {
|
|
1270
|
+
c.loadTimes = () => ({});
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1104
1273
|
}).catch(() => { });
|
|
1105
1274
|
console.error(`[CDPClient] Stealth tab ${targetId} attached after settle period`);
|
|
1106
1275
|
return { page, targetId };
|
|
@@ -1216,6 +1385,40 @@ class CDPClient {
|
|
|
1216
1385
|
});
|
|
1217
1386
|
}
|
|
1218
1387
|
}).catch(() => { });
|
|
1388
|
+
// Defense 4: window dimensions + chrome stubs (anti-headless, #361)
|
|
1389
|
+
page.evaluateOnNewDocument(() => {
|
|
1390
|
+
// outerWidth/outerHeight — headless Chrome returns 0
|
|
1391
|
+
if (window.outerWidth === 0) {
|
|
1392
|
+
Object.defineProperty(window, 'outerWidth', { get: () => window.innerWidth, configurable: true });
|
|
1393
|
+
}
|
|
1394
|
+
if (window.outerHeight === 0) {
|
|
1395
|
+
Object.defineProperty(window, 'outerHeight', { get: () => window.innerHeight + 85, configurable: true });
|
|
1396
|
+
}
|
|
1397
|
+
// navigator.mimeTypes — headless has 0 mimeTypes
|
|
1398
|
+
if (navigator.mimeTypes.length === 0) {
|
|
1399
|
+
Object.defineProperty(navigator, 'mimeTypes', {
|
|
1400
|
+
get: () => {
|
|
1401
|
+
const mt = typeof MimeTypeArray !== 'undefined' ? Object.create(MimeTypeArray.prototype) : [];
|
|
1402
|
+
mt[0] = { type: 'application/pdf', suffixes: 'pdf', description: 'Portable Document Format' };
|
|
1403
|
+
Object.defineProperty(mt, 'length', { value: 1 });
|
|
1404
|
+
mt.item = (i) => mt[i] || null;
|
|
1405
|
+
mt.namedItem = (name) => name === 'application/pdf' ? mt[0] : null;
|
|
1406
|
+
return mt;
|
|
1407
|
+
},
|
|
1408
|
+
configurable: true,
|
|
1409
|
+
});
|
|
1410
|
+
}
|
|
1411
|
+
// chrome.app and chrome.loadTimes stubs
|
|
1412
|
+
if (window.chrome) {
|
|
1413
|
+
const c = window.chrome;
|
|
1414
|
+
if (!c.app) {
|
|
1415
|
+
c.app = { isInstalled: false, getDetails: () => null, getIsInstalled: () => false, installState: () => 'disabled' };
|
|
1416
|
+
}
|
|
1417
|
+
if (!c.loadTimes) {
|
|
1418
|
+
c.loadTimes = () => ({});
|
|
1419
|
+
}
|
|
1420
|
+
}
|
|
1421
|
+
}).catch(() => { });
|
|
1219
1422
|
// Deny file downloads by default — Content-Disposition: attachment
|
|
1220
1423
|
// responses block the navigation promise indefinitely.
|
|
1221
1424
|
this.send(page, 'Page.setDownloadBehavior', { behavior: 'deny' }).catch(() => { });
|
|
@@ -1310,11 +1513,12 @@ class CDPClient {
|
|
|
1310
1513
|
return session;
|
|
1311
1514
|
}
|
|
1312
1515
|
/**
|
|
1313
|
-
* Execute CDP command on a page
|
|
1516
|
+
* Execute CDP command on a page.
|
|
1517
|
+
* Wrapped with per-call timeout to prevent hung renderers from blocking indefinitely.
|
|
1314
1518
|
*/
|
|
1315
1519
|
async send(page, method, params) {
|
|
1316
1520
|
const session = await this.getCDPSession(page);
|
|
1317
|
-
return session.send(method, params);
|
|
1521
|
+
return (0, with_timeout_1.withTimeout)(session.send(method, params), defaults_1.DEFAULT_CDP_SEND_TIMEOUT_MS, `CDP ${method}`);
|
|
1318
1522
|
}
|
|
1319
1523
|
/**
|
|
1320
1524
|
* Get all targets
|