@poolzin/pool-bot 2026.3.22 → 2026.3.23

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 (124) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/dist/acp/bindings-store.js +209 -0
  3. package/dist/acp/control-plane/runtime-cache.js +54 -0
  4. package/dist/acp/control-plane/runtime-options.js +215 -0
  5. package/dist/acp/control-plane/session-actor-queue.js +36 -0
  6. package/dist/acp/runtime/errors.js +47 -0
  7. package/dist/acp/runtime/registry.js +86 -0
  8. package/dist/acp/runtime/types.js +1 -0
  9. package/dist/acp/translator.js +97 -0
  10. package/dist/agents/failover-error.js +145 -47
  11. package/dist/browser/browser-profile-manager.js +319 -0
  12. package/dist/browser/cdp-proxy-bypass.js +129 -0
  13. package/dist/browser/cdp-timeouts.js +41 -0
  14. package/dist/browser/chrome-extension-validator.js +406 -0
  15. package/dist/browser/chrome-mcp-snapshot.js +222 -0
  16. package/dist/browser/chrome-mcp.js +421 -0
  17. package/dist/browser/chrome-mcp.snapshot.js +133 -0
  18. package/dist/browser/errors.js +67 -0
  19. package/dist/browser/form-fields.js +22 -0
  20. package/dist/browser/output-atomic.js +44 -0
  21. package/dist/browser/profile-capabilities.js +47 -0
  22. package/dist/browser/safe-filename.js +25 -0
  23. package/dist/browser/snapshot-roles.js +60 -0
  24. package/dist/build-info.json +3 -3
  25. package/dist/commands/security-owner-only.js +86 -0
  26. package/dist/control-ui/assets/{index-Dvkl4Xlx.js → index-D7shnQwQ.js} +404 -388
  27. package/dist/control-ui/assets/index-D7shnQwQ.js.map +1 -0
  28. package/dist/control-ui/index.html +1 -1
  29. package/dist/cron/cron-filters.js +150 -0
  30. package/dist/gateway/device-pairing-security.js +197 -0
  31. package/dist/gateway/event-deduplication.js +167 -0
  32. package/dist/gateway/run-tracker.js +253 -0
  33. package/dist/gateway/server-methods/nodes.js +14 -0
  34. package/dist/gateway/websocket-preauth-security.js +188 -0
  35. package/dist/infra/errors.js +53 -13
  36. package/dist/infra/exec-approvals-security.js +217 -0
  37. package/dist/infra/security/command-analyzer.js +257 -0
  38. package/dist/plugins/loader.js +16 -8
  39. package/dist/security/external-content.js +51 -1
  40. package/dist/sessions/session-costs.js +228 -0
  41. package/dist/shared/param-key.js +16 -0
  42. package/dist/shared/poll-params.js +58 -0
  43. package/dist/shared/polls.js +55 -0
  44. package/docs/DASHBOARD-GAP-ANALYSIS-AND-PLAN.md +430 -0
  45. package/docs/FEATURES.md +523 -0
  46. package/docs/FINAL-IMPLEMENTATION-REVIEW.md +274 -0
  47. package/docs/FINAL-IMPLEMENTATION-SUMMARY.md +356 -0
  48. package/docs/FINAL-PROFESSIONAL-EVALUATION.md +312 -0
  49. package/docs/IMPLEMENTATION-PRIORITY-EVALUATION.md +298 -0
  50. package/docs/IMPLEMENTATION-PROGRESS.md +237 -0
  51. package/docs/IMPLEMENTATION-REVIEW-PHASE1-2.md +381 -0
  52. package/docs/IMPLEMENTATION-REVIEW-PHASE4.md +389 -0
  53. package/docs/IMPLEMENTATION-REVIEW-PHASE5.md +420 -0
  54. package/docs/IMPLEMENTATION-REVIEW-PHASE6.md +422 -0
  55. package/docs/IMPLEMENTATION-REVIEW-PHASE7-FINAL.md +184 -0
  56. package/docs/MIKRODASH-ANALYSIS.md +412 -0
  57. package/docs/OPENCLAW-GAP-ANALYSIS-FINAL.md +431 -0
  58. package/docs/OPENCLAW-VS-POOLBOT-ANALYSIS.md +351 -0
  59. package/docs/PHASE-7-SUMMARY.md +144 -0
  60. package/docs/POOLBOT-OFFICE-PLAN.md +697 -0
  61. package/docs/PROJECT-FINAL-STATUS.md +237 -0
  62. package/docs/README.md +116 -0
  63. package/docs/REAL-IMPROVEMENTS-EVALUATION.md +477 -0
  64. package/docs/SECURITY-HARDENING-IMPLEMENTATION.md +161 -0
  65. package/docs/channels/googlechat.md +235 -206
  66. package/docs/channels/irc.md +332 -0
  67. package/docs/channels/nostr.md +255 -168
  68. package/docs/components/command-palette.md +166 -0
  69. package/docs/components/login-gate.md +219 -0
  70. package/docs/getting-started/installation.md +191 -0
  71. package/docs/getting-started/introduction.md +120 -0
  72. package/docs/improvements/USAGE-GUIDE.md +359 -0
  73. package/docs/plans/2026-03-15-openclaw-features-implementation.md +1632 -0
  74. package/docs/reference/deadcode-detection.md +72 -0
  75. package/extensions/acpx/node_modules/.bin/acpx +21 -0
  76. package/extensions/agency-agents/node_modules/.bin/vite +4 -4
  77. package/extensions/agency-agents/node_modules/.bin/vitest +2 -2
  78. package/extensions/googlechat/node_modules/.bin/tsc +21 -0
  79. package/extensions/googlechat/node_modules/.bin/tsserver +21 -0
  80. package/extensions/googlechat/node_modules/.bin/vitest +21 -0
  81. package/extensions/googlechat/package.json +11 -28
  82. package/extensions/googlechat/src/googlechat-channel.test.ts +60 -0
  83. package/extensions/googlechat/src/googlechat-channel.ts +120 -0
  84. package/extensions/googlechat/src/index.ts +14 -0
  85. package/extensions/irc/node_modules/.bin/tsc +21 -0
  86. package/extensions/irc/node_modules/.bin/tsserver +21 -0
  87. package/extensions/irc/node_modules/.bin/vitest +21 -0
  88. package/extensions/irc/package.json +16 -8
  89. package/extensions/irc/src/index.ts +14 -0
  90. package/extensions/irc/src/irc-channel.test.ts +43 -0
  91. package/extensions/irc/src/irc-channel.ts +191 -0
  92. package/extensions/keyed-async-queue/node_modules/.bin/tsc +21 -0
  93. package/extensions/keyed-async-queue/node_modules/.bin/tsserver +21 -0
  94. package/extensions/keyed-async-queue/node_modules/.bin/vitest +21 -0
  95. package/extensions/keyed-async-queue/package.json +20 -0
  96. package/extensions/keyed-async-queue/src/index.ts +14 -0
  97. package/extensions/keyed-async-queue/src/queue.test.ts +135 -0
  98. package/extensions/keyed-async-queue/src/queue.ts +200 -0
  99. package/extensions/memory-core/node_modules/.bin/tsc +21 -0
  100. package/extensions/memory-core/node_modules/.bin/tsserver +21 -0
  101. package/extensions/memory-core/node_modules/.bin/vitest +21 -0
  102. package/extensions/memory-core/package.json +11 -8
  103. package/extensions/memory-core/src/index.ts +14 -0
  104. package/extensions/memory-core/src/memory-manager.test.ts +124 -0
  105. package/extensions/memory-core/src/memory-manager.ts +186 -0
  106. package/extensions/nostr/node_modules/.bin/tsc +2 -2
  107. package/extensions/nostr/node_modules/.bin/tsserver +2 -2
  108. package/extensions/nostr/node_modules/.bin/vitest +21 -0
  109. package/extensions/nostr/package.json +15 -24
  110. package/extensions/nostr/src/index.ts +14 -0
  111. package/extensions/nostr/src/nostr-channel.test.ts +55 -0
  112. package/extensions/nostr/src/nostr-channel.ts +228 -0
  113. package/extensions/page-agent/node_modules/.bin/vitest +2 -2
  114. package/extensions/test-utils/node_modules/.bin/jiti +21 -0
  115. package/extensions/test-utils/node_modules/.bin/playwright +21 -0
  116. package/extensions/test-utils/node_modules/.bin/tsx +21 -0
  117. package/extensions/test-utils/node_modules/.bin/vite +21 -0
  118. package/extensions/test-utils/node_modules/.bin/vitest +21 -0
  119. package/extensions/test-utils/node_modules/.bin/yaml +21 -0
  120. package/extensions/xyops/node_modules/.bin/vitest +2 -2
  121. package/package.json +2 -1
  122. package/dist/control-ui/assets/index-Dvkl4Xlx.js.map +0 -1
  123. package/extensions/googlechat/node_modules/.bin/poolbot +0 -21
  124. package/extensions/memory-core/node_modules/.bin/poolbot +0 -21
