@xbrowser/cli 1.10.0 → 1.12.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.
Files changed (42) hide show
  1. package/dist/{anti-bot-DR56Y63V.js → anti-bot-GTTYNEFB.js} +1 -1
  2. package/dist/{browser-WQZ3D6AE.js → browser-3HQKDZQ6.js} +4 -2
  3. package/dist/{browser-T3V3JWVH.js → browser-ILXTEWH4.js} +1 -1
  4. package/dist/{browser-HBL72GPZ.js → browser-XPDMY2NX.js} +3 -3
  5. package/dist/{cdp-driver-J3YQ4LJV.js → cdp-driver-ESH3PDE6.js} +2 -1
  6. package/dist/cdp-driver-N2E2ZNSC.js +4644 -0
  7. package/dist/cdp-driver-YIKR4BUX.js +49 -0
  8. package/dist/chunk-3FWLW7FS.js +106 -0
  9. package/dist/chunk-A6LPGFAL.js +437 -0
  10. package/dist/{chunk-RRBXV7KE.js → chunk-DKM2JOZL.js} +633 -63
  11. package/dist/{chunk-ABXMBNQ6.js → chunk-H2A5JUK5.js} +37 -466
  12. package/dist/{chunk-5UGR6MUK.js → chunk-HBHOKZPN.js} +51 -16
  13. package/dist/{chunk-BNO7OKO4.js → chunk-HBMEFSTB.js} +39 -0
  14. package/dist/{chunk-INTQPBYF.js → chunk-JKVUFP3G.js} +6 -2
  15. package/dist/chunk-KJTABK3Z.js +1255 -0
  16. package/dist/{chunk-DWDXEGVK.js → chunk-NKW4A74J.js} +9 -3
  17. package/dist/{chunk-4452SPFI.js → chunk-NODRQGOK.js} +51 -16
  18. package/dist/{chunk-SQHSZENE.js → chunk-OMU63E6J.js} +3 -22
  19. package/dist/{chunk-NITFVWWS.js → chunk-OVW2UEWN.js} +981 -407
  20. package/dist/{chunk-WSCP7QCJ.js → chunk-R6IJ6PIV.js} +14 -11
  21. package/dist/{chunk-YMUSHPU4.js → chunk-SH6OTPXN.js} +46 -16
  22. package/dist/{chunk-MWFHZUIY.js → chunk-TEXCXIBW.js} +1 -1
  23. package/dist/{cdp-driver-2T4P4ZE2.js → chunk-Z6CUR3VG.js} +1542 -134
  24. package/dist/{chunk-IIM5GOD7.js → chunk-ZTHE5RBZ.js} +7 -1
  25. package/dist/cli.js +1136 -584
  26. package/dist/{daemon-client-GKEPT4NY.js → daemon-client-7D6EZNOE.js} +46 -16
  27. package/dist/{daemon-client-O6BYVRXV.js → daemon-client-HEGAXWGK.js} +1 -1
  28. package/dist/daemon-main.js +1031 -522
  29. package/dist/{human-interaction-5AO42MBA.js → human-interaction-4YR2N6R6.js} +2 -2
  30. package/dist/{human-interaction-C5OEGXGO.js → human-interaction-Y5IDH6LD.js} +1 -1
  31. package/dist/{human-interaction-ISXZTAYY.js → human-interaction-ZUTR5AC2.js} +2 -2
  32. package/dist/index.d.ts +10 -0
  33. package/dist/index.js +1147 -593
  34. package/dist/{proxy-C6CK3UH5.js → proxy-LUR4U5YF.js} +2 -1
  35. package/dist/{recovery-J2ISVGUL.js → recovery-FTZGV6VY.js} +2 -2
  36. package/dist/{recovery-ZJVVHP7N.js → recovery-L5GLDX4O.js} +1 -1
  37. package/dist/{recovery-EF33LKRJ.js → recovery-Z3RDZENA.js} +2 -2
  38. package/dist/{session-recorder-H3KEYU26.js → session-recorder-3BEVWHOK.js} +1 -1
  39. package/dist/{session-recorder-QRZMKFVL.js → session-recorder-SLDBENVF.js} +1 -1
  40. package/dist/{session-replayer-LJUC4TI7.js → session-replayer-K4A6OI4M.js} +90 -2
  41. package/package.json +1 -1
  42. package/dist/chunk-BAHSRZIX.js +0 -2191
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  CDPInterceptorProxy
3
- } from "./chunk-ABXMBNQ6.js";
3
+ } from "./chunk-A6LPGFAL.js";
4
+ import "./chunk-H2A5JUK5.js";
4
5
  import "./chunk-KFQGP6VL.js";
