@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
|
@@ -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
|
+
};
|
|
@@ -31,7 +31,7 @@ var SessionReplayer = class {
|
|
|
31
31
|
if (this.opts.page) {
|
|
32
32
|
this.page = this.opts.page;
|
|
33
33
|
} else if (this.opts.cdpUrl) {
|
|
34
|
-
const { launch } = await import("./cdp-driver-
|
|
34
|
+
const { launch } = await import("./cdp-driver-J3YQ4LJV.js");
|
|
35
35
|
const { browser } = await launch({ cdpEndpoint: this.opts.cdpUrl });
|
|
36
36
|
let contexts = browser.contexts();
|
|
37
37
|
for (let i = 0; i < 10 && contexts.length === 0; i++) {
|
|
@@ -79,6 +79,11 @@ var SessionReplayer = class {
|
|
|
79
79
|
const page = this.page;
|
|
80
80
|
const timeout = this.opts.stepTimeout;
|
|
81
81
|
switch (action.type) {
|
|
82
|
+
// Proactive sensing actions are observations, not user actions — skip
|
|
83
|
+
// them during replay (they don't represent anything the user did).
|
|
84
|
+
case "popup_appear":
|
|
85
|
+
case "discovered_filters":
|
|
86
|
+
return;
|
|
82
87
|
case "navigation":
|
|
83
88
|
await page.goto(action.url, { waitUntil: "load", timeout });
|
|
84
89
|
break;
|
|
@@ -153,7 +158,19 @@ var SessionReplayer = class {
|
|
|
153
158
|
}
|
|
154
159
|
case "hover": {
|
|
155
160
|
const selector = await this.resolveAndWait(action);
|
|
156
|
-
|
|
161
|
+
if (selector) {
|
|
162
|
+
await page.hover(selector);
|
|
163
|
+
}
|
|
164
|
+
const firstPopup = action.hoverContext?.appeared?.[0];
|
|
165
|
+
if (firstPopup?.selector) {
|
|
166
|
+
try {
|
|
167
|
+
await page.waitForSelector(firstPopup.selector, {
|
|
168
|
+
state: "visible",
|
|
169
|
+
timeout: 1e3
|
|
170
|
+
});
|
|
171
|
+
} catch {
|
|
172
|
+
}
|
|
173
|
+
}
|
|
157
174
|
break;
|
|
158
175
|
}
|
|
159
176
|
case "drag": {
|
|
@@ -257,7 +274,9 @@ var SessionReplayer = class {
|
|
|
257
274
|
* Returns the first matching selector, or throws if none match.
|
|
258
275
|
* Fallback order:
|
|
259
276
|
* 1. Primary CSS selector (always tried first)
|
|
260
|
-
* 2. textFallback selector (
|
|
277
|
+
* 2. textFallback selector (used when primary fails — not just for low
|
|
278
|
+
* confidence, since high-confidence selectors from dynamic attributes
|
|
279
|
+
* like data-spm-anchor-id can also fail on replay)
|
|
261
280
|
* 3. Tag-based fallback (last resort)
|
|
262
281
|
*/
|
|
263
282
|
async resolveAndWait(action) {
|
|
@@ -267,10 +286,8 @@ var SessionReplayer = class {
|
|
|
267
286
|
const timeout = this.opts.stepTimeout;
|
|
268
287
|
const candidates = [];
|
|
269
288
|
if (el.selector) candidates.push(el.selector);
|
|
270
|
-
if (el.
|
|
271
|
-
|
|
272
|
-
candidates.push(el.textFallback.selector);
|
|
273
|
-
}
|
|
289
|
+
if (el.textFallback?.selector && !candidates.includes(el.textFallback.selector)) {
|
|
290
|
+
candidates.push(el.textFallback.selector);
|
|
274
291
|
}
|
|
275
292
|
if (el.tag && !candidates.includes(el.tag)) {
|
|
276
293
|
candidates.push(el.tag);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xbrowser/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.10.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": {
|