@@ -0,0 +1,319 @@
1
+ /**
2
+ * Browser Profile Manager
3
+ *
4
+ * Manages multiple Chrome profiles with isolated user data directories.
5
+ * Enables parallel browser sessions with separate cookies, storage, and preferences.
6
+ */
7
+ import fs from "node:fs/promises";
8
+ import path from "node:path";
9
+ import os from "node:os";
10
+ import playwright from "playwright-core";
11
+ export class BrowserProfileManager {
12
+ profiles = new Map();
13
+ activeSessions = new Map();
14
+ baseDir;
15
+ IDLE_TIMEOUT_MS;
16
+ MAX_SESSIONS;
17
+ cleanupInterval = null;
18
+ constructor(options) {
19
+ this.baseDir = options?.baseDir ?? path.join(os.tmpdir(), "poolbot-browser-profiles");
20
+ this.IDLE_TIMEOUT_MS = options?.idleTimeoutMs ?? 300000; // 5 minutes
21
+ this.MAX_SESSIONS = options?.maxSessions ?? 10;
22
+ if (options?.autoCleanup !== false) {
23
+ this.startCleanupInterval();
24
+ }
25
+ }
26
+ /**
27
+ * Initialize manager - create base directory
28
+ */
29
+ async initialize() {
30
+ await fs.mkdir(this.baseDir, { recursive: true });
31
+ }
32
+ /**
33
+ * Create a new browser profile
34
+ */
35
+ async createProfile(config) {
36
+ if (this.profiles.has(config.name)) {
37
+ throw new Error(`Profile ${config.name} already exists`);
38
+ }
39
+ const userDataDir = config.userDataDir ?? path.join(this.baseDir, config.name);
40
+ await fs.mkdir(userDataDir, { recursive: true });
41
+ const profile = {
42
+ ...config,
43
+ userDataDir,
44
+ preferences: {
45
+ // Default preferences
46
+ "download.default_directory": path.join(userDataDir, "Downloads"),
47
+ "plugins.always_open_pdf_externally": true,
48
+ credentials_enable_service: false,
49
+ "profile.password_manager_enabled": false,
50
+ "safebrowsing.enabled": true,
51
+ ...config.preferences,
52
+ },
53
+ };
54
+ this.profiles.set(config.name, profile);
55
+ await this.writePreferences(profile);
56
+ return profile;
57
+ }
58
+ /**
59
+ * Get profile by name
60
+ */
61
+ getProfile(name) {
62
+ return this.profiles.get(name);
63
+ }
64
+ /**
65
+ * Launch browser with specified profile
66
+ */
67
+ async launchProfile(profileName) {
68
+ const profile = this.profiles.get(profileName);
69
+ if (!profile) {
70
+ throw new Error(`Profile ${profileName} not found`);
71
+ }
72
+ // Check if session already exists
73
+ const existing = this.activeSessions.get(profileName);
74
+ if (existing) {
75
+ existing.lastActivity = Date.now();
76
+ return existing;
77
+ }
78
+ // Evict oldest session if at capacity
79
+ if (this.activeSessions.size >= this.MAX_SESSIONS) {
80
+ await this.evictOldestSession();
81
+ }
82
+ // Launch browser
83
+ const context = await playwright.chromium.launchPersistentContext(profile.userDataDir, {
84
+ headless: profile.headless ?? false,
85
+ viewport: profile.viewport ?? { width: 1280, height: 720 },
86
+ args: [
87
+ "--disable-web-security",
88
+ "--disable-features=IsolateOrigins,site-per-process",
89
+ "--disable-site-isolation-trials",
90
+ "--disable-blink-features=AutomationControlled",
91
+ "--no-first-run",
92
+ "--no-default-browser-check",
93
+ "--disable-dev-shm-usage",
94
+ "--disable-accelerated-2d-canvas",
95
+ "--disable-gpu",
96
+ ],
97
+ proxy: profile.proxy
98
+ ? {
99
+ server: `${profile.proxy.host}:${profile.proxy.port}`,
100
+ username: profile.proxy.username,
101
+ password: profile.proxy.password,
102
+ }
103
+ : undefined,
104
+ ...profile.preferences,
105
+ });
106
+ const session = {
107
+ profile,
108
+ context,
109
+ browser: context.pages()[0]?.context().browser(),
110
+ startTime: Date.now(),
111
+ lastActivity: Date.now(),
112
+ };
113
+ this.activeSessions.set(profileName, session);
114
+ return session;
115
+ }
116
+ /**
117
+ * Close a profile session
118
+ */
119
+ async closeProfile(profileName) {
120
+ const session = this.activeSessions.get(profileName);
121
+ if (session) {
122
+ await session.context.close();
123
+ this.activeSessions.delete(profileName);
124
+ }
125
+ }
126
+ /**
127
+ * Get active session
128
+ */
129
+ getSession(profileName) {
130
+ return this.activeSessions.get(profileName);
131
+ }
132
+ /**
133
+ * Get all active sessions
134
+ */
135
+ getActiveSessions() {
136
+ return Array.from(this.activeSessions.values());
137
+ }
138
+ /**
139
+ * Update session activity timestamp
140
+ */
141
+ touchSession(profileName) {
142
+ const session = this.activeSessions.get(profileName);
143
+ if (session) {
144
+ session.lastActivity = Date.now();
145
+ }
146
+ }
147
+ /**
148
+ * Get statistics
149
+ */
150
+ getStats() {
151
+ const sessions = Array.from(this.activeSessions.values());
152
+ const oldest = sessions.length > 0 ? Math.min(...sessions.map((s) => s.startTime)) : null;
153
+ return {
154
+ totalProfiles: this.profiles.size,
155
+ activeSessions: sessions.length,
156
+ oldestSession: oldest,
157
+ };
158
+ }
159
+ /**
160
+ * Cleanup idle sessions
161
+ */
162
+ async cleanupIdleSessions() {
163
+ const now = Date.now();
164
+ let removed = 0;
165
+ for (const [name, session] of this.activeSessions.entries()) {
166
+ if (now - session.lastActivity > this.IDLE_TIMEOUT_MS) {
167
+ await this.closeProfile(name);
168
+ removed++;
169
+ }
170
+ }
171
+ return removed;
172
+ }
173
+ /**
174
+ * Destroy manager and close all sessions
175
+ */
176
+ async destroy() {
177
+ if (this.cleanupInterval) {
178
+ clearInterval(this.cleanupInterval);
179
+ this.cleanupInterval = null;
180
+ }
181
+ const closePromises = Array.from(this.activeSessions.keys()).map((name) => this.closeProfile(name).catch(console.error));
182
+ await Promise.all(closePromises);
183
+ this.profiles.clear();
184
+ }
185
+ async writePreferences(profile) {
186
+ if (!profile.userDataDir)
187
+ return;
188
+ const preferencesPath = path.join(profile.userDataDir, "Default", "Preferences");
189
+ await fs.mkdir(path.dirname(preferencesPath), { recursive: true });
190
+ const preferences = {
191
+ profile: {
192
+ default_content_setting_values: {
193
+ notifications: 2, // Block notifications
194
+ geolocation: 2, // Block geolocation
195
+ },
196
+ },
197
+ ...profile.preferences,
198
+ };
199
+ await fs.writeFile(preferencesPath, JSON.stringify(preferences, null, 2));
200
+ }
201
+ async evictOldestSession() {
202
+ let oldestName = null;
203
+ let oldestTime = Infinity;
204
+ for (const [name, session] of this.activeSessions.entries()) {
205
+ if (session.startTime < oldestTime) {
206
+ oldestTime = session.startTime;
207
+ oldestName = name;
208
+ }
209
+ }
210
+ if (oldestName) {
211
+ await this.closeProfile(oldestName);
212
+ }
213
+ }
214
+ startCleanupInterval() {
215
+ const cleanupIntervalMs = Math.max(30000, this.IDLE_TIMEOUT_MS / 10);
216
+ this.cleanupInterval = setInterval(() => {
217
+ this.cleanupIdleSessions().catch(console.error);
218
+ }, cleanupIntervalMs);
219
+ this.cleanupInterval.unref();
220
+ }
221
+ }
222
+ /**
223
+ * Navigation Guards for safe browsing
224
+ */
225
+ export class GuardedNavigator {
226
+ blockedUrls;
227
+ allowedDomains;
228
+ maxRedirects;
229
+ timeout;
230
+ constructor(options) {
231
+ this.blockedUrls = options?.blockedUrls ?? [
232
+ /file:\/\/\//,
233
+ /chrome:\/\//,
234
+ /chrome-extension:\/\//,
235
+ /about:/,
236
+ ];
237
+ this.allowedDomains = new Set(options?.allowedDomains ?? []);
238
+ this.maxRedirects = options?.maxRedirects ?? 5;
239
+ this.timeout = options?.timeout ?? 30000;
240
+ }
241
+ /**
242
+ * Navigate with security guards
243
+ */
244
+ async navigate(context, url) {
245
+ try {
246
+ // Validate URL
247
+ const parsed = new URL(url);
248
+ if (!this.isAllowedDomain(parsed.hostname)) {
249
+ return {
250
+ success: false,
251
+ finalUrl: url,
252
+ status: 0,
253
+ redirects: 0,
254
+ error: `Domain ${parsed.hostname} not in allowlist`,
255
+ };
256
+ }
257
+ if (this.isBlockedUrl(url)) {
258
+ return {
259
+ success: false,
260
+ finalUrl: url,
261
+ status: 0,
262
+ redirects: 0,
263
+ error: `URL matches blocked pattern`,
264
+ };
265
+ }
266
+ // Navigate with timeout
267
+ const page = await context.newPage();
268
+ let redirects = 0;
269
+ let currentUrl = url;
270
+ const response = await page.goto(url, {
271
+ waitUntil: "networkidle",
272
+ timeout: this.timeout,
273
+ });
274
+ // Track redirects
275
+ while (currentUrl !== url && redirects < this.maxRedirects) {
276
+ redirects++;
277
+ currentUrl = page.url();
278
+ await page.waitForTimeout(100);
279
+ }
280
+ if (redirects >= this.maxRedirects) {
281
+ await page.close();
282
+ return {
283
+ success: false,
284
+ finalUrl: page.url(),
285
+ status: 0,
286
+ redirects,
287
+ error: `Too many redirects (${redirects})`,
288
+ };
289
+ }
290
+ const status = response?.status() ?? 0;
291
+ const finalUrl = page.url();
292
+ // Don't close page - let caller manage it
293
+ return {
294
+ success: status >= 200 && status < 400,
295
+ finalUrl,
296
+ status,
297
+ redirects,
298
+ };
299
+ }
300
+ catch (error) {
301
+ return {
302
+ success: false,
303
+ finalUrl: url,
304
+ status: 0,
305
+ redirects: 0,
306
+ error: error.message,
307
+ };
308
+ }
309
+ }
310
+ isAllowedDomain(hostname) {
311
+ if (this.allowedDomains.size === 0) {
312
+ return true; // No restrictions
313
+ }
314
+ return this.allowedDomains.has(hostname);
315
+ }
316
+ isBlockedUrl(url) {
317
+ return this.blockedUrls.some((re) => re.test(url));
318
+ }
319
+ }
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Proxy bypass for CDP (Chrome DevTools Protocol) localhost connections.
3
+ *
4
+ * When HTTP_PROXY / HTTPS_PROXY / ALL_PROXY environment variables are set,
5
+ * CDP connections to localhost/127.0.0.1 can be incorrectly routed through
6
+ * the proxy, causing browser control to fail.
7
+ */
8
+ import http from "node:http";
9
+ import https from "node:https";
10
+ import { isLoopbackHost } from "../gateway/net.js";
11
+ import { hasProxyEnvConfigured } from "../infra/net/proxy-env.js";
12
+ /** HTTP agent that never uses a proxy — for localhost CDP connections. */
13
+ const directHttpAgent = new http.Agent();
14
+ const directHttpsAgent = new https.Agent();
15
+ /**
16
+ * Returns a plain (non-proxy) agent for WebSocket or HTTP connections
17
+ * when the target is a loopback address. Returns `undefined` otherwise
18
+ * so callers fall through to their default behaviour.
19
+ */
20
+ export function getDirectAgentForCdp(url) {
21
+ try {
22
+ const parsed = new URL(url);
23
+ if (isLoopbackHost(parsed.hostname)) {
24
+ return parsed.protocol === "https:" || parsed.protocol === "wss:"
25
+ ? directHttpsAgent
26
+ : directHttpAgent;
27
+ }
28
+ }
29
+ catch {
30
+ // not a valid URL — let caller handle it
31
+ }
32
+ return undefined;
33
+ }
34
+ /**
35
+ * Returns `true` when any proxy-related env var is set that could
36
+ * interfere with loopback connections.
37
+ */
38
+ export function hasProxyEnv() {
39
+ return hasProxyEnvConfigured();
40
+ }
41
+ const LOOPBACK_ENTRIES = "localhost,127.0.0.1,[::1]";
42
+ function noProxyAlreadyCoversLocalhost() {
43
+ const current = process.env.NO_PROXY || process.env.no_proxy || "";
44
+ return (current.includes("localhost") && current.includes("127.0.0.1") && current.includes("[::1]"));
45
+ }
46
+ export async function withNoProxyForLocalhost(fn) {
47
+ return await withNoProxyForCdpUrl("http://127.0.0.1", fn);
48
+ }
49
+ function isLoopbackCdpUrl(url) {
50
+ try {
51
+ return isLoopbackHost(new URL(url).hostname);
52
+ }
53
+ catch {
54
+ return false;
55
+ }
56
+ }
57
+ class NoProxyLeaseManager {
58
+ leaseCount = 0;
59
+ snapshot = null;
60
+ acquire(url) {
61
+ if (!isLoopbackCdpUrl(url) || !hasProxyEnv()) {
62
+ return null;
63
+ }
64
+ if (this.leaseCount === 0 && !noProxyAlreadyCoversLocalhost()) {
65
+ const noProxy = process.env.NO_PROXY;
66
+ const noProxyLower = process.env.no_proxy;
67
+ const current = noProxy || noProxyLower || "";
68
+ const applied = current ? `${current},${LOOPBACK_ENTRIES}` : LOOPBACK_ENTRIES;
69
+ process.env.NO_PROXY = applied;
70
+ process.env.no_proxy = applied;
71
+ this.snapshot = { noProxy, noProxyLower, applied };
72
+ }
73
+ this.leaseCount += 1;
74
+ let released = false;
75
+ return () => {
76
+ if (released) {
77
+ return;
78
+ }
79
+ released = true;
80
+ this.release();
81
+ };
82
+ }
83
+ release() {
84
+ if (this.leaseCount <= 0) {
85
+ return;
86
+ }
87
+ this.leaseCount -= 1;
88
+ if (this.leaseCount > 0 || !this.snapshot) {
89
+ return;
90
+ }
91
+ const { noProxy, noProxyLower, applied } = this.snapshot;
92
+ const currentNoProxy = process.env.NO_PROXY;
93
+ const currentNoProxyLower = process.env.no_proxy;
94
+ const untouched = currentNoProxy === applied &&
95
+ (currentNoProxyLower === applied || currentNoProxyLower === undefined);
96
+ if (untouched) {
97
+ if (noProxy !== undefined) {
98
+ process.env.NO_PROXY = noProxy;
99
+ }
100
+ else {
101
+ delete process.env.NO_PROXY;
102
+ }
103
+ if (noProxyLower !== undefined) {
104
+ process.env.no_proxy = noProxyLower;
105
+ }
106
+ else {
107
+ delete process.env.no_proxy;
108
+ }
109
+ }
110
+ this.snapshot = null;
111
+ }
112
+ }
113
+ const noProxyLeaseManager = new NoProxyLeaseManager();
114
+ /**
115
+ * Scoped NO_PROXY bypass for loopback CDP URLs.
116
+ *
117
+ * This wrapper only mutates env vars for loopback destinations. On restore,
118
+ * it avoids clobbering external NO_PROXY changes that happened while calls
119
+ * were in-flight.
120
+ */
121
+ export async function withNoProxyForCdpUrl(url, fn) {
122
+ const release = noProxyLeaseManager.acquire(url);
123
+ try {
124
+ return await fn();
125
+ }
126
+ finally {
127
+ release?.();
128
+ }
129
+ }
@@ -0,0 +1,41 @@
1
+ export const CDP_HTTP_REQUEST_TIMEOUT_MS = 1500;
2
+ export const CDP_WS_HANDSHAKE_TIMEOUT_MS = 5000;
3
+ export const CDP_JSON_NEW_TIMEOUT_MS = 1500;
4
+ export const CHROME_REACHABILITY_TIMEOUT_MS = 500;
5
+ export const CHROME_WS_READY_TIMEOUT_MS = 800;
6
+ export const CHROME_BOOTSTRAP_PREFS_TIMEOUT_MS = 10_000;
7
+ export const CHROME_BOOTSTRAP_EXIT_TIMEOUT_MS = 5000;
8
+ export const CHROME_LAUNCH_READY_WINDOW_MS = 15_000;
9
+ export const CHROME_LAUNCH_READY_POLL_MS = 200;
10
+ export const CHROME_STOP_TIMEOUT_MS = 2500;
11
+ export const CHROME_STOP_PROBE_TIMEOUT_MS = 200;
12
+ export const CHROME_STDERR_HINT_MAX_CHARS = 2000;
13
+ export const PROFILE_HTTP_REACHABILITY_TIMEOUT_MS = 300;
14
+ export const PROFILE_WS_REACHABILITY_MIN_TIMEOUT_MS = 200;
15
+ export const PROFILE_WS_REACHABILITY_MAX_TIMEOUT_MS = 2000;
16
+ export const PROFILE_ATTACH_RETRY_TIMEOUT_MS = 1200;
17
+ export const PROFILE_POST_RESTART_WS_TIMEOUT_MS = 600;
18
+ function normalizeTimeoutMs(value) {
19
+ if (typeof value !== "number" || !Number.isFinite(value)) {
20
+ return undefined;
21
+ }
22
+ return Math.max(1, Math.floor(value));
23
+ }
24
+ export function resolveCdpReachabilityTimeouts(params) {
25
+ const normalized = normalizeTimeoutMs(params.timeoutMs);
26
+ if (params.profileIsLoopback) {
27
+ const httpTimeoutMs = normalized ?? PROFILE_HTTP_REACHABILITY_TIMEOUT_MS;
28
+ const wsTimeoutMs = Math.max(PROFILE_WS_REACHABILITY_MIN_TIMEOUT_MS, Math.min(PROFILE_WS_REACHABILITY_MAX_TIMEOUT_MS, httpTimeoutMs * 2));
29
+ return { httpTimeoutMs, wsTimeoutMs };
30
+ }
31
+ if (normalized !== undefined) {
32
+ return {
33
+ httpTimeoutMs: Math.max(normalized, params.remoteHttpTimeoutMs),
34
+ wsTimeoutMs: Math.max(normalized * 2, params.remoteHandshakeTimeoutMs),
35
+ };
36
+ }
37
+ return {
38
+ httpTimeoutMs: params.remoteHttpTimeoutMs,
39
+ wsTimeoutMs: params.remoteHandshakeTimeoutMs,
40
+ };
41
+ }