@xbrowser/cli 1.9.8 → 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.
Files changed (39) hide show
  1. package/dist/{anti-bot-MCVM6IFB.js → anti-bot-DR56Y63V.js} +1 -1
  2. package/dist/{browser-GT4BMX6X.js → browser-FFYPFTBV.js} +1 -1
  3. package/dist/{browser-43YXUIML.js → browser-RHWUAMTB.js} +2 -2
  4. package/dist/{browser-SJWPJFN7.js → browser-YCO4GJMZ.js} +2 -2
  5. package/dist/{cdp-driver-WAFYSIIW.js → cdp-driver-2T4P4ZE2.js} +87 -1
  6. package/dist/{cdp-driver-PUZLDSQU.js → cdp-driver-J3YQ4LJV.js} +1 -1
  7. package/dist/chunk-3OD76SUE.js +20 -0
  8. package/dist/chunk-43TEMG4U.js +9 -0
  9. package/dist/{chunk-C3LIGO4V.js → chunk-4452SPFI.js} +906 -16
  10. package/dist/{chunk-VEWTHYDG.js → chunk-5UGR6MUK.js} +906 -16
  11. package/dist/{chunk-ENCEMM7Z.js → chunk-6OZWKXSW.js} +15 -16
  12. package/dist/{chunk-7ZDWM6T2.js → chunk-BNO7OKO4.js} +5 -6
  13. package/dist/{chunk-24SY6PE5.js → chunk-CG3D3CHY.js} +1 -8
  14. package/dist/{chunk-ZJHQPMCY.js → chunk-DWDXEGVK.js} +5 -6
  15. package/dist/{chunk-A4YHJW3Z.js → chunk-GUTBIYFW.js} +101 -16
  16. package/dist/{chunk-DPJNCMLC.js → chunk-INTQPBYF.js} +7 -0
  17. package/dist/{human-interaction-OU7LZ6OZ.js → chunk-MWFHZUIY.js} +6 -7
  18. package/dist/chunk-Q2DFJQGS.js +128 -0
  19. package/dist/{chunk-GM3HWQ5U.js → chunk-RRBXV7KE.js} +87 -1
  20. package/dist/{chunk-PBDID7SE.js → chunk-RZM5CNIY.js} +15 -16
  21. package/dist/{human-interaction-HJTXFVQS.js → chunk-SQHSZENE.js} +2 -2
  22. package/dist/chunk-TZPKFUBT.js +20 -0
  23. package/dist/{chunk-YAUG3NSI.js → chunk-YMUSHPU4.js} +16 -128
  24. package/dist/cli.js +41 -28
  25. package/dist/{daemon-client-4HJENICO.js → daemon-client-GKEPT4NY.js} +15 -3
  26. package/dist/{daemon-client-CMRAWXTN.js → daemon-client-O6BYVRXV.js} +4 -1
  27. package/dist/daemon-main.js +635 -57
  28. package/dist/human-interaction-5AO42MBA.js +8 -0
  29. package/dist/{human-interaction-2DZK4MW7.js → human-interaction-C5OEGXGO.js} +1 -1
  30. package/dist/human-interaction-ISXZTAYY.js +8 -0
  31. package/dist/index.d.ts +167 -2
  32. package/dist/index.js +173 -63
  33. package/dist/recovery-EF33LKRJ.js +77 -0
  34. package/dist/recovery-J2ISVGUL.js +77 -0
  35. package/dist/recovery-ZJVVHP7N.js +76 -0
  36. package/dist/{session-recorder-K3K63Q33.js → session-recorder-H3KEYU26.js} +1 -1
  37. package/dist/{session-recorder-AG6CH6EI.js → session-recorder-QRZMKFVL.js} +1 -1
  38. package/dist/{session-replayer-V4MP6M6I.js → session-replayer-LJUC4TI7.js} +24 -7
  39. 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-DPJNCMLC.js";
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
- version
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-C3LIGO4V.js";
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-PBDID7SE.js";
90
- import "./chunk-GM3HWQ5U.js";
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-2DZK4MW7.js");
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-MCVM6IFB.js");
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-SJWPJFN7.js");
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-CMRAWXTN.js");
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-CMRAWXTN.js");
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-AG6CH6EI.js");
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.name || process.env.XBROWSER_SESSION || "default";
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 { forwardExec } = await import("./daemon-client-CMRAWXTN.js");
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 forwardExec(`${command}.${subCommand}`, params, sessionName, cdpEndpoint, userTimeout);
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-SJWPJFN7.js");
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
- user_interacting: { format: "jpeg", quality: 85, maxFps: 60, scale: 0.8 },
14089
- screen_moving: { format: "jpeg", quality: 85, maxFps: 8, scale: 0.8 },
14090
- static: { format: "jpeg", quality: 95, maxFps: 2, scale: 1 }
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 = 1e3;
14140
+ USER_INTERACTION_TIMEOUT_MS = 2e3;
14141
+ // 1s → 2s: linger in interacting longer
14101
14142
  SCREEN_MOVING_THRESHOLD_MS = 1e3;
14102
- STATIC_TIMEOUT_MS = 1500;
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
- processed = processed.extract({
14212
- left: Math.round(cropConfig.x),
14213
- top: Math.round(cropConfig.y),
14214
- width: Math.round(cropConfig.width),
14215
- height: Math.round(cropConfig.height)
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 processedBuffer = await this.frameProcessor.process(
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 views = elements.map((e, i) => ({
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 ?? 80,
14675
+ quality: options?.quality ?? 40,
14601
14676
  type: options?.type ?? "jpeg",
14602
- width: options?.width ?? 1920,
14603
- height: options?.height ?? 1080
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() && this.sessionManager.isCapturerActive(sessionId)) {
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-PUZLDSQU.js");
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
+ };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  SessionRecorder
3
- } from "./chunk-VEWTHYDG.js";
3
+ } from "./chunk-5UGR6MUK.js";
4
4
  import "./chunk-KFQGP6VL.js";
5
5
  export {
6
6
  SessionRecorder
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  SessionRecorder
3
- } from "./chunk-C3LIGO4V.js";
3
+ } from "./chunk-4452SPFI.js";
4
4
  import "./chunk-OZKD3W4X.js";
5
5
  import "./chunk-KFQGP6VL.js";
6
6
  export {