@xbrowser/cli 1.16.0 → 1.17.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.
@@ -20,8 +20,8 @@ import {
20
20
  saveSessionDiskMeta,
21
21
  setActivePage,
22
22
  touchSession
23
- } from "./chunk-7V3B7VPX.js";
24
- import "./chunk-A5HJ6IRE.js";
23
+ } from "./chunk-CE3L6HYV.js";
24
+ import "./chunk-3KUJOXUE.js";
25
25
  import "./chunk-TNEN6VQ2.js";
26
26
  import "./chunk-GDKLH7ZY.js";
27
27
  import "./chunk-A6LPGFAL.js";
@@ -20,7 +20,7 @@ import {
20
20
  saveSessionDiskMeta,
21
21
  setActivePage,
22
22
  touchSession
23
- } from "./chunk-5EYOEB4R.js";
23
+ } from "./chunk-3LVR44KG.js";
24
24
  import "./chunk-TNEN6VQ2.js";
25
25
  import "./chunk-GDKLH7ZY.js";
26
26
  import "./chunk-KFQGP6VL.js";
@@ -20,8 +20,8 @@ import {
20
20
  saveSessionDiskMeta,
21
21
  setActivePage,
22
22
  touchSession
23
- } from "./chunk-GWQ5NTVE.js";
24
- import "./chunk-LTBNQERK.js";
23
+ } from "./chunk-UQUJVBXE.js";
24
+ import "./chunk-W4JETHAD.js";
25
25
  import "./chunk-3FWLW7FS.js";
26
26
  import "./chunk-TNEN6VQ2.js";
27
27
  import "./chunk-GDKLH7ZY.js";
@@ -14,7 +14,7 @@ import {
14
14
  scrollIntoView,
15
15
  waitForActionable,
16
16
  waitForNetworkIdle
17
- } from "./chunk-LTBNQERK.js";
17
+ } from "./chunk-W4JETHAD.js";
18
18
  import "./chunk-3FWLW7FS.js";
