@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
package/dist/index.js
CHANGED
|
@@ -1,21 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
detectAntiBot,
|
|
3
3
|
formatDetectionMessage
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import {
|
|
6
|
-
CaptchaDetector,
|
|
7
|
-
HumanInteractionManager,
|
|
8
|
-
NPM_REGISTRY_URL,
|
|
9
|
-
NPM_SCOPE,
|
|
10
|
-
ScreencastCapturer,
|
|
11
|
-
WebhookNotifier,
|
|
12
|
-
getCaptchaConfig,
|
|
13
|
-
getConfigValue,
|
|
14
|
-
getMarketplaceUrl,
|
|
15
|
-
loadConfig,
|
|
16
|
-
resolveNpmPackageWithFallback,
|
|
17
|
-
setConfigValue
|
|
18
|
-
} from "./chunk-ZJHQPMCY.js";
|
|
4
|
+
} from "./chunk-INTQPBYF.js";
|
|
19
5
|
import {
|
|
20
6
|
forwardCommandLog,
|
|
21
7
|
forwardNetworkAnalyze,
|
|
@@ -38,13 +24,32 @@ import {
|
|
|
38
24
|
forwardSessionClose,
|
|
39
25
|
forwardSessionList,
|
|
40
26
|
forwardViewerCheckSelector,
|
|
27
|
+
version
|
|
28
|
+
} from "./chunk-YMUSHPU4.js";
|
|
29
|
+
import {
|
|
30
|
+
buildViewerUrl
|
|
31
|
+
} from "./chunk-3OD76SUE.js";
|
|
32
|
+
import {
|
|
33
|
+
CaptchaDetector,
|
|
34
|
+
HumanInteractionManager,
|
|
35
|
+
NPM_REGISTRY_URL,
|
|
36
|
+
NPM_SCOPE,
|
|
37
|
+
ScreencastCapturer,
|
|
38
|
+
WebhookNotifier,
|
|
39
|
+
getCaptchaConfig,
|
|
40
|
+
getConfigValue,
|
|
41
|
+
getMarketplaceUrl,
|
|
42
|
+
loadConfig,
|
|
43
|
+
resolveNpmPackageWithFallback,
|
|
44
|
+
setConfigValue
|
|
45
|
+
} from "./chunk-DWDXEGVK.js";
|
|
46
|
+
import {
|
|
41
47
|
getDaemonConfig,
|
|
42
48
|
getDaemonProcessStatus,
|
|
43
49
|
killAllDaemonProcesses,
|
|
44
50
|
startDaemonProcess,
|
|
45
|
-
stopDaemonProcess
|
|
46
|
-
|
|
47
|
-
} from "./chunk-YAUG3NSI.js";
|
|
51
|
+
stopDaemonProcess
|
|
52
|
+
} from "./chunk-Q2DFJQGS.js";
|
|
48
53
|
import {
|
|
49
54
|
generateBashScript,
|
|
50
55
|
generateJSScript,
|
|
@@ -61,7 +66,7 @@ import {
|
|
|
61
66
|
} from "./chunk-ANVL2ID2.js";
|
|
62
67
|
import {
|
|
63
68
|
SessionRecorder
|
|
64
|
-
} from "./chunk-
|
|
69
|
+
} from "./chunk-4452SPFI.js";
|
|
65
70
|
import {
|
|
66
71
|
addKnownIssue,
|
|
67
72
|
getKnowledgePath,
|
|
@@ -86,8 +91,8 @@ import {
|
|
|
86
91
|
resolveLaunchOpts,
|
|
87
92
|
saveSessionDiskMeta,
|
|
88
93
|
setActivePage
|
|
89
|
-
} from "./chunk-
|
|
90
|
-
import "./chunk-
|
|
94
|
+
} from "./chunk-RZM5CNIY.js";
|
|
95
|
+
import "./chunk-RRBXV7KE.js";
|
|
91
96
|
import "./chunk-TNEN6VQ2.js";
|
|
92
97
|
import {
|
|
93
98
|
errMsg
|
|
@@ -6197,7 +6202,7 @@ function attachWaitForHuman(ctx, getOrCreateWSServer) {
|
|
|
6197
6202
|
if (!ctx.page) {
|
|
6198
6203
|
throw new Error("waitForHuman requires an active page");
|
|
6199
6204
|
}
|
|
6200
|
-
const { HumanInteractionManager: HumanInteractionManager2 } = await import("./human-interaction-
|
|
6205
|
+
const { HumanInteractionManager: HumanInteractionManager2 } = await import("./human-interaction-C5OEGXGO.js");
|
|
6201
6206
|
const wsServer2 = await getOrCreateWSServer(ctx.browserContext);
|
|
6202
6207
|
const manager = new HumanInteractionManager2(wsServer2, ctx.page);
|
|
6203
6208
|
return manager.waitForHuman(options);
|
|
@@ -6205,7 +6210,7 @@ function attachWaitForHuman(ctx, getOrCreateWSServer) {
|
|
|
6205
6210
|
}
|
|
6206
6211
|
function attachDetectAntiBot(ctx) {
|
|
6207
6212
|
ctx.detectAntiBot = async (page, config) => {
|
|
6208
|
-
const { detectAntiBot: detectAntiBot2 } = await import("./anti-bot-
|
|
6213
|
+
const { detectAntiBot: detectAntiBot2 } = await import("./anti-bot-DR56Y63V.js");
|
|
6209
6214
|
return detectAntiBot2(page, config);
|
|
6210
6215
|
};
|
|
6211
6216
|
}
|
|
@@ -6658,18 +6663,6 @@ async function getPluginLoader() {
|
|
|
6658
6663
|
return pluginLoader;
|
|
6659
6664
|
}
|
|
6660
6665
|
|
|
6661
|
-
// src/utils/viewer-url.ts
|
|
6662
|
-
function buildViewerUrl(sessionName = "default") {
|
|
6663
|
-
try {
|
|
6664
|
-
const status = getDaemonProcessStatus();
|
|
6665
|
-
if (!status.running) return void 0;
|
|
6666
|
-
const port = status.port || getDaemonConfig().basePort;
|
|
6667
|
-
return `http://localhost:${port}/preview/${encodeURIComponent(sessionName)}`;
|
|
6668
|
-
} catch {
|
|
6669
|
-
return void 0;
|
|
6670
|
-
}
|
|
6671
|
-
}
|
|
6672
|
-
|
|
6673
6666
|
// src/plugin/login-guard.ts
|
|
6674
6667
|
async function checkPluginLoginRequired(options) {
|
|
6675
6668
|
const { site, command, commandName, ctx, page, sessionName } = options;
|
|
@@ -7406,7 +7399,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
|
|
|
7406
7399
|
}
|
|
7407
7400
|
let targetPageOverride = null;
|
|
7408
7401
|
if (_target && extraOpts?.cdpEndpoint) {
|
|
7409
|
-
const { findTargetPage } = await import("./browser-
|
|
7402
|
+
const { findTargetPage } = await import("./browser-YCO4GJMZ.js");
|
|
7410
7403
|
targetPageOverride = await findTargetPage(extraOpts.cdpEndpoint, _target);
|
|
7411
7404
|
if (!targetPageOverride) {
|
|
7412
7405
|
return errorResult(`Target "${_target}" not found. Use 'xbrowser targets --cdp ${extraOpts.cdpEndpoint}' to list available pages.`);
|
|
@@ -7423,7 +7416,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
|
|
|
7423
7416
|
params = result.data;
|
|
7424
7417
|
}
|
|
7425
7418
|
if (command.scope !== "cli" && !process.env.XBROWSER_DAEMON_WORKER) {
|
|
7426
|
-
const { forwardExec } = await import("./daemon-client-
|
|
7419
|
+
const { forwardExec } = await import("./daemon-client-O6BYVRXV.js");
|
|
7427
7420
|
const result = await forwardExec(commandName, params, sessionName, extraOpts?.cdpEndpoint);
|
|
7428
7421
|
if (result) return result;
|
|
7429
7422
|
}
|
|
@@ -7611,6 +7604,26 @@ async function executeCommand(commandName, params, sessionName = "default", extr
|
|
|
7611
7604
|
const end = Date.now();
|
|
7612
7605
|
const duration = end - start;
|
|
7613
7606
|
const errorMessage = errMsg(err);
|
|
7607
|
+
if (session?.page && process.env.XBROWSER_RECOVERY && !extraOpts?._recoveryAttempted) {
|
|
7608
|
+
try {
|
|
7609
|
+
const { attemptRecovery } = await import("./recovery-ZJVVHP7N.js");
|
|
7610
|
+
const recovery = await attemptRecovery(
|
|
7611
|
+
session.page,
|
|
7612
|
+
sessionName,
|
|
7613
|
+
commandName,
|
|
7614
|
+
errorMessage
|
|
7615
|
+
);
|
|
7616
|
+
if (recovery.recovered) {
|
|
7617
|
+
const retry = await executeCommand(commandName, params, sessionName, {
|
|
7618
|
+
...extraOpts,
|
|
7619
|
+
skipCleanup: true,
|
|
7620
|
+
_recoveryAttempted: true
|
|
7621
|
+
});
|
|
7622
|
+
return retry;
|
|
7623
|
+
}
|
|
7624
|
+
} catch {
|
|
7625
|
+
}
|
|
7626
|
+
}
|
|
7614
7627
|
if (session) {
|
|
7615
7628
|
streamCommandEvent(session.id, {
|
|
7616
7629
|
sessionId: session.id,
|
|
@@ -10937,7 +10950,7 @@ async function handlePlugin(args, options, mode) {
|
|
|
10937
10950
|
} catch {
|
|
10938
10951
|
}
|
|
10939
10952
|
try {
|
|
10940
|
-
const { daemonPing } = await import("./daemon-client-
|
|
10953
|
+
const { daemonPing } = await import("./daemon-client-O6BYVRXV.js");
|
|
10941
10954
|
if (await daemonPing()) {
|
|
10942
10955
|
await fetch("http://localhost:9224/rpc", {
|
|
10943
10956
|
method: "POST",
|
|
@@ -11503,7 +11516,7 @@ async function handleFilter(args, _mode, options) {
|
|
|
11503
11516
|
}
|
|
11504
11517
|
}
|
|
11505
11518
|
async function handleGeneratePlugin(sessionName, pluginName, outputDir) {
|
|
11506
|
-
const { SessionRecorder: SessionRecorder2 } = await import("./session-recorder-
|
|
11519
|
+
const { SessionRecorder: SessionRecorder2 } = await import("./session-recorder-QRZMKFVL.js");
|
|
11507
11520
|
const { readSiteKnowledge: readSiteKnowledge2, toMarkdown } = await import("./site-knowledge-SYC6VCDB.js");
|
|
11508
11521
|
const { mkdirSync: mkdirSync11, writeFileSync: writeFileSync13 } = await import("fs");
|
|
11509
11522
|
const { join: join14 } = await import("path");
|
|
@@ -11740,7 +11753,7 @@ async function handleRun(filePath, options) {
|
|
|
11740
11753
|
|
|
11741
11754
|
// src/cli/viewer-routes.ts
|
|
11742
11755
|
async function handleViewer(_args, options, mode, _cdpEndpoint) {
|
|
11743
|
-
const name = options.
|
|
11756
|
+
const name = options.session || process.env.XBROWSER_SESSION || "default";
|
|
11744
11757
|
const selector = options.selector;
|
|
11745
11758
|
const userPort = options.port ? Number(options.port) : void 0;
|
|
11746
11759
|
let status = getDaemonProcessStatus();
|
|
@@ -13440,9 +13453,9 @@ Run "xbrowser ${command} ${subCommand} --help" to see available parameters.`
|
|
|
13440
13453
|
}
|
|
13441
13454
|
const needsBrowser = cmdEntry.scope === "page" || cmdEntry.scope === "browser";
|
|
13442
13455
|
if (needsBrowser && !process.env.XBROWSER_DAEMON_WORKER) {
|
|
13443
|
-
const {
|
|
13456
|
+
const { forwardPluginExec } = await import("./daemon-client-O6BYVRXV.js");
|
|
13444
13457
|
const userTimeout = typeof params.timeout === "number" && params.timeout > 0 ? params.timeout * 1e3 + 3e4 : void 0;
|
|
13445
|
-
const result = await
|
|
13458
|
+
const result = await forwardPluginExec(`${command}.${subCommand}`, params, sessionName, cdpEndpoint, userTimeout);
|
|
13446
13459
|
const resultData = result && typeof result === "object" && "data" in result ? result.data : void 0;
|
|
13447
13460
|
if (result && result.success === false && resultData?.code === "LOGIN_REQUIRED") {
|
|
13448
13461
|
process.exitCode = 1;
|
|
@@ -13480,7 +13493,7 @@ Run "xbrowser ${command} ${subCommand} --help" to see available parameters.`
|
|
|
13480
13493
|
const targetPage = pages[cmdTabIndex];
|
|
13481
13494
|
await targetPage.bringToFront().catch(() => {
|
|
13482
13495
|
});
|
|
13483
|
-
const { setActivePage: setActivePage2 } = await import("./browser-
|
|
13496
|
+
const { setActivePage: setActivePage2 } = await import("./browser-YCO4GJMZ.js");
|
|
13484
13497
|
setActivePage2(session, targetPage);
|
|
13485
13498
|
}
|
|
13486
13499
|
}
|
|
@@ -14085,10 +14098,37 @@ import { EventEmitter as EventEmitter2 } from "events";
|
|
|
14085
14098
|
// src/stream/frame-processor.ts
|
|
14086
14099
|
import sharp from "sharp";
|
|
14087
14100
|
var STATE_CONFIGS = {
|
|
14088
|
-
|
|
14089
|
-
|
|
14090
|
-
|
|
14101
|
+
// Full-screen: aggressive compression for speed
|
|
14102
|
+
// user_interacting: low quality + downscale — prioritize FPS over clarity
|
|
14103
|
+
user_interacting: { format: "jpeg", quality: 40, maxFps: 30, scale: 0.6 },
|
|
14104
|
+
// screen_moving: page is animating but user is NOT operating.
|
|
14105
|
+
// Keep quality close to interacting to avoid visible flicker on state change.
|
|
14106
|
+
screen_moving: { format: "jpeg", quality: 45, maxFps: 2, scale: 0.7 },
|
|
14107
|
+
// static: highest quality — user is reading, bandwidth is not a concern
|
|
14108
|
+
static: { format: "jpeg", quality: 95, maxFps: 1, scale: 1 }
|
|
14091
14109
|
};
|
|
14110
|
+
function adjustConfigForSize(config, frameWidth, frameHeight, fullViewportWidth, fullViewportHeight) {
|
|
14111
|
+
const framePixels = frameWidth * frameHeight;
|
|
14112
|
+
const fullPixels = fullViewportWidth * fullViewportHeight;
|
|
14113
|
+
const ratio = fullPixels > 0 ? framePixels / fullPixels : 1;
|
|
14114
|
+
if (ratio < 0.3) {
|
|
14115
|
+
return {
|
|
14116
|
+
...config,
|
|
14117
|
+
quality: Math.min(config.quality + 40, 90),
|
|
14118
|
+
// boost quality
|
|
14119
|
+
scale: Math.max(config.scale, 0.9)
|
|
14120
|
+
// don't downscale
|
|
14121
|
+
};
|
|
14122
|
+
}
|
|
14123
|
+
if (ratio < 0.6) {
|
|
14124
|
+
return {
|
|
14125
|
+
...config,
|
|
14126
|
+
quality: Math.min(config.quality + 20, 80),
|
|
14127
|
+
scale: Math.max(config.scale, 0.7)
|
|
14128
|
+
};
|
|
14129
|
+
}
|
|
14130
|
+
return config;
|
|
14131
|
+
}
|
|
14092
14132
|
var StreamStateManager = class {
|
|
14093
14133
|
currentState = "static";
|
|
14094
14134
|
isUserInteracting = false;
|
|
@@ -14097,9 +14137,11 @@ var StreamStateManager = class {
|
|
|
14097
14137
|
frameInterval = Infinity;
|
|
14098
14138
|
onStateChange = null;
|
|
14099
14139
|
staticTimer = null;
|
|
14100
|
-
USER_INTERACTION_TIMEOUT_MS =
|
|
14140
|
+
USER_INTERACTION_TIMEOUT_MS = 2e3;
|
|
14141
|
+
// 1s → 2s: linger in interacting longer
|
|
14101
14142
|
SCREEN_MOVING_THRESHOLD_MS = 1e3;
|
|
14102
|
-
STATIC_TIMEOUT_MS =
|
|
14143
|
+
STATIC_TIMEOUT_MS = 3e3;
|
|
14144
|
+
// 1.5s → 3s: don't rush to static
|
|
14103
14145
|
setStateChangeCallback(callback) {
|
|
14104
14146
|
this.onStateChange = callback;
|
|
14105
14147
|
}
|
|
@@ -14208,12 +14250,25 @@ var FrameProcessor = class {
|
|
|
14208
14250
|
}
|
|
14209
14251
|
let processed = sharp(buffer);
|
|
14210
14252
|
if (cropConfig) {
|
|
14211
|
-
|
|
14212
|
-
|
|
14213
|
-
|
|
14214
|
-
|
|
14215
|
-
|
|
14216
|
-
|
|
14253
|
+
const meta = await processed.metadata();
|
|
14254
|
+
const actualW = meta.width ?? viewportWidth ?? 1;
|
|
14255
|
+
const actualH = meta.height ?? viewportHeight ?? 1;
|
|
14256
|
+
const refW = viewportWidth ?? actualW;
|
|
14257
|
+
const refH = viewportHeight ?? actualH;
|
|
14258
|
+
const scaleX = actualW / refW;
|
|
14259
|
+
const scaleY = actualH / refH;
|
|
14260
|
+
const cropLeft = Math.round(cropConfig.x * scaleX);
|
|
14261
|
+
const cropTop = Math.round(cropConfig.y * scaleY);
|
|
14262
|
+
const cropW = Math.min(Math.round(cropConfig.width * scaleX), actualW - cropLeft);
|
|
14263
|
+
const cropH = Math.min(Math.round(cropConfig.height * scaleY), actualH - cropTop);
|
|
14264
|
+
if (cropW > 0 && cropH > 0 && cropLeft >= 0 && cropTop >= 0) {
|
|
14265
|
+
processed = processed.extract({
|
|
14266
|
+
left: cropLeft,
|
|
14267
|
+
top: cropTop,
|
|
14268
|
+
width: cropW,
|
|
14269
|
+
height: cropH
|
|
14270
|
+
});
|
|
14271
|
+
}
|
|
14217
14272
|
}
|
|
14218
14273
|
if (needsResize) {
|
|
14219
14274
|
const newWidth = Math.round((viewportWidth ?? 0) * config.scale);
|
|
@@ -14362,11 +14417,20 @@ var StreamCoordinator = class {
|
|
|
14362
14417
|
const crop = this.sessionCrops.get(sessionId);
|
|
14363
14418
|
const cropConfig = crop ? crop.box : void 0;
|
|
14364
14419
|
const effectiveViewport = crop ? { width: crop.box.width, height: crop.box.height } : frameViewport;
|
|
14365
|
-
const
|
|
14366
|
-
frameData,
|
|
14420
|
+
const adjustedConfig = adjustConfigForSize(
|
|
14367
14421
|
config,
|
|
14368
14422
|
effectiveViewport.width,
|
|
14369
14423
|
effectiveViewport.height,
|
|
14424
|
+
frameViewport.width,
|
|
14425
|
+
frameViewport.height
|
|
14426
|
+
);
|
|
14427
|
+
const processedBuffer = await this.frameProcessor.process(
|
|
14428
|
+
frameData,
|
|
14429
|
+
adjustedConfig,
|
|
14430
|
+
// Pass the ORIGINAL viewport (not cropped) so crop coordinates
|
|
14431
|
+
// can be scaled correctly from viewport space to frame pixel space.
|
|
14432
|
+
crop ? frameViewport.width : effectiveViewport.width,
|
|
14433
|
+
crop ? frameViewport.height : effectiveViewport.height,
|
|
14370
14434
|
cropConfig
|
|
14371
14435
|
);
|
|
14372
14436
|
const header = Buffer.from(JSON.stringify({
|
|
@@ -14538,7 +14602,18 @@ var ElementMonitor = class {
|
|
|
14538
14602
|
}
|
|
14539
14603
|
return results;
|
|
14540
14604
|
});
|
|
14541
|
-
const
|
|
14605
|
+
const TOLERANCE_PX = 10;
|
|
14606
|
+
const deduped = [];
|
|
14607
|
+
const isSameSize = (a, b) => Math.abs(a.width - b.width) <= TOLERANCE_PX && Math.abs(a.height - b.height) <= TOLERANCE_PX;
|
|
14608
|
+
const sorted = [...elements].sort((a, b) => a.rect.width * a.rect.height - b.rect.width * b.rect.height);
|
|
14609
|
+
for (const el of sorted) {
|
|
14610
|
+
const dupe = deduped.some(
|
|
14611
|
+
(d) => isSameSize(d.rect, el.rect) && Math.abs(d.rect.x - el.rect.x) <= TOLERANCE_PX && Math.abs(d.rect.y - el.rect.y) <= TOLERANCE_PX
|
|
14612
|
+
);
|
|
14613
|
+
if (!dupe) deduped.push(el);
|
|
14614
|
+
}
|
|
14615
|
+
deduped.sort((a, b) => b.rect.width * b.rect.height - a.rect.width * a.rect.height);
|
|
14616
|
+
const views = deduped.map((e, i) => ({
|
|
14542
14617
|
id: "el-" + i + "-" + (e.id || e.tag),
|
|
14543
14618
|
label: e.id || e.cls || e.tag,
|
|
14544
14619
|
rect: e.rect
|
|
@@ -14597,10 +14672,10 @@ var SessionManager = class extends EventEmitter {
|
|
|
14597
14672
|
this.screencasts.set(sessionId, {
|
|
14598
14673
|
capturer: new ScreencastCapturer({
|
|
14599
14674
|
interval: options?.interval ?? 100,
|
|
14600
|
-
quality: options?.quality ??
|
|
14675
|
+
quality: options?.quality ?? 40,
|
|
14601
14676
|
type: options?.type ?? "jpeg",
|
|
14602
|
-
width: options?.width ??
|
|
14603
|
-
height: options?.height ??
|
|
14677
|
+
width: options?.width ?? 1024,
|
|
14678
|
+
height: options?.height ?? 576
|
|
14604
14679
|
}),
|
|
14605
14680
|
page,
|
|
14606
14681
|
clientCount: 0
|
|
@@ -14741,6 +14816,8 @@ var SessionManager = class extends EventEmitter {
|
|
|
14741
14816
|
this.resetStaticSnapshotTimer(sessionId);
|
|
14742
14817
|
});
|
|
14743
14818
|
await sc.capturer.startCapture(sc.page, sessionId, onFrame);
|
|
14819
|
+
this.streamCoordinator.takeStaticSnapshot(sessionId, sc.page, sc.clientCount).catch(() => {
|
|
14820
|
+
});
|
|
14744
14821
|
}
|
|
14745
14822
|
async stopCapturer(sessionId) {
|
|
14746
14823
|
const sc = this.screencasts.get(sessionId);
|
|
@@ -15191,6 +15268,15 @@ var WSServer = class extends EventEmitter2 {
|
|
|
15191
15268
|
registerSession(sessionId, page, options) {
|
|
15192
15269
|
this.sessionManager.registerSession(sessionId, page, options);
|
|
15193
15270
|
}
|
|
15271
|
+
/** Restart screencast for a session — used by viewer reconnect button. */
|
|
15272
|
+
async reconnectSession(sessionId) {
|
|
15273
|
+
try {
|
|
15274
|
+
await this.sessionManager.stopCapturer(sessionId);
|
|
15275
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
15276
|
+
await this.sessionManager.startCapturer(sessionId);
|
|
15277
|
+
} catch {
|
|
15278
|
+
}
|
|
15279
|
+
}
|
|
15194
15280
|
unregisterSession(sessionId) {
|
|
15195
15281
|
this.sessionManager.unregisterSession(sessionId);
|
|
15196
15282
|
}
|
|
@@ -15213,7 +15299,7 @@ var WSServer = class extends EventEmitter2 {
|
|
|
15213
15299
|
});
|
|
15214
15300
|
}
|
|
15215
15301
|
this.sessionManager.incrementClientCount(sessionId);
|
|
15216
|
-
if (this.streamCoordinator.getLastFrameViewport()
|
|
15302
|
+
if (this.streamCoordinator.getLastFrameViewport()) {
|
|
15217
15303
|
this.streamCoordinator.replayLastFrame(sessionId).catch(() => {
|
|
15218
15304
|
});
|
|
15219
15305
|
}
|
|
@@ -15223,7 +15309,6 @@ var WSServer = class extends EventEmitter2 {
|
|
|
15223
15309
|
if (count === 0 && this.sessionManager.isCapturerActive(sessionId)) {
|
|
15224
15310
|
this.sessionManager.stopCapturer(sessionId).then(() => {
|
|
15225
15311
|
this.streamCoordinator.resetFrameRate();
|
|
15226
|
-
this.streamCoordinator.clearLastFrame();
|
|
15227
15312
|
this.emit("screencast-stopped", sessionId);
|
|
15228
15313
|
}).catch(() => {
|
|
15229
15314
|
this.emit("screencast-stopped", sessionId);
|
|
@@ -15399,6 +15484,31 @@ var WSServer = class extends EventEmitter2 {
|
|
|
15399
15484
|
this.emit("human-solved", { sessionId: sessionId ?? null, clientId });
|
|
15400
15485
|
return;
|
|
15401
15486
|
}
|
|
15487
|
+
if (msg.type === "reconnect") {
|
|
15488
|
+
this.emit("reconnect-request", { sessionId: sessionId ?? null, clientId });
|
|
15489
|
+
return;
|
|
15490
|
+
}
|
|
15491
|
+
if (msg.type === "health_ping") {
|
|
15492
|
+
this.sendToClient(clientId, { type: "health_pong", ts: msg.ts });
|
|
15493
|
+
return;
|
|
15494
|
+
}
|
|
15495
|
+
if (msg.type === "snapshot_request") {
|
|
15496
|
+
const page2 = sessionId ? this.sessionManager.getPageForSession(sessionId) : void 0;
|
|
15497
|
+
if (!page2) {
|
|
15498
|
+
this.sendToClient(clientId, { type: "snapshot_result", data: null, error: "no page" });
|
|
15499
|
+
return;
|
|
15500
|
+
}
|
|
15501
|
+
const fmt = msg.format === "webp" ? "webp" : "png";
|
|
15502
|
+
page2.screenshot({ type: fmt, quality: fmt === "webp" ? msg.quality ?? 90 : void 0 }).then((buf) => {
|
|
15503
|
+
this.sendToClient(clientId, {
|
|
15504
|
+
type: "snapshot_result",
|
|
15505
|
+
data: { data: buf.toString("base64"), format: fmt }
|
|
15506
|
+
});
|
|
15507
|
+
}).catch(() => {
|
|
15508
|
+
this.sendToClient(clientId, { type: "snapshot_result", data: null, error: "screenshot failed" });
|
|
15509
|
+
});
|
|
15510
|
+
return;
|
|
15511
|
+
}
|
|
15402
15512
|
const ctx = {
|
|
15403
15513
|
clientId,
|
|
15404
15514
|
sessionId,
|
|
@@ -16543,7 +16653,7 @@ var DataCollector = class {
|
|
|
16543
16653
|
return results;
|
|
16544
16654
|
}
|
|
16545
16655
|
async createBrowserContext() {
|
|
16546
|
-
const { launch } = await import("./cdp-driver-
|
|
16656
|
+
const { launch } = await import("./cdp-driver-J3YQ4LJV.js");
|
|
16547
16657
|
const { browser } = await launch({
|
|
16548
16658
|
headless: true,
|
|
16549
16659
|
args: ["--no-sandbox", "--disable-setuid-sandbox"]
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import {
|
|
2
|
+
buildViewerUrl
|
|
3
|
+
} from "./chunk-TZPKFUBT.js";
|
|
4
|
+
import "./chunk-CG3D3CHY.js";
|
|
5
|
+
import {
|
|
6
|
+
HumanInteractionManager
|
|
7
|
+
} from "./chunk-SQHSZENE.js";
|
|
8
|
+
import "./chunk-BNO7OKO4.js";
|
|
9
|
+
import "./chunk-KFQGP6VL.js";
|
|
10
|
+
|
|
11
|
+
// src/recovery.ts
|
|
12
|
+
function getRecoveryConfig() {
|
|
13
|
+
const val = process.env.XBROWSER_RECOVERY?.toLowerCase();
|
|
14
|
+
return {
|
|
15
|
+
enabled: val === "true" || val === "1" || val === "yes",
|
|
16
|
+
timeout: Math.max(10, parseInt(process.env.XBROWSER_RECOVERY_TIMEOUT || "120", 10))
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
async function attemptRecovery(page, sessionName, commandName, errorMessage, previewWS) {
|
|
20
|
+
const cfg = getRecoveryConfig();
|
|
21
|
+
if (!cfg.enabled) return { recovered: false };
|
|
22
|
+
if (!page) {
|
|
23
|
+
return { recovered: false };
|
|
24
|
+
}
|
|
25
|
+
const viewerUrl = buildViewerUrl(sessionName);
|
|
26
|
+
if (previewWS) {
|
|
27
|
+
try {
|
|
28
|
+
previewWS.registerSession(sessionName, page);
|
|
29
|
+
const manager = new HumanInteractionManager(previewWS, page);
|
|
30
|
+
const result = await manager.waitForHuman({
|
|
31
|
+
reason: `Command "${commandName}" failed: ${errorMessage}. Fix the issue and confirm via viewer.`,
|
|
32
|
+
timeout: cfg.timeout,
|
|
33
|
+
autoDetect: false
|
|
34
|
+
});
|
|
35
|
+
return { recovered: result.solved };
|
|
36
|
+
} catch {
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
console.log("");
|
|
40
|
+
console.log("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550");
|
|
41
|
+
console.log(`\u26A0\uFE0F COMMAND FAILED: ${commandName}`);
|
|
42
|
+
console.log(`\u26A0\uFE0F Error: ${errorMessage}`);
|
|
43
|
+
console.log(`\u26A0\uFE0F `);
|
|
44
|
+
console.log(`\u26A0\uFE0F Recovery mode activated`);
|
|
45
|
+
if (viewerUrl) {
|
|
46
|
+
console.log(`\u26A0\uFE0F Viewer: ${viewerUrl}`);
|
|
47
|
+
}
|
|
48
|
+
console.log(`\u26A0\uFE0F `);
|
|
49
|
+
console.log(`\u26A0\uFE0F Fix the issue, then press ENTER to retry`);
|
|
50
|
+
console.log(`\u26A0\uFE0F Type "abort" + ENTER to cancel`);
|
|
51
|
+
console.log(`\u26A0\uFE0F (${cfg.timeout}s timeout)`);
|
|
52
|
+
console.log("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550");
|
|
53
|
+
console.log("");
|
|
54
|
+
return new Promise((resolve) => {
|
|
55
|
+
const cleanup = () => {
|
|
56
|
+
clearTimeout(timeoutTimer);
|
|
57
|
+
process.stdin.removeListener("data", onData);
|
|
58
|
+
process.stdin.pause();
|
|
59
|
+
};
|
|
60
|
+
const timeoutTimer = setTimeout(() => {
|
|
61
|
+
console.log("\u23F0 Recovery timeout \u2014 aborting");
|
|
62
|
+
cleanup();
|
|
63
|
+
resolve({ recovered: false });
|
|
64
|
+
}, cfg.timeout * 1e3);
|
|
65
|
+
const onData = (buf) => {
|
|
66
|
+
const line = buf.toString().trim().toLowerCase();
|
|
67
|
+
cleanup();
|
|
68
|
+
resolve({ recovered: line !== "abort" && line !== "q" && line !== "exit" });
|
|
69
|
+
};
|
|
70
|
+
process.stdin.resume();
|
|
71
|
+
process.stdin.on("data", onData);
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
export {
|
|
75
|
+
attemptRecovery,
|
|
76
|
+
getRecoveryConfig
|
|
77
|
+
};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import {
|
|
2
|
+
buildViewerUrl
|
|
3
|
+
} from "./chunk-3OD76SUE.js";
|
|
4
|
+
import "./chunk-Q2DFJQGS.js";
|
|
5
|
+
import {
|
|
6
|
+
HumanInteractionManager
|
|
7
|
+
} from "./chunk-MWFHZUIY.js";
|
|
8
|
+
import "./chunk-IIM5GOD7.js";
|
|
9
|
+
import "./chunk-KFQGP6VL.js";
|
|
10
|
+
|
|
11
|
+
// src/recovery.ts
|
|
12
|
+
function getRecoveryConfig() {
|
|
13
|
+
const val = process.env.XBROWSER_RECOVERY?.toLowerCase();
|
|
14
|
+
return {
|
|
15
|
+
enabled: val === "true" || val === "1" || val === "yes",
|
|
16
|
+
timeout: Math.max(10, parseInt(process.env.XBROWSER_RECOVERY_TIMEOUT || "120", 10))
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
async function attemptRecovery(page, sessionName, commandName, errorMessage, previewWS) {
|
|
20
|
+
const cfg = getRecoveryConfig();
|
|
21
|
+
if (!cfg.enabled) return { recovered: false };
|
|
22
|
+
if (!page) {
|
|
23
|
+
return { recovered: false };
|
|
24
|
+
}
|
|
25
|
+
const viewerUrl = buildViewerUrl(sessionName);
|
|
26
|
+
if (previewWS) {
|
|
27
|
+
try {
|
|
28
|
+
previewWS.registerSession(sessionName, page);
|
|
29
|
+
const manager = new HumanInteractionManager(previewWS, page);
|
|
30
|
+
const result = await manager.waitForHuman({
|
|
31
|
+
reason: `Command "${commandName}" failed: ${errorMessage}. Fix the issue and confirm via viewer.`,
|
|
32
|
+
timeout: cfg.timeout,
|
|
33
|
+
autoDetect: false
|
|
34
|
+
});
|
|
35
|
+
return { recovered: result.solved };
|
|
36
|
+
} catch {
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
console.log("");
|
|
40
|
+
console.log("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550");
|
|
41
|
+
console.log(`\u26A0\uFE0F COMMAND FAILED: ${commandName}`);
|
|
42
|
+
console.log(`\u26A0\uFE0F Error: ${errorMessage}`);
|
|
43
|
+
console.log(`\u26A0\uFE0F `);
|
|
44
|
+
console.log(`\u26A0\uFE0F Recovery mode activated`);
|
|
45
|
+
if (viewerUrl) {
|
|
46
|
+
console.log(`\u26A0\uFE0F Viewer: ${viewerUrl}`);
|
|
47
|
+
}
|
|
48
|
+
console.log(`\u26A0\uFE0F `);
|
|
49
|
+
console.log(`\u26A0\uFE0F Fix the issue, then press ENTER to retry`);
|
|
50
|
+
console.log(`\u26A0\uFE0F Type "abort" + ENTER to cancel`);
|
|
51
|
+
console.log(`\u26A0\uFE0F (${cfg.timeout}s timeout)`);
|
|
52
|
+
console.log("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550");
|
|
53
|
+
console.log("");
|
|
54
|
+
return new Promise((resolve) => {
|
|
55
|
+
const cleanup = () => {
|
|
56
|
+
clearTimeout(timeoutTimer);
|
|
57
|
+
process.stdin.removeListener("data", onData);
|
|
58
|
+
process.stdin.pause();
|
|
59
|
+
};
|
|
60
|
+
const timeoutTimer = setTimeout(() => {
|
|
61
|
+
console.log("\u23F0 Recovery timeout \u2014 aborting");
|
|
62
|
+
cleanup();
|
|
63
|
+
resolve({ recovered: false });
|
|
64
|
+
}, cfg.timeout * 1e3);
|
|
65
|
+
const onData = (buf) => {
|
|
66
|
+
const line = buf.toString().trim().toLowerCase();
|
|
67
|
+
cleanup();
|
|
68
|
+
resolve({ recovered: line !== "abort" && line !== "q" && line !== "exit" });
|
|
69
|
+
};
|
|
70
|
+
process.stdin.resume();
|
|
71
|
+
process.stdin.on("data", onData);
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
export {
|
|
75
|
+
attemptRecovery,
|
|
76
|
+
getRecoveryConfig
|
|
77
|
+
};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import {
|
|
2
|
+
buildViewerUrl
|
|
3
|
+
} from "./chunk-3OD76SUE.js";
|
|
4
|
+
import {
|
|
5
|
+
HumanInteractionManager
|
|
6
|
+
} from "./chunk-DWDXEGVK.js";
|
|
7
|
+
import "./chunk-Q2DFJQGS.js";
|
|
8
|
+
import "./chunk-KFQGP6VL.js";
|
|
9
|
+
|
|
10
|
+
// src/recovery.ts
|
|
11
|
+
function getRecoveryConfig() {
|
|
12
|
+
const val = process.env.XBROWSER_RECOVERY?.toLowerCase();
|
|
13
|
+
return {
|
|
14
|
+
enabled: val === "true" || val === "1" || val === "yes",
|
|
15
|
+
timeout: Math.max(10, parseInt(process.env.XBROWSER_RECOVERY_TIMEOUT || "120", 10))
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
async function attemptRecovery(page, sessionName, commandName, errorMessage, previewWS) {
|
|
19
|
+
const cfg = getRecoveryConfig();
|
|
20
|
+
if (!cfg.enabled) return { recovered: false };
|
|
21
|
+
if (!page) {
|
|
22
|
+
return { recovered: false };
|
|
23
|
+
}
|
|
24
|
+
const viewerUrl = buildViewerUrl(sessionName);
|
|
25
|
+
if (previewWS) {
|
|
26
|
+
try {
|
|
27
|
+
previewWS.registerSession(sessionName, page);
|
|
28
|
+
const manager = new HumanInteractionManager(previewWS, page);
|
|
29
|
+
const result = await manager.waitForHuman({
|
|
30
|
+
reason: `Command "${commandName}" failed: ${errorMessage}. Fix the issue and confirm via viewer.`,
|
|
31
|
+
timeout: cfg.timeout,
|
|
32
|
+
autoDetect: false
|
|
33
|
+
});
|
|
34
|
+
return { recovered: result.solved };
|
|
35
|
+
} catch {
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
console.log("");
|
|
39
|
+
console.log("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550");
|
|
40
|
+
console.log(`\u26A0\uFE0F COMMAND FAILED: ${commandName}`);
|
|
41
|
+
console.log(`\u26A0\uFE0F Error: ${errorMessage}`);
|
|
42
|
+
console.log(`\u26A0\uFE0F `);
|
|
43
|
+
console.log(`\u26A0\uFE0F Recovery mode activated`);
|
|
44
|
+
if (viewerUrl) {
|
|
45
|
+
console.log(`\u26A0\uFE0F Viewer: ${viewerUrl}`);
|
|
46
|
+
}
|
|
47
|
+
console.log(`\u26A0\uFE0F `);
|
|
48
|
+
console.log(`\u26A0\uFE0F Fix the issue, then press ENTER to retry`);
|
|
49
|
+
console.log(`\u26A0\uFE0F Type "abort" + ENTER to cancel`);
|
|
50
|
+
console.log(`\u26A0\uFE0F (${cfg.timeout}s timeout)`);
|
|
51
|
+
console.log("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550");
|
|
52
|
+
console.log("");
|
|
53
|
+
return new Promise((resolve) => {
|
|
54
|
+
const cleanup = () => {
|
|
55
|
+
clearTimeout(timeoutTimer);
|
|
56
|
+
process.stdin.removeListener("data", onData);
|
|
57
|
+
process.stdin.pause();
|
|
58
|
+
};
|
|
59
|
+
const timeoutTimer = setTimeout(() => {
|
|
60
|
+
console.log("\u23F0 Recovery timeout \u2014 aborting");
|
|
61
|
+
cleanup();
|
|
62
|
+
resolve({ recovered: false });
|
|
63
|
+
}, cfg.timeout * 1e3);
|
|
64
|
+
const onData = (buf) => {
|
|
65
|
+
const line = buf.toString().trim().toLowerCase();
|
|
66
|
+
cleanup();
|
|
67
|
+
resolve({ recovered: line !== "abort" && line !== "q" && line !== "exit" });
|
|
68
|
+
};
|
|
69
|
+
process.stdin.resume();
|
|
70
|
+
process.stdin.on("data", onData);
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
export {
|
|
74
|
+
attemptRecovery,
|
|
75
|
+
getRecoveryConfig
|
|
76
|
+
};
|