@xbrowser/cli 1.9.8 → 1.10.0
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-SJWPJFN7.js → browser-HBL72GPZ.js} +2 -2
- package/dist/{browser-GT4BMX6X.js → browser-T3V3JWVH.js} +1 -1
- package/dist/{browser-43YXUIML.js → browser-WQZ3D6AE.js} +2 -2
- package/dist/{cdp-driver-WAFYSIIW.js → cdp-driver-2T4P4ZE2.js} +87 -1
- package/dist/{cdp-driver-PUZLDSQU.js → cdp-driver-J3YQ4LJV.js} +1 -1
- package/dist/chunk-3OD76SUE.js +20 -0
- package/dist/chunk-43TEMG4U.js +9 -0
- package/dist/{chunk-C3LIGO4V.js → chunk-4452SPFI.js} +906 -16
- package/dist/{chunk-VEWTHYDG.js → chunk-5UGR6MUK.js} +906 -16
- package/dist/{chunk-ENCEMM7Z.js → chunk-BAHSRZIX.js} +82 -29
- 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-DPJNCMLC.js → chunk-INTQPBYF.js} +7 -0
- package/dist/{human-interaction-OU7LZ6OZ.js → chunk-MWFHZUIY.js} +6 -7
- package/dist/{chunk-A4YHJW3Z.js → chunk-NITFVWWS.js} +168 -29
- package/dist/chunk-Q2DFJQGS.js +128 -0
- package/dist/{chunk-GM3HWQ5U.js → chunk-RRBXV7KE.js} +87 -1
- package/dist/{human-interaction-HJTXFVQS.js → chunk-SQHSZENE.js} +2 -2
- package/dist/chunk-TZPKFUBT.js +20 -0
- package/dist/{chunk-PBDID7SE.js → chunk-WSCP7QCJ.js} +82 -29
- package/dist/{chunk-YAUG3NSI.js → chunk-YMUSHPU4.js} +16 -128
- package/dist/cli.js +48 -31
- 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 +649 -58
- 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 +167 -2
- package/dist/index.js +194 -67
- 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-K3K63Q33.js → session-recorder-H3KEYU26.js} +1 -1
- package/dist/{session-recorder-AG6CH6EI.js → session-recorder-QRZMKFVL.js} +1 -1
- package/dist/{session-replayer-V4MP6M6I.js → session-replayer-LJUC4TI7.js} +24 -7
- 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-WSCP7QCJ.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-HBL72GPZ.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();
|
|
@@ -13071,12 +13084,16 @@ async function routeCommand(argvIn, stdinCommands) {
|
|
|
13071
13084
|
}
|
|
13072
13085
|
const globalFlags = /* @__PURE__ */ new Set(["--session", "--cdp", "--json", "--yaml", "--output", "--timeout", "--help", "-h", "--version"]);
|
|
13073
13086
|
let chainArgIdx = -1;
|
|
13087
|
+
let seenCommandWord = false;
|
|
13074
13088
|
for (let i = 0; i < argv.length; i++) {
|
|
13075
13089
|
if (globalFlags.has(argv[i])) continue;
|
|
13076
13090
|
if (globalFlags.has(argv[i]) || i > 0 && globalFlags.has(argv[i - 1]) && !argv[i].startsWith("-")) continue;
|
|
13077
|
-
if (
|
|
13078
|
-
|
|
13079
|
-
|
|
13091
|
+
if (!seenCommandWord) {
|
|
13092
|
+
if (argv[i].includes(" ") && /^[a-zA-Z]/.test(argv[i])) {
|
|
13093
|
+
chainArgIdx = i;
|
|
13094
|
+
break;
|
|
13095
|
+
}
|
|
13096
|
+
seenCommandWord = true;
|
|
13080
13097
|
}
|
|
13081
13098
|
}
|
|
13082
13099
|
if (chainArgIdx >= 0) {
|
|
@@ -13440,9 +13457,9 @@ Run "xbrowser ${command} ${subCommand} --help" to see available parameters.`
|
|
|
13440
13457
|
}
|
|
13441
13458
|
const needsBrowser = cmdEntry.scope === "page" || cmdEntry.scope === "browser";
|
|
13442
13459
|
if (needsBrowser && !process.env.XBROWSER_DAEMON_WORKER) {
|
|
13443
|
-
const {
|
|
13460
|
+
const { forwardPluginExec } = await import("./daemon-client-O6BYVRXV.js");
|
|
13444
13461
|
const userTimeout = typeof params.timeout === "number" && params.timeout > 0 ? params.timeout * 1e3 + 3e4 : void 0;
|
|
13445
|
-
const result = await
|
|
13462
|
+
const result = await forwardPluginExec(`${command}.${subCommand}`, params, sessionName, cdpEndpoint, userTimeout);
|
|
13446
13463
|
const resultData = result && typeof result === "object" && "data" in result ? result.data : void 0;
|
|
13447
13464
|
if (result && result.success === false && resultData?.code === "LOGIN_REQUIRED") {
|
|
13448
13465
|
process.exitCode = 1;
|
|
@@ -13480,7 +13497,7 @@ Run "xbrowser ${command} ${subCommand} --help" to see available parameters.`
|
|
|
13480
13497
|
const targetPage = pages[cmdTabIndex];
|
|
13481
13498
|
await targetPage.bringToFront().catch(() => {
|
|
13482
13499
|
});
|
|
13483
|
-
const { setActivePage: setActivePage2 } = await import("./browser-
|
|
13500
|
+
const { setActivePage: setActivePage2 } = await import("./browser-HBL72GPZ.js");
|
|
13484
13501
|
setActivePage2(session, targetPage);
|
|
13485
13502
|
}
|
|
13486
13503
|
}
|
|
@@ -14083,12 +14100,51 @@ var PlaybackEngine = class _PlaybackEngine {
|
|
|
14083
14100
|
import { EventEmitter as EventEmitter2 } from "events";
|
|
14084
14101
|
|
|
14085
14102
|
// src/stream/frame-processor.ts
|
|
14086
|
-
|
|
14103
|
+
var _sharp = null;
|
|
14104
|
+
async function loadSharp() {
|
|
14105
|
+
if (!_sharp) {
|
|
14106
|
+
try {
|
|
14107
|
+
_sharp = (await import("sharp")).default;
|
|
14108
|
+
} catch (err) {
|
|
14109
|
+
throw new Error(
|
|
14110
|
+
"\u76F4\u64AD\u6D41\u5E27\u5904\u7406\u9700\u8981 sharp \u539F\u751F\u6A21\u5757\uFF0C\u5F53\u524D\u73AF\u5883\u52A0\u8F7D\u5931\u8D25\uFF1A" + (err instanceof Error ? err.message : String(err)) + "\u3002\u4E0D\u7528\u76F4\u64AD\u6D41\u529F\u80FD\u53EF\u5FFD\u7565\uFF1B\u9700\u8981\u76F4\u64AD\u8BF7\u5728\u517C\u5BB9\u73AF\u5883\u8FD0\u884C\u6216\u5B89\u88C5\u5BF9\u5E94\u5E73\u53F0\u4F9D\u8D56\u3002"
|
|
14111
|
+
);
|
|
14112
|
+
}
|
|
14113
|
+
}
|
|
14114
|
+
return _sharp;
|
|
14115
|
+
}
|
|
14087
14116
|
var STATE_CONFIGS = {
|
|
14088
|
-
|
|
14089
|
-
|
|
14090
|
-
|
|
14117
|
+
// Full-screen: aggressive compression for speed
|
|
14118
|
+
// user_interacting: low quality + downscale — prioritize FPS over clarity
|
|
14119
|
+
user_interacting: { format: "jpeg", quality: 40, maxFps: 30, scale: 0.6 },
|
|
14120
|
+
// screen_moving: page is animating but user is NOT operating.
|
|
14121
|
+
// Keep quality close to interacting to avoid visible flicker on state change.
|
|
14122
|
+
screen_moving: { format: "jpeg", quality: 45, maxFps: 2, scale: 0.7 },
|
|
14123
|
+
// static: highest quality — user is reading, bandwidth is not a concern
|
|
14124
|
+
static: { format: "jpeg", quality: 95, maxFps: 1, scale: 1 }
|
|
14091
14125
|
};
|
|
14126
|
+
function adjustConfigForSize(config, frameWidth, frameHeight, fullViewportWidth, fullViewportHeight) {
|
|
14127
|
+
const framePixels = frameWidth * frameHeight;
|
|
14128
|
+
const fullPixels = fullViewportWidth * fullViewportHeight;
|
|
14129
|
+
const ratio = fullPixels > 0 ? framePixels / fullPixels : 1;
|
|
14130
|
+
if (ratio < 0.3) {
|
|
14131
|
+
return {
|
|
14132
|
+
...config,
|
|
14133
|
+
quality: Math.min(config.quality + 40, 90),
|
|
14134
|
+
// boost quality
|
|
14135
|
+
scale: Math.max(config.scale, 0.9)
|
|
14136
|
+
// don't downscale
|
|
14137
|
+
};
|
|
14138
|
+
}
|
|
14139
|
+
if (ratio < 0.6) {
|
|
14140
|
+
return {
|
|
14141
|
+
...config,
|
|
14142
|
+
quality: Math.min(config.quality + 20, 80),
|
|
14143
|
+
scale: Math.max(config.scale, 0.7)
|
|
14144
|
+
};
|
|
14145
|
+
}
|
|
14146
|
+
return config;
|
|
14147
|
+
}
|
|
14092
14148
|
var StreamStateManager = class {
|
|
14093
14149
|
currentState = "static";
|
|
14094
14150
|
isUserInteracting = false;
|
|
@@ -14097,9 +14153,11 @@ var StreamStateManager = class {
|
|
|
14097
14153
|
frameInterval = Infinity;
|
|
14098
14154
|
onStateChange = null;
|
|
14099
14155
|
staticTimer = null;
|
|
14100
|
-
USER_INTERACTION_TIMEOUT_MS =
|
|
14156
|
+
USER_INTERACTION_TIMEOUT_MS = 2e3;
|
|
14157
|
+
// 1s → 2s: linger in interacting longer
|
|
14101
14158
|
SCREEN_MOVING_THRESHOLD_MS = 1e3;
|
|
14102
|
-
STATIC_TIMEOUT_MS =
|
|
14159
|
+
STATIC_TIMEOUT_MS = 3e3;
|
|
14160
|
+
// 1.5s → 3s: don't rush to static
|
|
14103
14161
|
setStateChangeCallback(callback) {
|
|
14104
14162
|
this.onStateChange = callback;
|
|
14105
14163
|
}
|
|
@@ -14206,14 +14264,28 @@ var FrameProcessor = class {
|
|
|
14206
14264
|
if (!needsResize && !needsCrop && !needsReencode) {
|
|
14207
14265
|
return buffer;
|
|
14208
14266
|
}
|
|
14267
|
+
const sharp = await loadSharp();
|
|
14209
14268
|
let processed = sharp(buffer);
|
|
14210
14269
|
if (cropConfig) {
|
|
14211
|
-
|
|
14212
|
-
|
|
14213
|
-
|
|
14214
|
-
|
|
14215
|
-
|
|
14216
|
-
|
|
14270
|
+
const meta = await processed.metadata();
|
|
14271
|
+
const actualW = meta.width ?? viewportWidth ?? 1;
|
|
14272
|
+
const actualH = meta.height ?? viewportHeight ?? 1;
|
|
14273
|
+
const refW = viewportWidth ?? actualW;
|
|
14274
|
+
const refH = viewportHeight ?? actualH;
|
|
14275
|
+
const scaleX = actualW / refW;
|
|
14276
|
+
const scaleY = actualH / refH;
|
|
14277
|
+
const cropLeft = Math.round(cropConfig.x * scaleX);
|
|
14278
|
+
const cropTop = Math.round(cropConfig.y * scaleY);
|
|
14279
|
+
const cropW = Math.min(Math.round(cropConfig.width * scaleX), actualW - cropLeft);
|
|
14280
|
+
const cropH = Math.min(Math.round(cropConfig.height * scaleY), actualH - cropTop);
|
|
14281
|
+
if (cropW > 0 && cropH > 0 && cropLeft >= 0 && cropTop >= 0) {
|
|
14282
|
+
processed = processed.extract({
|
|
14283
|
+
left: cropLeft,
|
|
14284
|
+
top: cropTop,
|
|
14285
|
+
width: cropW,
|
|
14286
|
+
height: cropH
|
|
14287
|
+
});
|
|
14288
|
+
}
|
|
14217
14289
|
}
|
|
14218
14290
|
if (needsResize) {
|
|
14219
14291
|
const newWidth = Math.round((viewportWidth ?? 0) * config.scale);
|
|
@@ -14362,11 +14434,20 @@ var StreamCoordinator = class {
|
|
|
14362
14434
|
const crop = this.sessionCrops.get(sessionId);
|
|
14363
14435
|
const cropConfig = crop ? crop.box : void 0;
|
|
14364
14436
|
const effectiveViewport = crop ? { width: crop.box.width, height: crop.box.height } : frameViewport;
|
|
14365
|
-
const
|
|
14366
|
-
frameData,
|
|
14437
|
+
const adjustedConfig = adjustConfigForSize(
|
|
14367
14438
|
config,
|
|
14368
14439
|
effectiveViewport.width,
|
|
14369
14440
|
effectiveViewport.height,
|
|
14441
|
+
frameViewport.width,
|
|
14442
|
+
frameViewport.height
|
|
14443
|
+
);
|
|
14444
|
+
const processedBuffer = await this.frameProcessor.process(
|
|
14445
|
+
frameData,
|
|
14446
|
+
adjustedConfig,
|
|
14447
|
+
// Pass the ORIGINAL viewport (not cropped) so crop coordinates
|
|
14448
|
+
// can be scaled correctly from viewport space to frame pixel space.
|
|
14449
|
+
crop ? frameViewport.width : effectiveViewport.width,
|
|
14450
|
+
crop ? frameViewport.height : effectiveViewport.height,
|
|
14370
14451
|
cropConfig
|
|
14371
14452
|
);
|
|
14372
14453
|
const header = Buffer.from(JSON.stringify({
|
|
@@ -14538,7 +14619,18 @@ var ElementMonitor = class {
|
|
|
14538
14619
|
}
|
|
14539
14620
|
return results;
|
|
14540
14621
|
});
|
|
14541
|
-
const
|
|
14622
|
+
const TOLERANCE_PX = 10;
|
|
14623
|
+
const deduped = [];
|
|
14624
|
+
const isSameSize = (a, b) => Math.abs(a.width - b.width) <= TOLERANCE_PX && Math.abs(a.height - b.height) <= TOLERANCE_PX;
|
|
14625
|
+
const sorted = [...elements].sort((a, b) => a.rect.width * a.rect.height - b.rect.width * b.rect.height);
|
|
14626
|
+
for (const el of sorted) {
|
|
14627
|
+
const dupe = deduped.some(
|
|
14628
|
+
(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
|
|
14629
|
+
);
|
|
14630
|
+
if (!dupe) deduped.push(el);
|
|
14631
|
+
}
|
|
14632
|
+
deduped.sort((a, b) => b.rect.width * b.rect.height - a.rect.width * a.rect.height);
|
|
14633
|
+
const views = deduped.map((e, i) => ({
|
|
14542
14634
|
id: "el-" + i + "-" + (e.id || e.tag),
|
|
14543
14635
|
label: e.id || e.cls || e.tag,
|
|
14544
14636
|
rect: e.rect
|
|
@@ -14597,10 +14689,10 @@ var SessionManager = class extends EventEmitter {
|
|
|
14597
14689
|
this.screencasts.set(sessionId, {
|
|
14598
14690
|
capturer: new ScreencastCapturer({
|
|
14599
14691
|
interval: options?.interval ?? 100,
|
|
14600
|
-
quality: options?.quality ??
|
|
14692
|
+
quality: options?.quality ?? 40,
|
|
14601
14693
|
type: options?.type ?? "jpeg",
|
|
14602
|
-
width: options?.width ??
|
|
14603
|
-
height: options?.height ??
|
|
14694
|
+
width: options?.width ?? 1024,
|
|
14695
|
+
height: options?.height ?? 576
|
|
14604
14696
|
}),
|
|
14605
14697
|
page,
|
|
14606
14698
|
clientCount: 0
|
|
@@ -14741,6 +14833,8 @@ var SessionManager = class extends EventEmitter {
|
|
|
14741
14833
|
this.resetStaticSnapshotTimer(sessionId);
|
|
14742
14834
|
});
|
|
14743
14835
|
await sc.capturer.startCapture(sc.page, sessionId, onFrame);
|
|
14836
|
+
this.streamCoordinator.takeStaticSnapshot(sessionId, sc.page, sc.clientCount).catch(() => {
|
|
14837
|
+
});
|
|
14744
14838
|
}
|
|
14745
14839
|
async stopCapturer(sessionId) {
|
|
14746
14840
|
const sc = this.screencasts.get(sessionId);
|
|
@@ -15191,6 +15285,15 @@ var WSServer = class extends EventEmitter2 {
|
|
|
15191
15285
|
registerSession(sessionId, page, options) {
|
|
15192
15286
|
this.sessionManager.registerSession(sessionId, page, options);
|
|
15193
15287
|
}
|
|
15288
|
+
/** Restart screencast for a session — used by viewer reconnect button. */
|
|
15289
|
+
async reconnectSession(sessionId) {
|
|
15290
|
+
try {
|
|
15291
|
+
await this.sessionManager.stopCapturer(sessionId);
|
|
15292
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
15293
|
+
await this.sessionManager.startCapturer(sessionId);
|
|
15294
|
+
} catch {
|
|
15295
|
+
}
|
|
15296
|
+
}
|
|
15194
15297
|
unregisterSession(sessionId) {
|
|
15195
15298
|
this.sessionManager.unregisterSession(sessionId);
|
|
15196
15299
|
}
|
|
@@ -15213,7 +15316,7 @@ var WSServer = class extends EventEmitter2 {
|
|
|
15213
15316
|
});
|
|
15214
15317
|
}
|
|
15215
15318
|
this.sessionManager.incrementClientCount(sessionId);
|
|
15216
|
-
if (this.streamCoordinator.getLastFrameViewport()
|
|
15319
|
+
if (this.streamCoordinator.getLastFrameViewport()) {
|
|
15217
15320
|
this.streamCoordinator.replayLastFrame(sessionId).catch(() => {
|
|
15218
15321
|
});
|
|
15219
15322
|
}
|
|
@@ -15223,7 +15326,6 @@ var WSServer = class extends EventEmitter2 {
|
|
|
15223
15326
|
if (count === 0 && this.sessionManager.isCapturerActive(sessionId)) {
|
|
15224
15327
|
this.sessionManager.stopCapturer(sessionId).then(() => {
|
|
15225
15328
|
this.streamCoordinator.resetFrameRate();
|
|
15226
|
-
this.streamCoordinator.clearLastFrame();
|
|
15227
15329
|
this.emit("screencast-stopped", sessionId);
|
|
15228
15330
|
}).catch(() => {
|
|
15229
15331
|
this.emit("screencast-stopped", sessionId);
|
|
@@ -15399,6 +15501,31 @@ var WSServer = class extends EventEmitter2 {
|
|
|
15399
15501
|
this.emit("human-solved", { sessionId: sessionId ?? null, clientId });
|
|
15400
15502
|
return;
|
|
15401
15503
|
}
|
|
15504
|
+
if (msg.type === "reconnect") {
|
|
15505
|
+
this.emit("reconnect-request", { sessionId: sessionId ?? null, clientId });
|
|
15506
|
+
return;
|
|
15507
|
+
}
|
|
15508
|
+
if (msg.type === "health_ping") {
|
|
15509
|
+
this.sendToClient(clientId, { type: "health_pong", ts: msg.ts });
|
|
15510
|
+
return;
|
|
15511
|
+
}
|
|
15512
|
+
if (msg.type === "snapshot_request") {
|
|
15513
|
+
const page2 = sessionId ? this.sessionManager.getPageForSession(sessionId) : void 0;
|
|
15514
|
+
if (!page2) {
|
|
15515
|
+
this.sendToClient(clientId, { type: "snapshot_result", data: null, error: "no page" });
|
|
15516
|
+
return;
|
|
15517
|
+
}
|
|
15518
|
+
const fmt = msg.format === "webp" ? "webp" : "png";
|
|
15519
|
+
page2.screenshot({ type: fmt, quality: fmt === "webp" ? msg.quality ?? 90 : void 0 }).then((buf) => {
|
|
15520
|
+
this.sendToClient(clientId, {
|
|
15521
|
+
type: "snapshot_result",
|
|
15522
|
+
data: { data: buf.toString("base64"), format: fmt }
|
|
15523
|
+
});
|
|
15524
|
+
}).catch(() => {
|
|
15525
|
+
this.sendToClient(clientId, { type: "snapshot_result", data: null, error: "screenshot failed" });
|
|
15526
|
+
});
|
|
15527
|
+
return;
|
|
15528
|
+
}
|
|
15402
15529
|
const ctx = {
|
|
15403
15530
|
clientId,
|
|
15404
15531
|
sessionId,
|
|
@@ -16543,7 +16670,7 @@ var DataCollector = class {
|
|
|
16543
16670
|
return results;
|
|
16544
16671
|
}
|
|
16545
16672
|
async createBrowserContext() {
|
|
16546
|
-
const { launch } = await import("./cdp-driver-
|
|
16673
|
+
const { launch } = await import("./cdp-driver-J3YQ4LJV.js");
|
|
16547
16674
|
const { browser } = await launch({
|
|
16548
16675
|
headless: true,
|
|
16549
16676
|
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
|
+
};
|