19
19
  import {
20
20
  connectToCDP,
@@ -14,7 +14,7 @@ import {
14
14
  scrollIntoView,
15
15
  waitForActionable,
16
16
  waitForNetworkIdle
17
- } from "./chunk-A5HJ6IRE.js";
17
+ } from "./chunk-3KUJOXUE.js";
18
18
  import {
19
19
  connectToCDP,
20
20
  findChrome,
@@ -515,6 +515,49 @@ var XBMouseImpl = class {
515
515
  this._x = x;
516
516
  this._y = y;
517
517
  }
518
+ /**
519
+ * Drag from the CURRENT cursor position to (x, y): press, traverse a
520
+ * bezier trajectory with the button held, release. Drives the REAL
521
+ * HTML5 DnD pipeline — field-verified (d59): this sequence fires
522
+ * dragstart → dragover… → drop → dragend, all isTrusted=true. No
523
+ * Input.dispatchDragEvent needed.
524
+ */
525
+ async drag(x, y, opts = {}) {
526
+ const stealth = process.env.XBROWSER_STEALTH !== "off";
527
+ await this.send("Input.dispatchMouseEvent", {
528
+ type: "mousePressed",
529
+ x: this._x,
530
+ y: this._y,
531
+ button: "left",
532
+ clickCount: 1
533
+ });
534
+ this._button = "left";
535
+ const steps = opts.steps ?? Math.max(10, Math.min(24, Math.round(Math.hypot(x - this._x, y - this._y) / 20)));
536
+ const fx = this._x, fy = this._y;
537
+ for (let i = 1; i <= steps; i++) {
538
+ if (stealth) await sleep(rand(16, 28));
539
+ const t = i / steps;
540
+ this._x = fx + (x - fx) * t;
541
+ this._y = fy + (y - fy) * t;
542
+ await this.send("Input.dispatchMouseEvent", {
543
+ type: "mouseMoved",
544
+ x: this._x,
545
+ y: this._y,
546
+ button: this._button
547
+ });
548
+ }
549
+ this._x = x;
550
+ this._y = y;
551
+ await sleep(rand(60, 140));
552
+ await this.send("Input.dispatchMouseEvent", {
553
+ type: "mouseReleased",
554
+ x,
555
+ y,
556
+ button: "left",
557
+ clickCount: 1
558
+ });
559
+ this._button = "none";
560
+ }
518
561
  async wheel(deltaX, deltaY) {
519
562
  await this.send("Input.dispatchMouseEvent", {
520
563
  type: "mouseWheel",
@@ -1255,6 +1298,21 @@ var XBLocatorImpl = class _XBLocatorImpl {
1255
1298
  `);
1256
1299
  return selected;
1257
1300
  }
1301
+ async dragAndDrop(source, target) {
1302
+ const boxes = await this.page.evaluate(`
1303
+ (function() {
1304
+ const s = ${this._q(source)}, t = ${this._q(target)};
1305
+ if (!s || !t) return null;
1306
+ const c = (el) => { const r = el.getBoundingClientRect(); return { x: r.x + r.width / 2, y: r.y + r.height / 2 }; };
1307
+ return [c(s), c(t)];
1308
+ })()
1309
+ `);
1310
+ if (!boxes || !boxes[0] || !boxes[1]) {
1311
+ throw new Error(`dragAndDrop: element not found (${source} / ${target})`);
1312
+ }
1313
+ await this.page.mouse.move(boxes[0].x, boxes[0].y);
1314
+ await this.page.mouse.drag(boxes[1].x, boxes[1].y);
1315
+ }
1258
1316
  async screenshot(opts = {}) {
1259
1317
  await waitForActionable(this.page, this.selector);
1260
1318
  const box = await this.page.evaluate(`
@@ -525,6 +525,49 @@ var XBMouseImpl = class {
525
525
  this._x = x;
526
526
  this._y = y;
527
527
  }
528
+ /**
529
+ * Drag from the CURRENT cursor position to (x, y): press, traverse a
530
+ * bezier trajectory with the button held, release. Drives the REAL
531
+ * HTML5 DnD pipeline — field-verified (d59): this sequence fires
532
+ * dragstart → dragover… → drop → dragend, all isTrusted=true. No
533
+ * Input.dispatchDragEvent needed.
534
+ */
535
+ async drag(x, y, opts = {}) {
536
+ const stealth = process.env.XBROWSER_STEALTH !== "off";
537
+ await this.send("Input.dispatchMouseEvent", {
538
+ type: "mousePressed",
539
+ x: this._x,
540
+ y: this._y,
541
+ button: "left",
542
+ clickCount: 1
543
+ });
544
+ this._button = "left";
545
+ const steps = opts.steps ?? Math.max(10, Math.min(24, Math.round(Math.hypot(x - this._x, y - this._y) / 20)));
546
+ const fx = this._x, fy = this._y;
547
+ for (let i = 1; i <= steps; i++) {
548
+ if (stealth) await sleep2(rand(16, 28));
549
+ const t = i / steps;
550
+ this._x = fx + (x - fx) * t;
551
+ this._y = fy + (y - fy) * t;
552
+ await this.send("Input.dispatchMouseEvent", {
553
+ type: "mouseMoved",
554
+ x: this._x,
555
+ y: this._y,
556
+ button: this._button
557
+ });
558
+ }
559
+ this._x = x;
560
+ this._y = y;
561
+ await sleep2(rand(60, 140));
562
+ await this.send("Input.dispatchMouseEvent", {
563
+ type: "mouseReleased",
564
+ x,
565
+ y,
566
+ button: "left",
567
+ clickCount: 1
568
+ });
569
+ this._button = "none";
570
+ }
528
571
  async wheel(deltaX, deltaY) {
529
572
  await this.send("Input.dispatchMouseEvent", {
530
573
  type: "mouseWheel",
@@ -1265,6 +1308,21 @@ var XBLocatorImpl = class _XBLocatorImpl {
1265
1308
  `);
1266
1309
  return selected;
1267
1310
  }
1311
+ async dragAndDrop(source, target) {
1312
+ const boxes = await this.page.evaluate(`
1313
+ (function() {
1314
+ const s = ${this._q(source)}, t = ${this._q(target)};
1315
+ if (!s || !t) return null;
1316
+ const c = (el) => { const r = el.getBoundingClientRect(); return { x: r.x + r.width / 2, y: r.y + r.height / 2 }; };
1317
+ return [c(s), c(t)];
1318
+ })()
1319
+ `);
1320
+ if (!boxes || !boxes[0] || !boxes[1]) {
1321
+ throw new Error(`dragAndDrop: element not found (${source} / ${target})`);
1322
+ }
1323
+ await this.page.mouse.move(boxes[0].x, boxes[0].y);
1324
+ await this.page.mouse.drag(boxes[1].x, boxes[1].y);
1325
+ }
1268
1326
  async screenshot(opts = {}) {
1269
1327
  await waitForActionable(this.page, this.selector);
1270
1328
  const box = await this.page.evaluate(`
@@ -528,6 +528,49 @@ var XBMouseImpl = class {
528
528
  this._x = x;
529
529
  this._y = y;
530
530
  }
531
+ /**
532
+ * Drag from the CURRENT cursor position to (x, y): press, traverse a
533
+ * bezier trajectory with the button held, release. Drives the REAL
534
+ * HTML5 DnD pipeline — field-verified (d59): this sequence fires
535
+ * dragstart → dragover… → drop → dragend, all isTrusted=true. No
536
+ * Input.dispatchDragEvent needed.
537
+ */
538
+ async drag(x, y, opts = {}) {
539
+ const stealth = process.env.XBROWSER_STEALTH !== "off";
540
+ await this.send("Input.dispatchMouseEvent", {
541
+ type: "mousePressed",
542
+ x: this._x,
543
+ y: this._y,
544
+ button: "left",
545
+ clickCount: 1
546
+ });
547
+ this._button = "left";
548
+ const steps = opts.steps ?? Math.max(10, Math.min(24, Math.round(Math.hypot(x - this._x, y - this._y) / 20)));
549
+ const fx = this._x, fy = this._y;
550
+ for (let i = 1; i <= steps; i++) {
551
+ if (stealth) await sleep2(rand(16, 28));
552
+ const t = i / steps;
553
+ this._x = fx + (x - fx) * t;
554
+ this._y = fy + (y - fy) * t;
555
+ await this.send("Input.dispatchMouseEvent", {
556
+ type: "mouseMoved",
557
+ x: this._x,
558
+ y: this._y,
559
+ button: this._button
560
+ });
561
+ }
562
+ this._x = x;
563
+ this._y = y;
564
+ await sleep2(rand(60, 140));
565
+ await this.send("Input.dispatchMouseEvent", {
566
+ type: "mouseReleased",
567
+ x,
568
+ y,
569
+ button: "left",
570
+ clickCount: 1
571
+ });
572
+ this._button = "none";
573
+ }
531
574
  async wheel(deltaX, deltaY) {
532
575
  await this.send("Input.dispatchMouseEvent", {
533
576
  type: "mouseWheel",
@@ -1268,6 +1311,21 @@ var XBLocatorImpl = class _XBLocatorImpl {
1268
1311
  `);
1269
1312
  return selected;
1270
1313
  }
1314
+ async dragAndDrop(source, target) {
1315
+ const boxes = await this.page.evaluate(`
1316
+ (function() {
1317
+ const s = ${this._q(source)}, t = ${this._q(target)};
1318
+ if (!s || !t) return null;
1319
+ const c = (el) => { const r = el.getBoundingClientRect(); return { x: r.x + r.width / 2, y: r.y + r.height / 2 }; };
1320
+ return [c(s), c(t)];
1321
+ })()
1322
+ `);
1323
+ if (!boxes || !boxes[0] || !boxes[1]) {
1324
+ throw new Error(`dragAndDrop: element not found (${source} / ${target})`);
1325
+ }
1326
+ await this.page.mouse.move(boxes[0].x, boxes[0].y);
1327
+ await this.page.mouse.drag(boxes[1].x, boxes[1].y);
1328
+ }
1271
1329
  async screenshot(opts = {}) {
1272
1330
  await waitForActionable(this.page, this.selector);
1273
1331
  const box = await this.page.evaluate(`
@@ -5873,16 +5931,40 @@ async function createSession(name, url, options) {
5873
5931
  (t) => t.url && t.url !== "about:blank" && !t.url.startsWith("chrome://") && !t.url.startsWith("chrome-untrusted://") && !t.url.startsWith("chrome-error://") && (url ? t.url.includes(new URL(url).hostname) : true)
5874
5932
  );
5875
5933
  if (matchTarget && matchTarget.url) {
5876
- targetPage = await context.newPage();
5877
- await targetPage.goto(matchTarget.url, { waitUntil: "domcontentloaded", timeout: 15e3 }).catch(() => {
5934
+ const existing2 = context.pages().find((p) => {
5935
+ try {
5936
+ return p.url() === matchTarget.url;
5937
+ } catch {
5938
+ return false;
5939
+ }
5878
5940
  });
5941
+ if (existing2) {
5942
+ targetPage = existing2;
5943
+ } else {
5944
+ targetPage = await context.newPage();
5945
+ await targetPage.goto(matchTarget.url, { waitUntil: "domcontentloaded", timeout: 15e3 }).catch(() => {
5946
+ });
5947
+ }
5879
5948
  }
5880
5949
  }
5881
5950
  if (!targetPage) {
5882
5951
  const pages = context.pages();
5883
- if (pages.length > 0) {
5952
+ if (pages.length > 1) {
5953
+ try {
5954
+ for (const p of pages) {
5955
+ const vis = await p.evaluate("document.visibilityState").catch(() => "hidden");
5956
+ if (vis === "visible") {
5957
+ targetPage = p;
5958
+ break;
5959
+ }
5960
+ }
5961
+ } catch {
5962
+ }
5963
+ }
5964
+ if (!targetPage && pages.length > 0) {
5884
5965
  targetPage = pages[0];
5885
- } else {
5966
+ }
5967
+ if (!targetPage) {
5886
5968
  targetPage = await context.newPage();
5887
5969
  }
5888
5970
  }
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  launch
3
- } from "./chunk-A5HJ6IRE.js";
3
+ } from "./chunk-3KUJOXUE.js";
4
4
  import {
5
5
  errMsg
6
6
  } from "./chunk-GDKLH7ZY.js";
@@ -639,16 +639,40 @@ async function createSession(name, url, options) {
639
639
  (t) => t.url && t.url !== "about:blank" && !t.url.startsWith("chrome://") && !t.url.startsWith("chrome-untrusted://") && !t.url.startsWith("chrome-error://") && (url ? t.url.includes(new URL(url).hostname) : true)
640
640
  );
641
641
  if (matchTarget && matchTarget.url) {
642
- targetPage = await context.newPage();
643
- await targetPage.goto(matchTarget.url, { waitUntil: "domcontentloaded", timeout: 15e3 }).catch(() => {
642
+ const existing2 = context.pages().find((p) => {
643
+ try {
644
+ return p.url() === matchTarget.url;
645
+ } catch {
646
+ return false;
647
+ }
644
648
  });
649
+ if (existing2) {
650
+ targetPage = existing2;
651
+ } else {
652
+ targetPage = await context.newPage();
653
+ await targetPage.goto(matchTarget.url, { waitUntil: "domcontentloaded", timeout: 15e3 }).catch(() => {
654
+ });
655
+ }
645
656
  }
646
657
  }
647
658
  if (!targetPage) {
648
659
  const pages = context.pages();
649
- if (pages.length > 0) {
660
+ if (pages.length > 1) {
661
+ try {
662
+ for (const p of pages) {
663
+ const vis = await p.evaluate("document.visibilityState").catch(() => "hidden");
664
+ if (vis === "visible") {
665
+ targetPage = p;
666
+ break;
667
+ }
668
+ }
669
+ } catch {
670
+ }
671
+ }
672
+ if (!targetPage && pages.length > 0) {
650
673
  targetPage = pages[0];
651
- } else {
674
+ }
675
+ if (!targetPage) {
652
676
  targetPage = await context.newPage();
653
677
  }
654
678
  }
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  createRuleEngine,
3
3
  launch
4
- } from "./chunk-LTBNQERK.js";
4
+ } from "./chunk-W4JETHAD.js";
5
5
  import {
6
6
  errMsg
7
7
  } from "./chunk-GDKLH7ZY.js";
@@ -1066,16 +1066,40 @@ async function createSession(name, url, options) {
1066
1066
  (t) => t.url && t.url !== "about:blank" && !t.url.startsWith("chrome://") && !t.url.startsWith("chrome-untrusted://") && !t.url.startsWith("chrome-error://") && (url ? t.url.includes(new URL(url).hostname) : true)
1067
1067
  );
1068
1068
  if (matchTarget && matchTarget.url) {
1069
- targetPage = await context.newPage();
1070
- await targetPage.goto(matchTarget.url, { waitUntil: "domcontentloaded", timeout: 15e3 }).catch(() => {
1069
+ const existing2 = context.pages().find((p) => {
1070
+ try {
1071
+ return p.url() === matchTarget.url;
1072
+ } catch {
1073
+ return false;
1074
+ }
1071
1075
  });
1076
+ if (existing2) {
1077
+ targetPage = existing2;
1078
+ } else {
1079
+ targetPage = await context.newPage();
1080
+ await targetPage.goto(matchTarget.url, { waitUntil: "domcontentloaded", timeout: 15e3 }).catch(() => {
1081
+ });
1082
+ }
1072
1083
  }
1073
1084
  }
1074
1085
  if (!targetPage) {
1075
1086
  const pages = context.pages();
1076
- if (pages.length > 0) {
1087
+ if (pages.length > 1) {
1088
+ try {
1089
+ for (const p of pages) {
1090
+ const vis = await p.evaluate("document.visibilityState").catch(() => "hidden");
1091
+ if (vis === "visible") {
1092
+ targetPage = p;
1093
+ break;
1094
+ }
1095
+ }
1096
+ } catch {
1097
+ }
1098
+ }
1099
+ if (!targetPage && pages.length > 0) {
1077
1100
  targetPage = pages[0];
1078
- } else {
1101
+ }
1102
+ if (!targetPage) {
1079
1103
  targetPage = await context.newPage();
1080
1104
  }
1081
1105
  }
@@ -526,6 +526,49 @@ var XBMouseImpl = class {
526
526
  this._x = x;
527
527
  this._y = y;
528
528
  }
529
+ /**
530
+ * Drag from the CURRENT cursor position to (x, y): press, traverse a
531
+ * bezier trajectory with the button held, release. Drives the REAL
532
+ * HTML5 DnD pipeline — field-verified (d59): this sequence fires
533
+ * dragstart → dragover… → drop → dragend, all isTrusted=true. No
534
+ * Input.dispatchDragEvent needed.
535
+ */
536
+ async drag(x, y, opts = {}) {
537
+ const stealth = process.env.XBROWSER_STEALTH !== "off";
538
+ await this.send("Input.dispatchMouseEvent", {
539
+ type: "mousePressed",
540
+ x: this._x,
541
+ y: this._y,
542
+ button: "left",
543
+ clickCount: 1
544
+ });
545
+ this._button = "left";
546
+ const steps = opts.steps ?? Math.max(10, Math.min(24, Math.round(Math.hypot(x - this._x, y - this._y) / 20)));
547
+ const fx = this._x, fy = this._y;
548
+ for (let i = 1; i <= steps; i++) {
549
+ if (stealth) await sleep2(rand(16, 28));
550
+ const t = i / steps;
551
+ this._x = fx + (x - fx) * t;
552
+ this._y = fy + (y - fy) * t;
553
+ await this.send("Input.dispatchMouseEvent", {
554
+ type: "mouseMoved",
555
+ x: this._x,
556
+ y: this._y,
557
+ button: this._button
558
+ });
559
+ }
560
+ this._x = x;
561
+ this._y = y;
562
+ await sleep2(rand(60, 140));
563
+ await this.send("Input.dispatchMouseEvent", {
564
+ type: "mouseReleased",
565
+ x,
566
+ y,
567
+ button: "left",
568
+ clickCount: 1
569
+ });
570
+ this._button = "none";
571
+ }
529
572
  async wheel(deltaX, deltaY) {
530
573
  await this.send("Input.dispatchMouseEvent", {
531
574
  type: "mouseWheel",
@@ -1164,6 +1207,21 @@ var XBLocatorImpl = class _XBLocatorImpl {
1164
1207
  `);
1165
1208
  return selected;
1166
1209
  }
1210
+ async dragAndDrop(source, target) {
1211
+ const boxes = await this.page.evaluate(`
1212
+ (function() {
1213
+ const s = ${this._q(source)}, t = ${this._q(target)};
1214
+ if (!s || !t) return null;
1215
+ const c = (el) => { const r = el.getBoundingClientRect(); return { x: r.x + r.width / 2, y: r.y + r.height / 2 }; };
1216
+ return [c(s), c(t)];
1217
+ })()
1218
+ `);
1219
+ if (!boxes || !boxes[0] || !boxes[1]) {
1220
+ throw new Error(`dragAndDrop: element not found (${source} / ${target})`);
1221
+ }
1222
+ await this.page.mouse.move(boxes[0].x, boxes[0].y);
1223
+ await this.page.mouse.drag(boxes[1].x, boxes[1].y);
1224
+ }
1167
1225
  async screenshot(opts = {}) {
1168
1226
  await waitForActionable(this.page, this.selector);
1169
1227
  const box = await this.page.evaluate(`
package/dist/cli.js CHANGED
@@ -72,7 +72,7 @@ import {
72
72
  setActivePage,
73
73
  sleep,
74
74
  wheelDelta
75
- } from "./chunk-5EYOEB4R.js";
75
+ } from "./chunk-3LVR44KG.js";
76
76
  import "./chunk-TNEN6VQ2.js";
77
77
  import {
78
78
  errMsg
@@ -947,14 +947,14 @@ var mouseCommand = registerCommand({
947
947
  description: "Control the mouse (move, click, etc.)",
948
948
  scope: "page",
949
949
  parameters: z6.object({
950
- action: z6.enum(["move", "down", "up", "click", "dblclick"]),
950
+ action: z6.enum(["move", "down", "up", "click", "dblclick", "drag"]),
951
951
  x: z6.coerce.number(),
952
952
  y: z6.coerce.number(),
953
953
  button: z6.enum(["left", "right", "middle"]).optional(),
954
954
  steps: z6.coerce.number().optional()
955
955
  }),
956
956
  result: z6.object({
957
- action: z6.enum(["move", "down", "up", "click", "dblclick"]),
957
+ action: z6.enum(["move", "down", "up", "click", "dblclick", "drag"]),
958
958
  x: z6.number(),
959
959
  y: z6.number()
960
960
  }),
@@ -978,6 +978,9 @@ var mouseCommand = registerCommand({
978
978
  case "dblclick":
979
979
  await ctx.page.mouse.dblclick(p.x, p.y, { button });
980
980
  break;
981
+ case "drag":
982
+ await ctx.page.mouse.drag(p.x, p.y, p.steps ? { steps: p.steps } : {});
983
+ break;
981
984
  }
982
985
  return ok6({ action: p.action, x: p.x, y: p.y });
983
986
  }
@@ -7542,7 +7545,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
7542
7545
  }
7543
7546
  let targetPageOverride = null;
7544
7547
  if (_target && extraOpts?.cdpEndpoint) {
7545
- const { findTargetPage } = await import("./browser-KT4FOWBO.js");
7548
+ const { findTargetPage } = await import("./browser-HY7UXUQV.js");
7546
7549
  targetPageOverride = await findTargetPage(extraOpts.cdpEndpoint, _target);
7547
7550
  if (!targetPageOverride) {
7548
7551
  return errorResult(`Target "${_target}" not found. Use 'xbrowser targets --cdp ${extraOpts.cdpEndpoint}' to list available pages.`);
@@ -13717,7 +13720,7 @@ Run "xbrowser ${command} ${subCommand} --help" to see available parameters.`
13717
13720
  const targetPage = pages[cmdTabIndex];
13718
13721
  await targetPage.bringToFront().catch(() => {
13719
13722
  });
13720
- const { setActivePage: setActivePage2 } = await import("./browser-KT4FOWBO.js");
13723
+ const { setActivePage: setActivePage2 } = await import("./browser-HY7UXUQV.js");
13721
13724
  setActivePage2(session, targetPage);
13722
13725
  }
13723
13726
  }
@@ -13993,7 +13996,7 @@ async function main() {
13993
13996
  const command = process.argv[2];
13994
13997
  const isLongRunning = command === "preview" || command === "serve";
13995
13998
  if (!isLongRunning) {
13996
- const { ensureProcessCanExit } = await import("./browser-KT4FOWBO.js");
13999
+ const { ensureProcessCanExit } = await import("./browser-HY7UXUQV.js");
13997
14000
  await ensureProcessCanExit().catch(() => {
13998
14001
  });
13999
14002
  process.exit(process.exitCode || exitCode);
@@ -34,13 +34,13 @@ import {
34
34
  resolveLaunchOpts,
35
35
  saveSessionDiskMeta,
36
36
  setActivePage
37
- } from "./chunk-GWQ5NTVE.js";
37
+ } from "./chunk-UQUJVBXE.js";
38
38
  import {
39
39
  createRuleEngine,
40
40
  rand,
41
41
  sleep,
42
42
  wheelDelta
43
- } from "./chunk-LTBNQERK.js";
43
+ } from "./chunk-W4JETHAD.js";
44
44
  import {
45
45
  queryJS
46
46
  } from "./chunk-3FWLW7FS.js";
@@ -907,14 +907,14 @@ var mouseCommand = registerCommand({
907
907
  description: "Control the mouse (move, click, etc.)",
908
908
  scope: "page",
909
909
  parameters: z6.object({
910
- action: z6.enum(["move", "down", "up", "click", "dblclick"]),
910
+ action: z6.enum(["move", "down", "up", "click", "dblclick", "drag"]),
911
911
  x: z6.coerce.number(),
912
912
  y: z6.coerce.number(),
913
913
  button: z6.enum(["left", "right", "middle"]).optional(),
914
914
  steps: z6.coerce.number().optional()
915
915
  }),
916
916
  result: z6.object({
917
- action: z6.enum(["move", "down", "up", "click", "dblclick"]),
917
+ action: z6.enum(["move", "down", "up", "click", "dblclick", "drag"]),
918
918
  x: z6.number(),
919
919
  y: z6.number()
920
920
  }),
@@ -938,6 +938,9 @@ var mouseCommand = registerCommand({
938
938
  case "dblclick":
939
939
  await ctx.page.mouse.dblclick(p.x, p.y, { button });
940
940
  break;
941
+ case "drag":
942
+ await ctx.page.mouse.drag(p.x, p.y, p.steps ? { steps: p.steps } : {});
943
+ break;
941
944
  }
942
945
  return ok6({ action: p.action, x: p.x, y: p.y });
943
946
  }
@@ -7060,7 +7063,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
7060
7063
  }
7061
7064
  let targetPageOverride = null;
7062
7065
  if (_target && extraOpts?.cdpEndpoint) {
7063
- const { findTargetPage } = await import("./browser-AOVQTGJ7.js");
7066
+ const { findTargetPage } = await import("./browser-ZVNDI4TR.js");
7064
7067
  targetPageOverride = await findTargetPage(extraOpts.cdpEndpoint, _target);
7065
7068
  if (!targetPageOverride) {
7066
7069
  return errorResult(`Target "${_target}" not found. Use 'xbrowser targets --cdp ${extraOpts.cdpEndpoint}' to list available pages.`);
@@ -8879,7 +8882,7 @@ function createRPCHandler() {
8879
8882
  if (isNewFormat) {
8880
8883
  try {
8881
8884
  const replayErrors = [];
8882
- const { SessionReplayer } = await import("./session-replayer-MJH5W7BB.js");
8885
+ const { SessionReplayer } = await import("./session-replayer-NV65I6OQ.js");
8883
8886
  const replayer = new SessionReplayer({
8884
8887
  page: session.page,
8885
8888
  stepDelay: slowMo * 500,
package/dist/index.d.ts CHANGED
@@ -311,6 +311,10 @@ interface XBMouse {
311
311
  steps?: number;
312
312
  }): Promise<void>;
313
313
  wheel(deltaX: number, deltaY: number): Promise<void>;
314
+ /** Drag from current position to (x,y) — drives the real HTML5 DnD pipeline */
315
+ drag(x: number, y: number, opts?: {
316
+ steps?: number;
317
+ }): Promise<void>;
314
318
  }
315
319
  interface XBKeyboard {
316
320
  press(key: string, opts?: {
package/dist/index.js CHANGED
@@ -18,7 +18,7 @@ import {
18
18
  resolveLaunchOpts,
19
19
  saveSessionDiskMeta,
20
20
  setActivePage
21
- } from "./chunk-7V3B7VPX.js";
21
+ } from "./chunk-CE3L6HYV.js";
22
22
  import {
23
23
  detectAntiBot,
24
24
  formatDetectionMessage
@@ -86,7 +86,7 @@ import {
86
86
  rand,
87
87
  sleep,
88
88
  wheelDelta
89
- } from "./chunk-A5HJ6IRE.js";
89
+ } from "./chunk-3KUJOXUE.js";
90
90
  import "./chunk-TNEN6VQ2.js";
91
91
  import {
92
92
  errMsg
@@ -981,14 +981,14 @@ var mouseCommand = registerCommand({
981
981
  description: "Control the mouse (move, click, etc.)",
982
982
  scope: "page",
983
983
  parameters: z6.object({
984
- action: z6.enum(["move", "down", "up", "click", "dblclick"]),
984
+ action: z6.enum(["move", "down", "up", "click", "dblclick", "drag"]),
985
985
  x: z6.coerce.number(),
986
986
  y: z6.coerce.number(),
987
987
  button: z6.enum(["left", "right", "middle"]).optional(),
988
988
  steps: z6.coerce.number().optional()
989
989
  }),
990
990
  result: z6.object({
991
- action: z6.enum(["move", "down", "up", "click", "dblclick"]),
991
+ action: z6.enum(["move", "down", "up", "click", "dblclick", "drag"]),
992
992
  x: z6.number(),
993
993
  y: z6.number()
994
994
  }),
@@ -1012,6 +1012,9 @@ var mouseCommand = registerCommand({
1012
1012
  case "dblclick":
1013
1013
  await ctx.page.mouse.dblclick(p.x, p.y, { button });
1014
1014
  break;
1015
+ case "drag":
1016
+ await ctx.page.mouse.drag(p.x, p.y, p.steps ? { steps: p.steps } : {});
1017
+ break;
1015
1018
  }
1016
1019
  return ok6({ action: p.action, x: p.x, y: p.y });
1017
1020
  }
@@ -7893,7 +7896,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
7893
7896
  }
7894
7897
  let targetPageOverride = null;
7895
7898
  if (_target && extraOpts?.cdpEndpoint) {
7896
- const { findTargetPage } = await import("./browser-3KTEVTMU.js");
7899
+ const { findTargetPage } = await import("./browser-GPRLH6P6.js");
7897
7900
  targetPageOverride = await findTargetPage(extraOpts.cdpEndpoint, _target);
7898
7901
  if (!targetPageOverride) {
7899
7902
  return errorResult(`Target "${_target}" not found. Use 'xbrowser targets --cdp ${extraOpts.cdpEndpoint}' to list available pages.`);
@@ -14106,7 +14109,7 @@ Run "xbrowser ${command} ${subCommand} --help" to see available parameters.`
14106
14109
  const targetPage = pages[cmdTabIndex];
14107
14110
  await targetPage.bringToFront().catch(() => {
14108
14111
  });
14109
- const { setActivePage: setActivePage2 } = await import("./browser-3KTEVTMU.js");
14112
+ const { setActivePage: setActivePage2 } = await import("./browser-GPRLH6P6.js");
14110
14113
  setActivePage2(session, targetPage);
14111
14114
  }
14112
14115
  }
@@ -17279,7 +17282,7 @@ var DataCollector = class {
17279
17282
  return results;
17280
17283
  }
17281
17284
  async createBrowserContext() {
17282
- const { launch } = await import("./cdp-driver-OC37OT4B.js");
17285
+ const { launch } = await import("./cdp-driver-GMDP4KDN.js");
17283
17286
  const { browser } = await launch({
17284
17287
  headless: true,
17285
17288
  args: ["--no-sandbox", "--disable-setuid-sandbox"]
@@ -34,7 +34,7 @@ var SessionReplayer = class {
34
34
  if (this.opts.page) {
35
35
  this.page = this.opts.page;
36
36
  } else if (this.opts.cdpUrl) {
37
- const { launch } = await import("./cdp-driver-DKNLIA6B.js");
37
+ const { launch } = await import("./cdp-driver-FIAUJTBZ.js");
38
38
  const { browser } = await launch({ cdpEndpoint: this.opts.cdpUrl });
39
39
  let contexts = browser.contexts();
40
40
  for (let i = 0; i < 10 && contexts.length === 0; i++) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xbrowser/cli",
3
- "version": "1.16.0",
3
+ "version": "1.17.0",
4
4
  "description": "Browser automation CLI for web scraping, headless browsing, SEO analysis, and AI agent workflows. A command-line alternative to Playwright, Puppeteer, and Selenium.",
5
5
  "type": "module",
6
6
  "bin": {