5
6
  export {
6
7
  CDPInterceptorProxy
@@ -4,8 +4,8 @@ import {
4
4
  import "./chunk-Q2DFJQGS.js";
5
5
  import {
6
6
  HumanInteractionManager
7
- } from "./chunk-MWFHZUIY.js";
8
- import "./chunk-IIM5GOD7.js";
7
+ } from "./chunk-TEXCXIBW.js";
8
+ import "./chunk-ZTHE5RBZ.js";
9
9
  import "./chunk-KFQGP6VL.js";
10
10
 
11
11
  // src/recovery.ts
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-3OD76SUE.js";
4
4
  import {
5
5
  HumanInteractionManager
6
- } from "./chunk-DWDXEGVK.js";
6
+ } from "./chunk-NKW4A74J.js";
7
7
  import "./chunk-Q2DFJQGS.js";
8
8
  import "./chunk-KFQGP6VL.js";
9
9
 
@@ -4,8 +4,8 @@ import {
4
4
  import "./chunk-CG3D3CHY.js";
5
5
  import {
6
6
  HumanInteractionManager
7
- } from "./chunk-SQHSZENE.js";
8
- import "./chunk-BNO7OKO4.js";
7
+ } from "./chunk-OMU63E6J.js";
8
+ import "./chunk-HBMEFSTB.js";
9
9
  import "./chunk-KFQGP6VL.js";
10
10
 
11
11
  // src/recovery.ts
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  SessionRecorder
3
- } from "./chunk-5UGR6MUK.js";
3
+ } from "./chunk-HBHOKZPN.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-4452SPFI.js";
3
+ } from "./chunk-NODRQGOK.js";
4
4
  import "./chunk-OZKD3W4X.js";
5
5
  import "./chunk-KFQGP6VL.js";
