@xbrowser/cli 1.9.7 → 1.9.9
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/{anti-bot-MCVM6IFB.js → anti-bot-DR56Y63V.js} +1 -1
- package/dist/{browser-JXIJF4VL.js → browser-FFYPFTBV.js} +1 -1
- package/dist/{browser-ALPLJNH2.js → browser-RHWUAMTB.js} +2 -2
- package/dist/{browser-ZPQP6VW7.js → browser-YCO4GJMZ.js} +2 -2
- package/dist/{cdp-driver-MI7VCE44.js → cdp-driver-2T4P4ZE2.js} +103 -2
- package/dist/{cdp-driver-6EVYLHL2.js → cdp-driver-J3YQ4LJV.js} +1 -1
- package/dist/chunk-3OD76SUE.js +20 -0
- package/dist/chunk-43TEMG4U.js +9 -0
- package/dist/{chunk-CH5ZQV64.js → chunk-4452SPFI.js} +928 -18
- package/dist/{chunk-C4O73DQL.js → chunk-5UGR6MUK.js} +928 -18
- package/dist/{chunk-37SHZSUR.js → chunk-6OZWKXSW.js} +15 -16
- package/dist/{chunk-7ZDWM6T2.js → chunk-BNO7OKO4.js} +5 -6
- package/dist/{chunk-24SY6PE5.js → chunk-CG3D3CHY.js} +1 -8
- package/dist/{chunk-ZJHQPMCY.js → chunk-DWDXEGVK.js} +5 -6
- package/dist/{chunk-74S7UUI2.js → chunk-GUTBIYFW.js} +117 -17
- package/dist/{chunk-DPJNCMLC.js → chunk-INTQPBYF.js} +7 -0
- package/dist/{human-interaction-OU7LZ6OZ.js → chunk-MWFHZUIY.js} +6 -7
- package/dist/chunk-Q2DFJQGS.js +128 -0
- package/dist/{chunk-VJNMAWPZ.js → chunk-RRBXV7KE.js} +103 -2
- package/dist/{chunk-5JSOGINQ.js → chunk-RZM5CNIY.js} +15 -16
- package/dist/{human-interaction-HJTXFVQS.js → chunk-SQHSZENE.js} +2 -2
- package/dist/chunk-TZPKFUBT.js +20 -0
- package/dist/{chunk-YAUG3NSI.js → chunk-YMUSHPU4.js} +16 -128
- package/dist/cli.js +41 -28
- package/dist/{daemon-client-4HJENICO.js → daemon-client-GKEPT4NY.js} +15 -3
- package/dist/{daemon-client-CMRAWXTN.js → daemon-client-O6BYVRXV.js} +4 -1
- package/dist/daemon-main.js +661 -70
- package/dist/human-interaction-5AO42MBA.js +8 -0
- package/dist/{human-interaction-2DZK4MW7.js → human-interaction-C5OEGXGO.js} +1 -1
- package/dist/human-interaction-ISXZTAYY.js +8 -0
- package/dist/index.d.ts +172 -2
- package/dist/index.js +173 -63
- package/dist/recovery-EF33LKRJ.js +77 -0
- package/dist/recovery-J2ISVGUL.js +77 -0
- package/dist/recovery-ZJVVHP7N.js +76 -0
- package/dist/{session-recorder-7GXI42FO.js → session-recorder-H3KEYU26.js} +1 -1
- package/dist/{session-recorder-RFOPX4SF.js → session-recorder-QRZMKFVL.js} +1 -1
- package/dist/{session-replayer-3HEIJBJ2.js → session-replayer-LJUC4TI7.js} +25 -8
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
launch
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-RRBXV7KE.js";
|
|
4
4
|
import {
|
|
5
5
|
errMsg
|
|
6
6
|
} from "./chunk-GDKLH7ZY.js";
|
|
@@ -1499,7 +1499,7 @@ process.on("exit", () => {
|
|
|
1499
1499
|
if (session.isCDP) {
|
|
1500
1500
|
logSessionEvent("process_exit", `Session "${session.name}": CDP connection (not closing external browser).`);
|
|
1501
1501
|
} else {
|
|
1502
|
-
logSessionEvent("process_exit", `Session "${session.name}": Closing browser.`);
|
|
1502
|
+
logSessionEvent("process_exit", `Session "${session.name}": Closing self-launched browser.`);
|
|
1503
1503
|
try {
|
|
1504
1504
|
session.browser?.close();
|
|
1505
1505
|
} catch {
|
|
@@ -1507,7 +1507,7 @@ process.on("exit", () => {
|
|
|
1507
1507
|
}
|
|
1508
1508
|
}
|
|
1509
1509
|
if (_sharedBrowser) {
|
|
1510
|
-
logSessionEvent("process_exit", "Closing shared browser.");
|
|
1510
|
+
logSessionEvent("process_exit", "Closing shared browser (self-launched only, external CDP is safe).");
|
|
1511
1511
|
try {
|
|
1512
1512
|
_sharedBrowser.close();
|
|
1513
1513
|
} catch {
|
|
@@ -1993,10 +1993,6 @@ async function closeSessionByName(name) {
|
|
|
1993
1993
|
if (session.name === name || session.id === name) {
|
|
1994
1994
|
logSessionEvent("close_session", `name="${session.name}" id="${session.id}" url="${session.page.url()}"`);
|
|
1995
1995
|
if (session.isCDP) {
|
|
1996
|
-
try {
|
|
1997
|
-
await session.page.close();
|
|
1998
|
-
} catch {
|
|
1999
|
-
}
|
|
2000
1996
|
if (session.browser) {
|
|
2001
1997
|
await session.browser.close().catch(() => {
|
|
2002
1998
|
});
|
|
@@ -2021,7 +2017,7 @@ async function closeSessionByName(name) {
|
|
|
2021
2017
|
} catch {
|
|
2022
2018
|
}
|
|
2023
2019
|
try {
|
|
2024
|
-
const { SessionRecorder } = await import("./session-recorder-
|
|
2020
|
+
const { SessionRecorder } = await import("./session-recorder-H3KEYU26.js");
|
|
2025
2021
|
SessionRecorder.cleanup(session.name);
|
|
2026
2022
|
} catch {
|
|
2027
2023
|
}
|
|
@@ -2040,12 +2036,17 @@ async function closeAllSessions() {
|
|
|
2040
2036
|
if (names) logSessionEvent("close_all_sessions", `Closing ${sessions.size} sessions: ${names}`);
|
|
2041
2037
|
for (const session of sessions.list()) {
|
|
2042
2038
|
try {
|
|
2043
|
-
if (
|
|
2039
|
+
if (session.isCDP) {
|
|
2040
|
+
if (session.browser) {
|
|
2041
|
+
await session.browser.close().catch(() => {
|
|
2042
|
+
});
|
|
2043
|
+
}
|
|
2044
|
+
} else {
|
|
2044
2045
|
await session.context.close();
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
}
|
|
2046
|
+
if (session.browser) {
|
|
2047
|
+
await session.browser.close().catch(() => {
|
|
2048
|
+
});
|
|
2049
|
+
}
|
|
2049
2050
|
}
|
|
2050
2051
|
sessions.removeById(session.id);
|
|
2051
2052
|
} catch {
|
|
@@ -2054,7 +2055,7 @@ async function closeAllSessions() {
|
|
|
2054
2055
|
}
|
|
2055
2056
|
}
|
|
2056
2057
|
async function destroyBrowser() {
|
|
2057
|
-
logSessionEvent("destroy_browser", `Sessions count: ${sessions.size}. Clearing idle timer and closing all
|
|
2058
|
+
logSessionEvent("destroy_browser", `Sessions count: ${sessions.size}. Clearing idle timer and closing all sessions.`);
|
|
2058
2059
|
if (idleTimer) {
|
|
2059
2060
|
clearTimeout(idleTimer);
|
|
2060
2061
|
idleTimer = null;
|
|
@@ -2090,8 +2091,6 @@ async function ensureProcessCanExit() {
|
|
|
2090
2091
|
for (const session of sessions.list()) {
|
|
2091
2092
|
if (session.browser) {
|
|
2092
2093
|
if (session.isCDP) {
|
|
2093
|
-
await session.browser.close().catch(() => {
|
|
2094
|
-
});
|
|
2095
2094
|
} else {
|
|
2096
2095
|
await session.browser.close().catch(() => {
|
|
2097
2096
|
});
|
|
@@ -36,16 +36,15 @@ var ScreencastCapturer = class {
|
|
|
36
36
|
const cdp = await page.context().newCDPSession(page);
|
|
37
37
|
this.cdpSession = cdp;
|
|
38
38
|
cdp.on("Page.screencastFrame", async (params) => {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
43
|
-
}
|
|
39
|
+
const cb = this.frameCallback;
|
|
40
|
+
if (!cb) return;
|
|
41
|
+
cdp.send("Page.screencastFrameAck", { sessionId: params.sessionId }).catch(() => {
|
|
42
|
+
});
|
|
44
43
|
const viewport = {
|
|
45
44
|
width: params.metadata?.deviceWidth || this.maxWidth,
|
|
46
45
|
height: params.metadata?.deviceHeight || this.maxHeight
|
|
47
46
|
};
|
|
48
|
-
|
|
47
|
+
cb({
|
|
49
48
|
id: crypto.randomUUID(),
|
|
50
49
|
sessionId: this.sessionId,
|
|
51
50
|
timestamp: Date.now(),
|
|
@@ -116,16 +116,9 @@ function getDaemonProcessStatus() {
|
|
|
116
116
|
};
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
-
// src/version.ts
|
|
120
|
-
import { createRequire } from "module";
|
|
121
|
-
var require2 = createRequire(import.meta.url);
|
|
122
|
-
var pkg = require2("../package.json");
|
|
123
|
-
var version = pkg.version;
|
|
124
|
-
|
|
125
119
|
export {
|
|
126
120
|
getDaemonConfig,
|
|
127
121
|
startDaemonProcess,
|
|
128
122
|
stopDaemonProcess,
|
|
129
|
-
getDaemonProcessStatus
|
|
130
|
-
version
|
|
123
|
+
getDaemonProcessStatus
|
|
131
124
|
};
|
|
@@ -39,16 +39,15 @@ var ScreencastCapturer = class {
|
|
|
39
39
|
const cdp = await page.context().newCDPSession(page);
|
|
40
40
|
this.cdpSession = cdp;
|
|
41
41
|
cdp.on("Page.screencastFrame", async (params) => {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
}
|
|
42
|
+
const cb = this.frameCallback;
|
|
43
|
+
if (!cb) return;
|
|
44
|
+
cdp.send("Page.screencastFrameAck", { sessionId: params.sessionId }).catch(() => {
|
|
45
|
+
});
|
|
47
46
|
const viewport = {
|
|
48
47
|
width: params.metadata?.deviceWidth || this.maxWidth,
|
|
49
48
|
height: params.metadata?.deviceHeight || this.maxHeight
|
|
50
49
|
};
|
|
51
|
-
|
|
50
|
+
cb({
|
|
52
51
|
id: crypto.randomUUID(),
|
|
53
52
|
sessionId: this.sessionId,
|
|
54
53
|
timestamp: Date.now(),
|
|
@@ -294,6 +294,36 @@ function queryJS(selector) {
|
|
|
294
294
|
const xpath = JSON.stringify(selector.slice(6));
|
|
295
295
|
return `document.evaluate(${xpath}, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue`;
|
|
296
296
|
}
|
|
297
|
+
if (selector.startsWith("text=")) {
|
|
298
|
+
const raw = selector.slice(5);
|
|
299
|
+
const exact = raw.startsWith('"') && raw.endsWith('"');
|
|
300
|
+
const text = exact ? raw.slice(1, -1) : raw;
|
|
301
|
+
return `(() => {
|
|
302
|
+
const target = ${JSON.stringify(text)};
|
|
303
|
+
const exact = ${exact};
|
|
304
|
+
const els = [...document.querySelectorAll('*')].filter(e => {
|
|
305
|
+
if (e.children.length > 0) return false;
|
|
306
|
+
if (e.offsetParent === null) return false;
|
|
307
|
+
const t = (e.textContent || '').trim();
|
|
308
|
+
if (!t) return false;
|
|
309
|
+
return exact ? t === target : t.toLowerCase().includes(target.toLowerCase());
|
|
310
|
+
});
|
|
311
|
+
return els[0] || null;
|
|
312
|
+
})()`;
|
|
313
|
+
}
|
|
314
|
+
if (selector.startsWith("popup-text=")) {
|
|
315
|
+
const text = selector.slice("popup-text=".length);
|
|
316
|
+
return `(() => {
|
|
317
|
+
const target = ${JSON.stringify(text)};
|
|
318
|
+
const els = [...document.querySelectorAll('*')].filter(e => {
|
|
319
|
+
if (e.children.length > 0) return false;
|
|
320
|
+
if (e.offsetParent === null) return false;
|
|
321
|
+
if ((e.textContent || '').trim() !== target) return false;
|
|
322
|
+
return true;
|
|
323
|
+
});
|
|
324
|
+
return els[0] || null;
|
|
325
|
+
})()`;
|
|
326
|
+
}
|
|
297
327
|
return `document.querySelector(${JSON.stringify(selector)})`;
|
|
298
328
|
}
|
|
299
329
|
function queryAllJS(selector) {
|
|
@@ -301,6 +331,9 @@ function queryAllJS(selector) {
|
|
|
301
331
|
const xpath = JSON.stringify(selector.slice(6));
|
|
302
332
|
return `(() => { const it = document.evaluate(${xpath}, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); const r=[]; for(let i=0;i<it.snapshotLength;i++) r.push(it.snapshotItem(i)); return r; })()`;
|
|
303
333
|
}
|
|
334
|
+
if (selector.startsWith("text=") || selector.startsWith("popup-text=")) {
|
|
335
|
+
return `(() => { const el = ${queryJS(selector)}; return el ? [el] : []; })()`;
|
|
336
|
+
}
|
|
304
337
|
return `document.querySelectorAll(${JSON.stringify(selector)})`;
|
|
305
338
|
}
|
|
306
339
|
|
|
@@ -1338,7 +1371,22 @@ Last error: ${lastError.message}` : "";
|
|
|
1338
1371
|
return this._url;
|
|
1339
1372
|
}
|
|
1340
1373
|
async title() {
|
|
1341
|
-
|
|
1374
|
+
for (let i = 0; i < 20; i++) {
|
|
1375
|
+
try {
|
|
1376
|
+
this._title = await this.evaluate("document.title");
|
|
1377
|
+
if (this._title) return this._title;
|
|
1378
|
+
} catch {
|
|
1379
|
+
}
|
|
1380
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
1381
|
+
}
|
|
1382
|
+
try {
|
|
1383
|
+
const info = await this._cdpSend("Target.getTargetInfo", { targetId: this._targetId });
|
|
1384
|
+
if (info?.title) {
|
|
1385
|
+
this._title = info.title;
|
|
1386
|
+
return this._title;
|
|
1387
|
+
}
|
|
1388
|
+
} catch {
|
|
1389
|
+
}
|
|
1342
1390
|
return this._title;
|
|
1343
1391
|
}
|
|
1344
1392
|
async content() {
|
|
@@ -2748,11 +2796,52 @@ var CDPConnection = class extends EventEmitter4 {
|
|
|
2748
2796
|
this.setMaxListeners(0);
|
|
2749
2797
|
this.defaultSessionId = sessionId;
|
|
2750
2798
|
if (typeof wsOrUrl === "string") {
|
|
2751
|
-
|
|
2799
|
+
const wsOptions = /^wss:\/\/\d+\.\d+\.\d+\.\d+/.test(wsOrUrl) ? { rejectUnauthorized: false } : void 0;
|
|
2800
|
+
this.ws = new WebSocket(wsOrUrl, wsOptions);
|
|
2752
2801
|
} else {
|
|
2753
2802
|
this.ws = wsOrUrl;
|
|
2754
2803
|
}
|
|
2755
2804
|
this.bindWebSocket();
|
|
2805
|
+
this.startKeepalive();
|
|
2806
|
+
}
|
|
2807
|
+
/** Send periodic WS pings to prevent idle-timeout disconnects (e.g. CF's 100s).
|
|
2808
|
+
* Also detects dead connections: if a pong isn't received within 10s of a
|
|
2809
|
+
* ping, the connection is considered dead and forcibly closed. */
|
|
2810
|
+
keepaliveTimer = null;
|
|
2811
|
+
pongTimer = null;
|
|
2812
|
+
startKeepalive() {
|
|
2813
|
+
this.ws.on("pong", () => {
|
|
2814
|
+
if (this.pongTimer) {
|
|
2815
|
+
clearTimeout(this.pongTimer);
|
|
2816
|
+
this.pongTimer = null;
|
|
2817
|
+
}
|
|
2818
|
+
});
|
|
2819
|
+
this.keepaliveTimer = setInterval(() => {
|
|
2820
|
+
if (this.ws.readyState === WebSocket.OPEN) {
|
|
2821
|
+
if (!this.pongTimer) {
|
|
2822
|
+
this.pongTimer = setTimeout(() => {
|
|
2823
|
+
if (!this.closed) {
|
|
2824
|
+
this.closed = true;
|
|
2825
|
+
this.closeReason = "keepalive timeout (no pong in 10s)";
|
|
2826
|
+
try {
|
|
2827
|
+
this.ws.terminate();
|
|
2828
|
+
} catch {
|
|
2829
|
+
}
|
|
2830
|
+
for (const [, pending] of this.pending) {
|
|
2831
|
+
clearTimeout(pending.timeout);
|
|
2832
|
+
pending.reject(new Error("Connection dead: keepalive timeout"));
|
|
2833
|
+
}
|
|
2834
|
+
this.pending.clear();
|
|
2835
|
+
this.emit("disconnect");
|
|
2836
|
+
}
|
|
2837
|
+
}, 1e4);
|
|
2838
|
+
}
|
|
2839
|
+
this.ws.ping?.();
|
|
2840
|
+
} else if (this.closed) {
|
|
2841
|
+
if (this.keepaliveTimer) clearInterval(this.keepaliveTimer);
|
|
2842
|
+
this.keepaliveTimer = null;
|
|
2843
|
+
}
|
|
2844
|
+
}, 3e4);
|
|
2756
2845
|
}
|
|
2757
2846
|
/** Wait for the connection to be fully open */
|
|
2758
2847
|
async ready() {
|
|
@@ -2861,6 +2950,14 @@ var CDPConnection = class extends EventEmitter4 {
|
|
|
2861
2950
|
if (this.closed) return;
|
|
2862
2951
|
this.closed = true;
|
|
2863
2952
|
this.closeReason = "closed by caller";
|
|
2953
|
+
if (this.keepaliveTimer) {
|
|
2954
|
+
clearInterval(this.keepaliveTimer);
|
|
2955
|
+
this.keepaliveTimer = null;
|
|
2956
|
+
}
|
|
2957
|
+
if (this.pongTimer) {
|
|
2958
|
+
clearTimeout(this.pongTimer);
|
|
2959
|
+
this.pongTimer = null;
|
|
2960
|
+
}
|
|
2864
2961
|
for (const [id, pending] of this.pending) {
|
|
2865
2962
|
clearTimeout(pending.timeout);
|
|
2866
2963
|
pending.reject(new Error(`Connection closed: ${pending.method}`));
|
|
@@ -2902,6 +2999,10 @@ var CDPConnection = class extends EventEmitter4 {
|
|
|
2902
2999
|
if (this.closed) return;
|
|
2903
3000
|
this.closed = true;
|
|
2904
3001
|
this.closeReason = `WebSocket closed: ${code} ${reason?.toString() ?? ""}`.trim();
|
|
3002
|
+
if (this.keepaliveTimer) {
|
|
3003
|
+
clearInterval(this.keepaliveTimer);
|
|
3004
|
+
this.keepaliveTimer = null;
|
|
3005
|
+
}
|
|
2905
3006
|
for (const [id, pending] of this.pending) {
|
|
2906
3007
|
clearTimeout(pending.timeout);
|
|
2907
3008
|
pending.reject(new Error(`Connection closed: ${pending.method}`));
|
|
@@ -4443,7 +4544,7 @@ process.on("exit", () => {
|
|
|
4443
4544
|
if (session.isCDP) {
|
|
4444
4545
|
logSessionEvent("process_exit", `Session "${session.name}": CDP connection (not closing external browser).`);
|
|
4445
4546
|
} else {
|
|
4446
|
-
logSessionEvent("process_exit", `Session "${session.name}": Closing browser.`);
|
|
4547
|
+
logSessionEvent("process_exit", `Session "${session.name}": Closing self-launched browser.`);
|
|
4447
4548
|
try {
|
|
4448
4549
|
session.browser?.close();
|
|
4449
4550
|
} catch {
|
|
@@ -4451,7 +4552,7 @@ process.on("exit", () => {
|
|
|
4451
4552
|
}
|
|
4452
4553
|
}
|
|
4453
4554
|
if (_sharedBrowser) {
|
|
4454
|
-
logSessionEvent("process_exit", "Closing shared browser.");
|
|
4555
|
+
logSessionEvent("process_exit", "Closing shared browser (self-launched only, external CDP is safe).");
|
|
4455
4556
|
try {
|
|
4456
4557
|
_sharedBrowser.close();
|
|
4457
4558
|
} catch {
|
|
@@ -4937,10 +5038,6 @@ async function closeSessionByName(name) {
|
|
|
4937
5038
|
if (session.name === name || session.id === name) {
|
|
4938
5039
|
logSessionEvent("close_session", `name="${session.name}" id="${session.id}" url="${session.page.url()}"`);
|
|
4939
5040
|
if (session.isCDP) {
|
|
4940
|
-
try {
|
|
4941
|
-
await session.page.close();
|
|
4942
|
-
} catch {
|
|
4943
|
-
}
|
|
4944
5041
|
if (session.browser) {
|
|
4945
5042
|
await session.browser.close().catch(() => {
|
|
4946
5043
|
});
|
|
@@ -4965,7 +5062,7 @@ async function closeSessionByName(name) {
|
|
|
4965
5062
|
} catch {
|
|
4966
5063
|
}
|
|
4967
5064
|
try {
|
|
4968
|
-
const { SessionRecorder } = await import("./session-recorder-
|
|
5065
|
+
const { SessionRecorder } = await import("./session-recorder-QRZMKFVL.js");
|
|
4969
5066
|
SessionRecorder.cleanup(session.name);
|
|
4970
5067
|
} catch {
|
|
4971
5068
|
}
|
|
@@ -4984,12 +5081,17 @@ async function closeAllSessions() {
|
|
|
4984
5081
|
if (names) logSessionEvent("close_all_sessions", `Closing ${sessions.size} sessions: ${names}`);
|
|
4985
5082
|
for (const session of sessions.list()) {
|
|
4986
5083
|
try {
|
|
4987
|
-
if (
|
|
5084
|
+
if (session.isCDP) {
|
|
5085
|
+
if (session.browser) {
|
|
5086
|
+
await session.browser.close().catch(() => {
|
|
5087
|
+
});
|
|
5088
|
+
}
|
|
5089
|
+
} else {
|
|
4988
5090
|
await session.context.close();
|
|
4989
|
-
|
|
4990
|
-
|
|
4991
|
-
|
|
4992
|
-
}
|
|
5091
|
+
if (session.browser) {
|
|
5092
|
+
await session.browser.close().catch(() => {
|
|
5093
|
+
});
|
|
5094
|
+
}
|
|
4993
5095
|
}
|
|
4994
5096
|
sessions.removeById(session.id);
|
|
4995
5097
|
} catch {
|
|
@@ -4998,7 +5100,7 @@ async function closeAllSessions() {
|
|
|
4998
5100
|
}
|
|
4999
5101
|
}
|
|
5000
5102
|
async function destroyBrowser() {
|
|
5001
|
-
logSessionEvent("destroy_browser", `Sessions count: ${sessions.size}. Clearing idle timer and closing all
|
|
5103
|
+
logSessionEvent("destroy_browser", `Sessions count: ${sessions.size}. Clearing idle timer and closing all sessions.`);
|
|
5002
5104
|
if (idleTimer) {
|
|
5003
5105
|
clearTimeout(idleTimer);
|
|
5004
5106
|
idleTimer = null;
|
|
@@ -5034,8 +5136,6 @@ async function ensureProcessCanExit() {
|
|
|
5034
5136
|
for (const session of sessions.list()) {
|
|
5035
5137
|
if (session.browser) {
|
|
5036
5138
|
if (session.isCDP) {
|
|
5037
|
-
await session.browser.close().catch(() => {
|
|
5038
|
-
});
|
|
5039
5139
|
} else {
|
|
5040
5140
|
await session.browser.close().catch(() => {
|
|
5041
5141
|
});
|
|
@@ -98,6 +98,13 @@ async function detectCaptcha(page) {
|
|
|
98
98
|
for (const selector of captchaSelectors) {
|
|
99
99
|
const element = await page.$(selector).catch(() => null);
|
|
100
100
|
if (element) {
|
|
101
|
+
const visible = await element.isVisible().catch(() => false);
|
|
102
|
+
if (!visible) continue;
|
|
103
|
+
try {
|
|
104
|
+
const box = await element.boundingBox();
|
|
105
|
+
if (box && (box.y < -1e3 || box.x < -1e3)) continue;
|
|
106
|
+
} catch {
|
|
107
|
+
}
|
|
101
108
|
return {
|
|
102
109
|
detected: true,
|
|
103
110
|
type: "captcha",
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getCaptchaConfig
|
|
3
3
|
} from "./chunk-IIM5GOD7.js";
|
|
4
|
-
import "./chunk-KFQGP6VL.js";
|
|
5
4
|
|
|
6
5
|
// src/human-interaction.ts
|
|
7
6
|
import { execSync } from "child_process";
|
|
@@ -44,16 +43,15 @@ var ScreencastCapturer = class {
|
|
|
44
43
|
const cdp = await page.context().newCDPSession(page);
|
|
45
44
|
this.cdpSession = cdp;
|
|
46
45
|
cdp.on("Page.screencastFrame", async (params) => {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}
|
|
51
|
-
}
|
|
46
|
+
const cb = this.frameCallback;
|
|
47
|
+
if (!cb) return;
|
|
48
|
+
cdp.send("Page.screencastFrameAck", { sessionId: params.sessionId }).catch(() => {
|
|
49
|
+
});
|
|
52
50
|
const viewport = {
|
|
53
51
|
width: params.metadata?.deviceWidth || this.maxWidth,
|
|
54
52
|
height: params.metadata?.deviceHeight || this.maxHeight
|
|
55
53
|
};
|
|
56
|
-
|
|
54
|
+
cb({
|
|
57
55
|
id: crypto.randomUUID(),
|
|
58
56
|
sessionId: this.sessionId,
|
|
59
57
|
timestamp: Date.now(),
|
|
@@ -429,6 +427,7 @@ var HumanInteractionManager = class {
|
|
|
429
427
|
});
|
|
430
428
|
}
|
|
431
429
|
};
|
|
430
|
+
|
|
432
431
|
export {
|
|
433
432
|
HumanInteractionManager
|
|
434
433
|
};
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
// src/daemon/daemon.ts
|
|
2
|
+
import { spawn } from "child_process";
|
|
3
|
+
import { join, dirname } from "path";
|
|
4
|
+
import { homedir } from "os";
|
|
5
|
+
import { fileURLToPath } from "url";
|
|
6
|
+
import { writeFileSync, unlinkSync, mkdirSync } from "fs";
|
|
7
|
+
import { stopDaemon as xcliStopDaemon, isDaemonRunning, getDaemonStatus, killAllDaemon } from "@dyyz1993/xcli-core";
|
|
8
|
+
var CONFIG_DIR = join(homedir(), ".xbrowser");
|
|
9
|
+
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
var WORKER_PATH = join(__dirname, "daemon-main.js");
|
|
11
|
+
function getDaemonConfig() {
|
|
12
|
+
return {
|
|
13
|
+
configDir: CONFIG_DIR,
|
|
14
|
+
workerEntryPath: WORKER_PATH,
|
|
15
|
+
basePort: 9224
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
async function startDaemonProcess(port = 9224) {
|
|
19
|
+
const config = getDaemonConfig();
|
|
20
|
+
if (isDaemonRunning(config)) {
|
|
21
|
+
const status = getDaemonStatus(config);
|
|
22
|
+
if (status.port === port && status.pid) {
|
|
23
|
+
return { pid: status.pid, port: status.port, startedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
24
|
+
}
|
|
25
|
+
await xcliStopDaemon(config);
|
|
26
|
+
}
|
|
27
|
+
const lockFile = join(CONFIG_DIR, "daemon.lock");
|
|
28
|
+
try {
|
|
29
|
+
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
30
|
+
writeFileSync(lockFile, String(process.pid), { flag: "wx" });
|
|
31
|
+
} catch {
|
|
32
|
+
console.error("Another process is starting the daemon, waiting...");
|
|
33
|
+
for (let i = 0; i < 50; i++) {
|
|
34
|
+
await new Promise((r) => setTimeout(r, 200));
|
|
35
|
+
if (isDaemonRunning(config)) {
|
|
36
|
+
const s = getDaemonStatus(config);
|
|
37
|
+
if (s.port === port && s.pid) {
|
|
38
|
+
return { pid: s.pid, port: s.port, startedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
try {
|
|
43
|
+
unlinkSync(lockFile);
|
|
44
|
+
} catch {
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
writeFileSync(lockFile, String(process.pid), { flag: "wx" });
|
|
48
|
+
} catch {
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
const child = spawn("node", [WORKER_PATH], {
|
|
52
|
+
detached: true,
|
|
53
|
+
stdio: "ignore",
|
|
54
|
+
env: {
|
|
55
|
+
...process.env,
|
|
56
|
+
XBROWSER_DAEMON_PORT: String(port)
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
child.unref();
|
|
60
|
+
return new Promise((resolve, reject) => {
|
|
61
|
+
let resolved = false;
|
|
62
|
+
const timeout = setTimeout(() => {
|
|
63
|
+
if (!resolved) {
|
|
64
|
+
resolved = true;
|
|
65
|
+
try {
|
|
66
|
+
unlinkSync(lockFile);
|
|
67
|
+
} catch {
|
|
68
|
+
}
|
|
69
|
+
reject(new Error("Daemon start timeout after 15s"));
|
|
70
|
+
}
|
|
71
|
+
}, 15e3);
|
|
72
|
+
const checkInterval = setInterval(() => {
|
|
73
|
+
if (isDaemonRunning(config)) {
|
|
74
|
+
const s = getDaemonStatus(config);
|
|
75
|
+
if (s.port === port && s.pid) {
|
|
76
|
+
resolved = true;
|
|
77
|
+
clearTimeout(timeout);
|
|
78
|
+
clearInterval(checkInterval);
|
|
79
|
+
try {
|
|
80
|
+
unlinkSync(lockFile);
|
|
81
|
+
} catch {
|
|
82
|
+
}
|
|
83
|
+
resolve({ pid: s.pid, port: s.port, startedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}, 200);
|
|
87
|
+
child.on("error", (err) => {
|
|
88
|
+
if (!resolved) {
|
|
89
|
+
resolved = true;
|
|
90
|
+
clearTimeout(timeout);
|
|
91
|
+
clearInterval(checkInterval);
|
|
92
|
+
try {
|
|
93
|
+
unlinkSync(lockFile);
|
|
94
|
+
} catch {
|
|
95
|
+
}
|
|
96
|
+
reject(err);
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
async function stopDaemonProcess() {
|
|
102
|
+
await xcliStopDaemon(getDaemonConfig());
|
|
103
|
+
}
|
|
104
|
+
async function killAllDaemonProcesses() {
|
|
105
|
+
await killAllDaemon(getDaemonConfig());
|
|
106
|
+
}
|
|
107
|
+
function getDaemonProcessStatus() {
|
|
108
|
+
const config = getDaemonConfig();
|
|
109
|
+
const running = isDaemonRunning(config);
|
|
110
|
+
if (!running) {
|
|
111
|
+
return { running: false, pid: 0, port: 0, info: null };
|
|
112
|
+
}
|
|
113
|
+
const status = getDaemonStatus(config);
|
|
114
|
+
return {
|
|
115
|
+
running: true,
|
|
116
|
+
pid: status.pid,
|
|
117
|
+
port: status.port,
|
|
118
|
+
info: { pid: status.pid, port: status.port, startedAt: (/* @__PURE__ */ new Date()).toISOString() }
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export {
|
|
123
|
+
getDaemonConfig,
|
|
124
|
+
startDaemonProcess,
|
|
125
|
+
stopDaemonProcess,
|
|
126
|
+
killAllDaemonProcesses,
|
|
127
|
+
getDaemonProcessStatus
|
|
128
|
+
};
|