6
6
  export {
@@ -1,3 +1,6 @@
1
+ import {
2
+ queryJS
3
+ } from "./chunk-3FWLW7FS.js";
1
4
  import "./chunk-KFQGP6VL.js";
2
5
 
3
6
  // src/recorder/session-replayer.ts
@@ -31,7 +34,7 @@ var SessionReplayer = class {
31
34
  if (this.opts.page) {
32
35
  this.page = this.opts.page;
33
36
  } else if (this.opts.cdpUrl) {
34
- const { launch } = await import("./cdp-driver-J3YQ4LJV.js");
37
+ const { launch } = await import("./cdp-driver-ESH3PDE6.js");
35
38
  const { browser } = await launch({ cdpEndpoint: this.opts.cdpUrl });
36
39
  let contexts = browser.contexts();
37
40
  for (let i = 0; i < 10 && contexts.length === 0; i++) {
@@ -44,13 +47,14 @@ var SessionReplayer = class {
44
47
  this.page = pages[0];
45
48
  }
46
49
  if (!this.page) throw new Error("No page available. Provide cdpUrl or page.");
47
- const actions = this.recording.actions;
50
+ const actions = this.dedupAdjacentActions(this.recording.actions);
48
51
  let success = 0;
49
52
  let failed = 0;
50
53
  let skipped = 0;
51
54
  for (let i = 0; i < actions.length; i++) {
52
55
  const action = actions[i];
53
56
  this.opts.onStep?.(action, i, actions.length);
57
+ const pagesBefore = this.listContextPages();
54
58
  try {
55
59
  if (action.trajectory) {
56
60
  await this.replayTrajectory(action.trajectory);
@@ -63,6 +67,15 @@ var SessionReplayer = class {
63
67
  }
64
68
  }
65
69
  success++;
70
+ if (action.type === "click" || action.type === "cdp-click") {
71
+ const pagesAfter = this.listContextPages();
72
+ if (pagesAfter && pagesBefore && pagesAfter.length > pagesBefore.length) {
73
+ const newest = pagesAfter[pagesAfter.length - 1];
74
+ await newest.bringToFront().catch(() => {
75
+ });
76
+ this.page = newest;
77
+ }
78
+ }
66
79
  } catch (e) {
67
80
  const err = e instanceof Error ? e : new Error(String(e));
68
81
  this.opts.onError?.(action, err, i);
@@ -75,6 +88,47 @@ var SessionReplayer = class {
75
88
  return { success, failed, skipped };
76
89
  }
77
90
  /** Replay a single action */
91
+ /** Pages of the replayer page's context (best-effort; null if unavailable). */
92
+ listContextPages() {
93
+ try {
94
+ const ctx = this.page.context?.();
95
+ const pages = ctx?.pages?.();
96
+ return Array.isArray(pages) ? pages : null;
97
+ } catch {
98
+ return null;
99
+ }
100
+ }
101
+ /**
102
+ * Replay-time adjacent dedup (rec-duel d02/d03/d05).
103
+ *
104
+ * The recorder can emit both the real action signal AND the injected cdp
105
+ * command action for a single interaction when the signal flush lags behind
106
+ * the recorder-side dedup window (heavy snapshot capture slows polling).
107
+ * Replaying both executes the interaction twice. Filter here: an action is
108
+ * skipped when a nearby (≤15s apart) earlier action matches on
109
+ * type-normalized key (selector + text) AND coordinates agree — either side
110
+ * lacks coords (cdp actions carry none) or both are within 30px. Two real
111
+ * clicks on the same element at different spots (canvas buttons) survive.
112
+ */
113
+ dedupAdjacentActions(actions) {
114
+ const normType = (t) => t === "cdp-click" ? "click" : t === "cdp-fill" ? "input" : t;
115
+ const generic = (a) => (a.type === "click" || a.type === "dblclick" || a.type === "contextmenu") && (a.element?.selector === "html" || a.element?.selector === "body");
116
+ const keyOf = (a) => `${normType(a.type)}|${a.element?.selector || ""}|${a.element?.text || ""}`;
117
+ const coordsOf = (a) => typeof a.x === "number" && typeof a.y === "number" ? { x: a.x, y: a.y } : null;
118
+ const near = (p, q) => Math.abs(p.x - q.x) <= 30 && Math.abs(p.y - q.y) <= 30;
119
+ const lastKept = /* @__PURE__ */ new Map();
120
+ return actions.filter((a) => {
121
+ if (generic(a)) return false;
122
+ const key = keyOf(a);
123
+ const c = coordsOf(a);
124
+ const recent = lastKept.get(key) || [];
125
+ const dup = recent.some((e) => Math.abs(a.timestamp - e.ts) <= 15e3 && (!c || !e.c || near(c, e.c)));
126
+ if (dup) return false;
127
+ recent.push({ ts: a.timestamp, c });
128
+ lastKept.set(key, recent);
129
+ return true;
130
+ });
131
+ }
78
132
  async replayAction(action) {
79
133
  const page = this.page;
80
134
  const timeout = this.opts.stepTimeout;
@@ -93,9 +147,43 @@ var SessionReplayer = class {
93
147
  case "click":
94
148
  case "cdp-click": {
95
149
  const selector = await this.resolveAndWait(action);
150
+ if (typeof action.x === "number" && typeof action.y === "number") {
151
+ const hit = await page.evaluate(`
152
+ (function() {
153
+ const el = ${queryJS(selector)};
154
+ if (!el) return false;
155
+ const r = el.getBoundingClientRect();
156
+ return ${action.x} >= r.x - 2 && ${action.x} <= r.x + r.width + 2
157
+ && ${action.y} >= r.y - 2 && ${action.y} <= r.y + r.height + 2;
158
+ })()
159
+ `).catch(() => false);
160
+ if (hit) {
161
+ await page.mouse.click(action.x, action.y, { stealth: true });
162
+ break;
163
+ }
164
+ }
96
165
  await page.click(selector, { timeout });
97
166
  break;
98
167
  }
168
+ case "scroll": {
169
+ const [dir, distStr] = (action.value || "down:300").split(":");
170
+ const dist = Number(distStr) || 300;
171
+ const sign = dir === "up" ? -1 : dir === "left" ? 0 : 1;
172
+ const selector = await this.resolveAndWait(action).catch(() => void 0);
173
+ if (selector) {
174
+ await page.evaluate(`
175
+ (function() {
176
+ const el = ${queryJS(selector)};
177
+ if (el) el.scrollTop += ${sign * dist};
178
+ })()
179
+ `).catch(() => {
180
+ });
181
+ } else {
182
+ await page.evaluate(`window.scrollBy(0, ${sign * dist})`).catch(() => {
183
+ });
184
+ }
185
+ break;
186
+ }
99
187
  case "input": {
100
188
  const selector = await this.resolveAndWait(action);
101
189
  await page.fill(selector, action.value ?? "", { timeout });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xbrowser/cli",
3
- "version": "1.10.0",
3
+ "version": "1.12.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": {