@xbrowser/cli 1.2.2 → 1.3.1
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/{browser-CWI6BXYK.js → browser-ALV2PLW4.js} +2 -2
- package/dist/{browser-PZX7PO23.js → browser-VKZVOOVV.js} +1 -1
- package/dist/{browser-JP2LFPR2.js → browser-WMQRPYXX.js} +2 -2
- package/dist/{cdp-driver-S5STYUZZ.js → cdp-driver-72HOBP4C.js} +1 -1
- package/dist/{cdp-driver-RPUNQBGM.js → cdp-driver-JZPU5YXY.js} +49 -1
- package/dist/{chunk-PHBK3TRN.js → chunk-2QQDTXDL.js} +4 -2
- package/dist/{chunk-SLQR57XZ.js → chunk-7OGKQWXE.js} +1 -1
- package/dist/{chunk-AT4PHAJY.js → chunk-GJAV3QGG.js} +2 -2
- package/dist/{chunk-L53IDAWK.js → chunk-MJFYLKGL.js} +3 -2
- package/dist/{chunk-QFROODUU.js → chunk-N4PIGZDG.js} +49 -1
- package/dist/{chunk-MNFOCOL6.js → chunk-TLOHV3FP.js} +49 -1
- package/dist/{chunk-6V57JME6.js → chunk-UZQYMXR5.js} +1 -1
- package/dist/cli.js +178 -51
- package/dist/daemon-main.js +68 -43
- package/dist/{extract-BSYBM4MR.js → extract-O46CC533.js} +3 -2
- package/dist/{extract-2ZFW2MX7.js → extract-RM62AJXW.js} +1 -1
- package/dist/{filter-3JQWBM5F.js → filter-K6FGRJQU.js} +1 -1
- package/dist/{filter-EDTFGLS5.js → filter-TAAYMSYI.js} +2 -2
- package/dist/index.d.ts +10 -0
- package/dist/index.js +183 -56
- package/dist/{plugin-singleton-ZBVTWEYK.js → plugin-singleton-SYJF6BD6.js} +1 -1
- package/dist/{session-replayer-IXLSCF5U.js → session-replayer-JIVFXVUL.js} +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -49,11 +49,11 @@ import {
|
|
|
49
49
|
extractAndSave,
|
|
50
50
|
extractRecording,
|
|
51
51
|
printExtractSummary
|
|
52
|
-
} from "./chunk-
|
|
52
|
+
} from "./chunk-MJFYLKGL.js";
|
|
53
53
|
import {
|
|
54
54
|
filterRecording,
|
|
55
55
|
parseExcludeTypes
|
|
56
|
-
} from "./chunk-
|
|
56
|
+
} from "./chunk-GJAV3QGG.js";
|
|
57
57
|
import {
|
|
58
58
|
SessionRecorder
|
|
59
59
|
} from "./chunk-ACFE6PKF.js";
|
|
@@ -81,8 +81,8 @@ import {
|
|
|
81
81
|
resolveLaunchOpts,
|
|
82
82
|
saveSessionDiskMeta,
|
|
83
83
|
setActivePage
|
|
84
|
-
} from "./chunk-
|
|
85
|
-
import "./chunk-
|
|
84
|
+
} from "./chunk-UZQYMXR5.js";
|
|
85
|
+
import "./chunk-N4PIGZDG.js";
|
|
86
86
|
import "./chunk-TNEN6VQ2.js";
|
|
87
87
|
import {
|
|
88
88
|
errMsg
|
|
@@ -114,7 +114,7 @@ var version = pkg.version;
|
|
|
114
114
|
// src/executor.ts
|
|
115
115
|
import {
|
|
116
116
|
ok as ok25,
|
|
117
|
-
fail as
|
|
117
|
+
fail as fail8,
|
|
118
118
|
isCommandResult,
|
|
119
119
|
CompositeStorage as CompositeStorage2,
|
|
120
120
|
TipCollector as TipCollector2,
|
|
@@ -300,7 +300,7 @@ function createStubContext(pluginName) {
|
|
|
300
300
|
|
|
301
301
|
// src/commands/navigation.ts
|
|
302
302
|
import { z } from "zod";
|
|
303
|
-
import { ok } from "@dyyz1993/xcli-core";
|
|
303
|
+
import { ok, fail } from "@dyyz1993/xcli-core";
|
|
304
304
|
|
|
305
305
|
// src/commands/command-registry.ts
|
|
306
306
|
var registry = /* @__PURE__ */ new Map();
|
|
@@ -392,13 +392,19 @@ var gotoCommand = registerCommand({
|
|
|
392
392
|
if (/^[\w-]+(\.[\w-]+)+/.test(url) || url.startsWith("localhost")) {
|
|
393
393
|
url = "https://" + url;
|
|
394
394
|
} else {
|
|
395
|
-
|
|
395
|
+
return fail(`Invalid URL: "${url}". Expected http(s)://, file://, about:, data:, or a domain name.`);
|
|
396
396
|
}
|
|
397
397
|
}
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
398
|
+
let response;
|
|
399
|
+
try {
|
|
400
|
+
response = await ctx.page.goto(url, {
|
|
401
|
+
waitUntil: p.waitUntil || "domcontentloaded",
|
|
402
|
+
...p.timeout ? { timeout: p.timeout } : {}
|
|
403
|
+
});
|
|
404
|
+
} catch (err) {
|
|
405
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
406
|
+
return fail(`Navigation failed: ${msg}`);
|
|
407
|
+
}
|
|
402
408
|
const ssr = await detectSsr(ctx.page);
|
|
403
409
|
return ok({ url, status: response?.status(), ...ssr ? { ssr } : {} });
|
|
404
410
|
}
|
|
@@ -713,6 +719,22 @@ var checkCommand = registerCommand({
|
|
|
713
719
|
return ok2({ selector: p.selector });
|
|
714
720
|
}
|
|
715
721
|
});
|
|
722
|
+
var uncheckCommand = registerCommand({
|
|
723
|
+
name: "uncheck",
|
|
724
|
+
description: "Uncheck checkbox or radio",
|
|
725
|
+
scope: "element",
|
|
726
|
+
selectorParams: ["selector"],
|
|
727
|
+
parameters: z2.object({
|
|
728
|
+
selector: z2.string()
|
|
729
|
+
}),
|
|
730
|
+
result: z2.object({
|
|
731
|
+
selector: z2.string()
|
|
732
|
+
}),
|
|
733
|
+
handler: async (p, ctx) => {
|
|
734
|
+
await ctx.page.uncheck(p.selector, { timeout: 1e4 });
|
|
735
|
+
return ok2({ selector: p.selector });
|
|
736
|
+
}
|
|
737
|
+
});
|
|
716
738
|
var hoverCommand = registerCommand({
|
|
717
739
|
name: "hover",
|
|
718
740
|
description: "Hover over element",
|
|
@@ -1254,7 +1276,7 @@ var setViewportCommand = registerCommand({
|
|
|
1254
1276
|
|
|
1255
1277
|
// src/commands/frame.ts
|
|
1256
1278
|
import { z as z12 } from "zod";
|
|
1257
|
-
import { ok as ok12, fail } from "@dyyz1993/xcli-core";
|
|
1279
|
+
import { ok as ok12, fail as fail2 } from "@dyyz1993/xcli-core";
|
|
1258
1280
|
var framesCommand = registerCommand({
|
|
1259
1281
|
name: "frames",
|
|
1260
1282
|
description: "List all frames in the current page",
|
|
@@ -1267,7 +1289,9 @@ var framesCommand = registerCommand({
|
|
|
1267
1289
|
}))
|
|
1268
1290
|
}),
|
|
1269
1291
|
handler: async (_p, ctx) => {
|
|
1270
|
-
const
|
|
1292
|
+
const discover = ctx.page.discoverFrames;
|
|
1293
|
+
const rawFrames = discover ? await discover.call(ctx.page) : ctx.page.frames();
|
|
1294
|
+
const frameList = rawFrames.map((frame, index) => ({
|
|
1271
1295
|
index,
|
|
1272
1296
|
name: frame.name(),
|
|
1273
1297
|
url: frame.url()
|
|
@@ -1289,17 +1313,18 @@ var frameCommand = registerCommand({
|
|
|
1289
1313
|
error: z12.string().optional()
|
|
1290
1314
|
}),
|
|
1291
1315
|
handler: async (p, ctx) => {
|
|
1292
|
-
const
|
|
1316
|
+
const discover = ctx.page.discoverFrames;
|
|
1317
|
+
const rawFrames = discover ? await discover.call(ctx.page) : ctx.page.frames();
|
|
1293
1318
|
let targetFrame;
|
|
1294
1319
|
if (p.index !== void 0) {
|
|
1295
|
-
targetFrame =
|
|
1320
|
+
targetFrame = rawFrames[p.index];
|
|
1296
1321
|
} else if (p.name !== void 0) {
|
|
1297
|
-
targetFrame =
|
|
1322
|
+
targetFrame = rawFrames.find((f) => f.name() === p.name);
|
|
1298
1323
|
} else {
|
|
1299
|
-
return
|
|
1324
|
+
return fail2("Must provide index or name");
|
|
1300
1325
|
}
|
|
1301
1326
|
if (!targetFrame) {
|
|
1302
|
-
return
|
|
1327
|
+
return fail2("Frame not found");
|
|
1303
1328
|
}
|
|
1304
1329
|
return ok12({
|
|
1305
1330
|
name: targetFrame.name(),
|
|
@@ -1945,7 +1970,7 @@ var actionsCommand = registerCommand({
|
|
|
1945
1970
|
|
|
1946
1971
|
// src/commands/scrape.ts
|
|
1947
1972
|
import { z as z15 } from "zod";
|
|
1948
|
-
import { ok as ok15, fail as
|
|
1973
|
+
import { ok as ok15, fail as fail3 } from "@dyyz1993/xcli-core";
|
|
1949
1974
|
|
|
1950
1975
|
// src/lib/html-to-markdown.ts
|
|
1951
1976
|
import * as cheerio from "cheerio";
|
|
@@ -2461,7 +2486,7 @@ var scrapeCommand = registerCommand({
|
|
|
2461
2486
|
}
|
|
2462
2487
|
}
|
|
2463
2488
|
}
|
|
2464
|
-
return
|
|
2489
|
+
return fail3(`Scrape failed after ${maxAttempts} attempt(s): ${lastError?.message ?? "unknown error"}`);
|
|
2465
2490
|
} finally {
|
|
2466
2491
|
await closeEphemeralContext(context);
|
|
2467
2492
|
}
|
|
@@ -3525,7 +3550,7 @@ ${errors.map((e) => ` - ${e.engine}: ${e.error}`).join("\n")}`
|
|
|
3525
3550
|
|
|
3526
3551
|
// src/commands/network.ts
|
|
3527
3552
|
import { z as z19 } from "zod";
|
|
3528
|
-
import { ok as ok19, fail as
|
|
3553
|
+
import { ok as ok19, fail as fail4 } from "@dyyz1993/xcli-core";
|
|
3529
3554
|
function extractPath2(url) {
|
|
3530
3555
|
try {
|
|
3531
3556
|
const u = new URL(url);
|
|
@@ -3690,7 +3715,7 @@ var networkCommand = registerCommand({
|
|
|
3690
3715
|
};
|
|
3691
3716
|
if (p.listen) {
|
|
3692
3717
|
const page2 = ctx.page;
|
|
3693
|
-
if (!page2) return
|
|
3718
|
+
if (!page2) return fail4("No active page. Use --cdp to connect first.");
|
|
3694
3719
|
const captures = [];
|
|
3695
3720
|
const consoleMessages = [];
|
|
3696
3721
|
const wsCaptures = [];
|
|
@@ -4347,7 +4372,7 @@ function parseMarkdownResults(rawText) {
|
|
|
4347
4372
|
|
|
4348
4373
|
// src/commands/snapshot.ts
|
|
4349
4374
|
import { z as z21 } from "zod";
|
|
4350
|
-
import { ok as ok20, fail as
|
|
4375
|
+
import { ok as ok20, fail as fail5, normalizeTips as normalizeTips3 } from "@dyyz1993/xcli-core";
|
|
4351
4376
|
|
|
4352
4377
|
// src/runtime/ref-store.ts
|
|
4353
4378
|
var sessions = /* @__PURE__ */ new Map();
|
|
@@ -5303,7 +5328,7 @@ var snapshotCommand = registerCommand({
|
|
|
5303
5328
|
persistSemantics(url, aria);
|
|
5304
5329
|
return ok20({ url, title, aria, text, dom }, normalizeTips3(tips));
|
|
5305
5330
|
}
|
|
5306
|
-
return
|
|
5331
|
+
return fail5(`Unknown snapshot type: ${p.type}`);
|
|
5307
5332
|
}
|
|
5308
5333
|
});
|
|
5309
5334
|
function persistSemantics(url, aria) {
|
|
@@ -5472,7 +5497,7 @@ var waitForCommand = registerCommand({
|
|
|
5472
5497
|
|
|
5473
5498
|
// src/commands/tab.ts
|
|
5474
5499
|
import { z as z23 } from "zod";
|
|
5475
|
-
import { ok as ok22, fail as
|
|
5500
|
+
import { ok as ok22, fail as fail6 } from "@dyyz1993/xcli-core";
|
|
5476
5501
|
var TabParams = z23.object({
|
|
5477
5502
|
subcommand: z23.enum(["list", "new", "close", "switch"]),
|
|
5478
5503
|
url: z23.string().optional(),
|
|
@@ -5499,7 +5524,7 @@ var tabCommand = registerCommand({
|
|
|
5499
5524
|
case "switch":
|
|
5500
5525
|
return handleSwitch(p, pages, ctx);
|
|
5501
5526
|
default:
|
|
5502
|
-
return
|
|
5527
|
+
return fail6(`Unknown subcommand: ${p.subcommand}`);
|
|
5503
5528
|
}
|
|
5504
5529
|
}
|
|
5505
5530
|
});
|
|
@@ -5556,11 +5581,11 @@ async function handleNew(p, _pages, ctx) {
|
|
|
5556
5581
|
}
|
|
5557
5582
|
async function handleClose(p, pages, ctx) {
|
|
5558
5583
|
if (pages.length <= 1) {
|
|
5559
|
-
return
|
|
5584
|
+
return fail6("Cannot close the last remaining tab");
|
|
5560
5585
|
}
|
|
5561
5586
|
const closeIndex = p.index ?? pages.indexOf(ctx.page);
|
|
5562
5587
|
if (closeIndex < 0 || closeIndex >= pages.length) {
|
|
5563
|
-
return
|
|
5588
|
+
return fail6(`Invalid tab index: ${closeIndex}. Valid range: 0-${pages.length - 1}`);
|
|
5564
5589
|
}
|
|
5565
5590
|
const pageToClose = pages[closeIndex];
|
|
5566
5591
|
const isActivePage = pageToClose === ctx.page;
|
|
@@ -5583,10 +5608,10 @@ async function handleClose(p, pages, ctx) {
|
|
|
5583
5608
|
}
|
|
5584
5609
|
async function handleSwitch(p, pages, ctx) {
|
|
5585
5610
|
if (p.index === void 0) {
|
|
5586
|
-
return
|
|
5611
|
+
return fail6("Parameter --index is required for switch subcommand");
|
|
5587
5612
|
}
|
|
5588
5613
|
if (p.index < 0 || p.index >= pages.length) {
|
|
5589
|
-
return
|
|
5614
|
+
return fail6(`Invalid tab index: ${p.index}. Valid range: 0-${pages.length - 1}`);
|
|
5590
5615
|
}
|
|
5591
5616
|
const targetPage = pages[p.index];
|
|
5592
5617
|
await targetPage.bringToFront().catch(() => {
|
|
@@ -5832,7 +5857,7 @@ registerCommandDefinition("addinitscript", ["script"]);
|
|
|
5832
5857
|
|
|
5833
5858
|
// src/commands/find.ts
|
|
5834
5859
|
import { z as z25 } from "zod";
|
|
5835
|
-
import { ok as ok24, fail as
|
|
5860
|
+
import { ok as ok24, fail as fail7, normalizeTips as normalizeTips5 } from "@dyyz1993/xcli-core";
|
|
5836
5861
|
var actionSchema2 = z25.enum(["click", "fill", "type", "select", "hover", "check"]);
|
|
5837
5862
|
var findCommand = registerCommand({
|
|
5838
5863
|
name: "find",
|
|
@@ -5873,7 +5898,7 @@ var findCommand = registerCommand({
|
|
|
5873
5898
|
});
|
|
5874
5899
|
const count = await locator.count();
|
|
5875
5900
|
if (count === 0) {
|
|
5876
|
-
return
|
|
5901
|
+
return fail7(`No element found with ${p.strategy}="${p.value}"`);
|
|
5877
5902
|
}
|
|
5878
5903
|
const tips = [];
|
|
5879
5904
|
const target = selectTarget(locator, p.strategy);
|
|
@@ -5885,15 +5910,15 @@ var findCommand = registerCommand({
|
|
|
5885
5910
|
await target.click({ timeout: p.timeout, force: true });
|
|
5886
5911
|
return okWithTips({ matched: count, selector, action: "click" }, tips);
|
|
5887
5912
|
} else if (actionName === "fill") {
|
|
5888
|
-
if (actionValue === void 0) return
|
|
5913
|
+
if (actionValue === void 0) return fail7("find fill requires a value");
|
|
5889
5914
|
await target.fill(actionValue, { timeout: p.timeout, force: true });
|
|
5890
5915
|
return okWithTips({ matched: count, selector, action: `fill("${actionValue}")` }, tips);
|
|
5891
5916
|
} else if (actionName === "type") {
|
|
5892
|
-
if (actionValue === void 0) return
|
|
5917
|
+
if (actionValue === void 0) return fail7("find type requires a value");
|
|
5893
5918
|
await target.type(actionValue, { delay: 10, timeout: p.timeout });
|
|
5894
5919
|
return okWithTips({ matched: count, selector, action: `type("${actionValue}")` }, tips);
|
|
5895
5920
|
} else if (actionName === "select") {
|
|
5896
|
-
if (actionValue === void 0) return
|
|
5921
|
+
if (actionValue === void 0) return fail7("find select requires a value");
|
|
5897
5922
|
await target.selectOption(actionValue);
|
|
5898
5923
|
return okWithTips({ matched: count, selector, action: `select("${actionValue}")` }, tips);
|
|
5899
5924
|
} else if (actionName === "hover") {
|
|
@@ -6648,8 +6673,10 @@ var XBrowserPluginLoader = class {
|
|
|
6648
6673
|
const instance = await this.loadPlugin(indexPath, entry.name);
|
|
6649
6674
|
loaded.push(instance);
|
|
6650
6675
|
} catch (err) {
|
|
6651
|
-
|
|
6652
|
-
|
|
6676
|
+
const errMsg2 = err instanceof Error ? err.message : String(err);
|
|
6677
|
+
console.warn(`\u26A0\uFE0F Plugin "${entry.name}" load failed: ${errMsg2}`);
|
|
6678
|
+
if (errMsg2.includes("Cannot find module") && errMsg2.includes("shared/")) {
|
|
6679
|
+
console.warn(` \u{1F4A1} This plugin needs shared/ dependencies. Try: xbrowser plugin install shared`);
|
|
6653
6680
|
}
|
|
6654
6681
|
}
|
|
6655
6682
|
}
|
|
@@ -7315,7 +7342,7 @@ async function guardCheck(commandName) {
|
|
|
7315
7342
|
}
|
|
7316
7343
|
}
|
|
7317
7344
|
function errorResult(message) {
|
|
7318
|
-
return { ...
|
|
7345
|
+
return { ...fail8(message), duration: 0 };
|
|
7319
7346
|
}
|
|
7320
7347
|
function tipsToMessages(tips) {
|
|
7321
7348
|
if (!tips || tips.length === 0) return [];
|
|
@@ -7351,7 +7378,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
|
|
|
7351
7378
|
}
|
|
7352
7379
|
let targetPageOverride = null;
|
|
7353
7380
|
if (_target && extraOpts?.cdpEndpoint) {
|
|
7354
|
-
const { findTargetPage } = await import("./browser-
|
|
7381
|
+
const { findTargetPage } = await import("./browser-WMQRPYXX.js");
|
|
7355
7382
|
targetPageOverride = await findTargetPage(extraOpts.cdpEndpoint, _target);
|
|
7356
7383
|
if (!targetPageOverride) {
|
|
7357
7384
|
return errorResult(`Target "${_target}" not found. Use 'xbrowser targets --cdp ${extraOpts.cdpEndpoint}' to list available pages.`);
|
|
@@ -7555,7 +7582,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
|
|
|
7555
7582
|
duration,
|
|
7556
7583
|
timestamp: start
|
|
7557
7584
|
});
|
|
7558
|
-
return { ...
|
|
7585
|
+
return { ...fail8(errorMessage), duration };
|
|
7559
7586
|
} finally {
|
|
7560
7587
|
}
|
|
7561
7588
|
}
|
|
@@ -7596,7 +7623,7 @@ async function executeChain(input, options) {
|
|
|
7596
7623
|
results.push({
|
|
7597
7624
|
command: cmdName,
|
|
7598
7625
|
raw: cmdStr,
|
|
7599
|
-
...
|
|
7626
|
+
...fail8(`Plugin "${cmdName}" requires a sub-command`),
|
|
7600
7627
|
duration: 0
|
|
7601
7628
|
});
|
|
7602
7629
|
if (type === "and") {
|
|
@@ -7615,7 +7642,7 @@ async function executeChain(input, options) {
|
|
|
7615
7642
|
results.push({
|
|
7616
7643
|
command: cmdName,
|
|
7617
7644
|
raw: cmdStr,
|
|
7618
|
-
...
|
|
7645
|
+
...fail8(`Unknown command "${subCommand}" for plugin "${cmdName}"`),
|
|
7619
7646
|
duration: 0
|
|
7620
7647
|
});
|
|
7621
7648
|
if (type === "and") {
|
|
@@ -7747,7 +7774,7 @@ async function executeChain(input, options) {
|
|
|
7747
7774
|
results.push({
|
|
7748
7775
|
command: `${cmdName} ${subCommand}`,
|
|
7749
7776
|
raw: cmdStr,
|
|
7750
|
-
...
|
|
7777
|
+
...fail8(errorMessage),
|
|
7751
7778
|
duration: duration2
|
|
7752
7779
|
});
|
|
7753
7780
|
if (type === "and") {
|
|
@@ -7819,7 +7846,7 @@ async function executeChain(input, options) {
|
|
|
7819
7846
|
}
|
|
7820
7847
|
}
|
|
7821
7848
|
function isChainInput(input) {
|
|
7822
|
-
return /\s&&\s|\s;\s|\s,\s|\s\+\s|\s->\s/.test(input);
|
|
7849
|
+
return /\s&&\s|\|\|\s|\s;\s|\s,\s|\s\+\s|\s->\s/.test(input);
|
|
7823
7850
|
}
|
|
7824
7851
|
|
|
7825
7852
|
// src/context.ts
|
|
@@ -8069,9 +8096,12 @@ import {
|
|
|
8069
8096
|
existsSync as existsSync10,
|
|
8070
8097
|
readdirSync as readdirSync2,
|
|
8071
8098
|
mkdirSync as mkdirSync8,
|
|
8072
|
-
rmSync as rmSync6
|
|
8099
|
+
rmSync as rmSync6,
|
|
8100
|
+
copyFileSync,
|
|
8101
|
+
cpSync as cpSync6,
|
|
8102
|
+
readFileSync as readFileSync8
|
|
8073
8103
|
} from "fs";
|
|
8074
|
-
import { resolve as resolve8, basename as basename2 } from "path";
|
|
8104
|
+
import { resolve as resolve8, basename as basename2, dirname as dirname3 } from "path";
|
|
8075
8105
|
import { homedir as homedir7 } from "os";
|
|
8076
8106
|
|
|
8077
8107
|
// src/plugin/install-sources/local.ts
|
|
@@ -8564,28 +8594,103 @@ var PluginInstaller = class {
|
|
|
8564
8594
|
switch (type) {
|
|
8565
8595
|
case "local":
|
|
8566
8596
|
return await installFromLocal(source, name, targetDir).then((r) => {
|
|
8597
|
+
this.fixSharedDeps(targetDir);
|
|
8567
8598
|
ensurePluginDependencies(this.pluginsDir);
|
|
8568
8599
|
return r;
|
|
8569
8600
|
});
|
|
8570
8601
|
case "npm":
|
|
8571
8602
|
return await installFromNpm(resolvedSource, name, targetDir).then((r) => {
|
|
8603
|
+
this.fixSharedDeps(targetDir);
|
|
8572
8604
|
ensurePluginDependencies(this.pluginsDir);
|
|
8573
8605
|
return r;
|
|
8574
8606
|
});
|
|
8575
8607
|
case "git":
|
|
8576
8608
|
return await installFromGit(source, name, targetDir).then((r) => {
|
|
8609
|
+
this.fixSharedDeps(targetDir);
|
|
8577
8610
|
ensurePluginDependencies(this.pluginsDir);
|
|
8578
8611
|
return r;
|
|
8579
8612
|
});
|
|
8580
8613
|
case "url":
|
|
8581
8614
|
return await installFromUrl(source, name, targetDir).then((r) => {
|
|
8615
|
+
this.fixSharedDeps(targetDir);
|
|
8582
8616
|
ensurePluginDependencies(this.pluginsDir);
|
|
8583
8617
|
return r;
|
|
8584
8618
|
});
|
|
8585
8619
|
}
|
|
8586
8620
|
}
|
|
8621
|
+
/**
|
|
8622
|
+
* Fix missing `../shared/` dependencies after installation.
|
|
8623
|
+
*
|
|
8624
|
+
* Some marketplace/npm packages import from `../shared/` (e.g. ssr-detect.js,
|
|
8625
|
+
* ai-chat-base.ts) but the `shared/` directory is not included in the package.
|
|
8626
|
+
* This method scans the installed plugin's index.ts for such imports and
|
|
8627
|
+
* copies the missing files from the local repository's `.xcli/plugins/shared/`
|
|
8628
|
+
* directory (if available).
|
|
8629
|
+
*/
|
|
8630
|
+
fixSharedDeps(pluginDir) {
|
|
8631
|
+
const indexPath = resolve8(pluginDir, "index.ts");
|
|
8632
|
+
if (!existsSync10(indexPath)) return;
|
|
8633
|
+
let content;
|
|
8634
|
+
try {
|
|
8635
|
+
content = readFileSync8(indexPath, "utf8");
|
|
8636
|
+
} catch {
|
|
8637
|
+
return;
|
|
8638
|
+
}
|
|
8639
|
+
const sharedImportRegex = /from\s+['"]\.\.\/shared\/([^'"]+)['"]/g;
|
|
8640
|
+
const missingFiles = [];
|
|
8641
|
+
let match;
|
|
8642
|
+
while ((match = sharedImportRegex.exec(content)) !== null) {
|
|
8643
|
+
missingFiles.push(match[1]);
|
|
8644
|
+
}
|
|
8645
|
+
if (missingFiles.length === 0) return;
|
|
8646
|
+
const sharedDir = resolve8(pluginDir, "..", "shared");
|
|
8647
|
+
const toCopy = [];
|
|
8648
|
+
for (const file of missingFiles) {
|
|
8649
|
+
const targetPath = resolve8(sharedDir, file);
|
|
8650
|
+
if (!existsSync10(targetPath)) {
|
|
8651
|
+
toCopy.push(file);
|
|
8652
|
+
}
|
|
8653
|
+
}
|
|
8654
|
+
if (toCopy.length === 0) return;
|
|
8655
|
+
const repoSharedDirs = [
|
|
8656
|
+
resolve8(process.cwd(), ".xcli/plugins/shared"),
|
|
8657
|
+
resolve8(homedir7(), ".xbrowser/plugins/shared")
|
|
8658
|
+
];
|
|
8659
|
+
let sourceSharedDir = null;
|
|
8660
|
+
for (const dir of repoSharedDirs) {
|
|
8661
|
+
if (existsSync10(dir)) {
|
|
8662
|
+
sourceSharedDir = dir;
|
|
8663
|
+
break;
|
|
8664
|
+
}
|
|
8665
|
+
}
|
|
8666
|
+
if (!sourceSharedDir) {
|
|
8667
|
+
console.warn(`\u26A0\uFE0F Plugin "${basename2(pluginDir)}" imports shared files but they are missing: ${toCopy.join(", ")}`);
|
|
8668
|
+
console.warn(` To fix: install the "shared" plugin or copy .xcli/plugins/shared/ to ~/.xbrowser/plugins/shared/`);
|
|
8669
|
+
return;
|
|
8670
|
+
}
|
|
8671
|
+
mkdirSync8(sharedDir, { recursive: true });
|
|
8672
|
+
for (const file of toCopy) {
|
|
8673
|
+
const src = resolve8(sourceSharedDir, file);
|
|
8674
|
+
const dst = resolve8(sharedDir, file);
|
|
8675
|
+
if (existsSync10(src)) {
|
|
8676
|
+
try {
|
|
8677
|
+
cpSync6(dirname3(src), dirname3(dst), { recursive: true });
|
|
8678
|
+
console.log(`\u2705 Copied shared/${file} for plugin "${basename2(pluginDir)}"`);
|
|
8679
|
+
} catch {
|
|
8680
|
+
try {
|
|
8681
|
+
copyFileSync(src, dst);
|
|
8682
|
+
console.log(`\u2705 Copied shared/${file} for plugin "${basename2(pluginDir)}"`);
|
|
8683
|
+
} catch {
|
|
8684
|
+
console.warn(`\u26A0\uFE0F Could not copy shared/${file}`);
|
|
8685
|
+
}
|
|
8686
|
+
}
|
|
8687
|
+
}
|
|
8688
|
+
}
|
|
8689
|
+
}
|
|
8587
8690
|
async installFromMarketplace(slug, options) {
|
|
8588
8691
|
const result = await installFromMarketplace(this.pluginsDir, slug, options);
|
|
8692
|
+
const targetDir = resolve8(this.pluginsDir, result.name);
|
|
8693
|
+
this.fixSharedDeps(targetDir);
|
|
8589
8694
|
ensurePluginDependencies(this.pluginsDir);
|
|
8590
8695
|
return result;
|
|
8591
8696
|
}
|
|
@@ -10233,6 +10338,17 @@ async function handleBrowserCommand(command, args, options, sessionName, mode, c
|
|
|
10233
10338
|
};
|
|
10234
10339
|
break;
|
|
10235
10340
|
}
|
|
10341
|
+
case "tab": {
|
|
10342
|
+
const tabSub = args[0];
|
|
10343
|
+
const validSubs = ["list", "new", "close", "switch"];
|
|
10344
|
+
cmdName = "tab";
|
|
10345
|
+
params = {
|
|
10346
|
+
subcommand: validSubs.includes(tabSub) ? tabSub : options.subcommand,
|
|
10347
|
+
url: args[1] || options.url,
|
|
10348
|
+
index: options.index ? Number(options.index) : void 0
|
|
10349
|
+
};
|
|
10350
|
+
break;
|
|
10351
|
+
}
|
|
10236
10352
|
default:
|
|
10237
10353
|
cmdName = command;
|
|
10238
10354
|
params = { ...options };
|
|
@@ -11055,6 +11171,7 @@ async function handleConvert(args, _mode) {
|
|
|
11055
11171
|
const { generateJSScript: generateJSScript2, generatePythonScript: generatePythonScript2, generateBashScript: generateBashScript2 } = await import("./convert-R3XXYKC6.js");
|
|
11056
11172
|
const content = fs6.readFileSync(filePath, "utf-8");
|
|
11057
11173
|
const recording = yaml3.parse(content);
|
|
11174
|
+
if (recording.actions && !recording.events) recording.events = recording.actions;
|
|
11058
11175
|
const ext = path5.extname(outputPath).toLowerCase();
|
|
11059
11176
|
let script;
|
|
11060
11177
|
if (ext === ".py") {
|
|
@@ -11066,7 +11183,7 @@ async function handleConvert(args, _mode) {
|
|
|
11066
11183
|
}
|
|
11067
11184
|
fs6.writeFileSync(outputPath, script);
|
|
11068
11185
|
fs6.chmodSync(outputPath, 493);
|
|
11069
|
-
const eventCount = (recording.events || []).length;
|
|
11186
|
+
const eventCount = (recording.events || recording.actions || []).length;
|
|
11070
11187
|
console.log(`Converted ${filePath} -> ${outputPath}`);
|
|
11071
11188
|
console.log(` Events: ${eventCount}, Start URL: ${recording.startUrl}`);
|
|
11072
11189
|
console.log(` Run: ${ext === ".py" ? "python" : ext === ".sh" ? "./" : "node"} ${outputPath}`);
|
|
@@ -11077,7 +11194,7 @@ async function handleExtract(args, _mode) {
|
|
|
11077
11194
|
console.error("Usage: xbrowser extract <recording.yaml>");
|
|
11078
11195
|
process.exit(1);
|
|
11079
11196
|
}
|
|
11080
|
-
const { extractAndSave: extractAndSave2, printExtractSummary: printExtractSummary2 } = await import("./extract-
|
|
11197
|
+
const { extractAndSave: extractAndSave2, printExtractSummary: printExtractSummary2 } = await import("./extract-RM62AJXW.js");
|
|
11081
11198
|
const { summary, outputPath } = extractAndSave2(filePath);
|
|
11082
11199
|
printExtractSummary2(summary);
|
|
11083
11200
|
console.log(`
|
|
@@ -11090,7 +11207,7 @@ async function handleFilter(args, _mode, options) {
|
|
|
11090
11207
|
console.error("Usage: xbrowser filter <input.yaml> <output.yaml> [--exclude type1,type2]");
|
|
11091
11208
|
process.exit(1);
|
|
11092
11209
|
}
|
|
11093
|
-
const { filterRecording: filterRecording2, parseExcludeTypes: parseExcludeTypes2 } = await import("./filter-
|
|
11210
|
+
const { filterRecording: filterRecording2, parseExcludeTypes: parseExcludeTypes2 } = await import("./filter-K6FGRJQU.js");
|
|
11094
11211
|
const excludeArgs = args.slice(2).concat(
|
|
11095
11212
|
Object.entries(options || {}).flatMap(
|
|
11096
11213
|
([k, v]) => k.startsWith("exclude") ? [`--${k}${typeof v === "string" ? "=" + v : ""}`] : []
|
|
@@ -11653,7 +11770,7 @@ async function handleNetCommand(args, options, mode, sessionName) {
|
|
|
11653
11770
|
|
|
11654
11771
|
// src/cli/test-routes.ts
|
|
11655
11772
|
import { execSync as execSync3 } from "child_process";
|
|
11656
|
-
import { readFileSync as
|
|
11773
|
+
import { readFileSync as readFileSync9 } from "fs";
|
|
11657
11774
|
import { resolve as resolve9 } from "path";
|
|
11658
11775
|
function findPluginPath(plugin) {
|
|
11659
11776
|
const candidates = [
|
|
@@ -11662,7 +11779,7 @@ function findPluginPath(plugin) {
|
|
|
11662
11779
|
];
|
|
11663
11780
|
for (const p of candidates) {
|
|
11664
11781
|
try {
|
|
11665
|
-
|
|
11782
|
+
readFileSync9(p, "utf-8");
|
|
11666
11783
|
return p;
|
|
11667
11784
|
} catch {
|
|
11668
11785
|
}
|
|
@@ -11673,7 +11790,7 @@ function extractSchema(plugin, command) {
|
|
|
11673
11790
|
const pluginPath = findPluginPath(plugin);
|
|
11674
11791
|
let src;
|
|
11675
11792
|
try {
|
|
11676
|
-
src =
|
|
11793
|
+
src = readFileSync9(pluginPath, "utf-8");
|
|
11677
11794
|
} catch {
|
|
11678
11795
|
return null;
|
|
11679
11796
|
}
|
|
@@ -12545,7 +12662,7 @@ function extractCdpFromArgv(argv) {
|
|
|
12545
12662
|
return process.env.XBROWSER_CDP;
|
|
12546
12663
|
}
|
|
12547
12664
|
async function handleStdinMode(stdinCommands, argv) {
|
|
12548
|
-
const chain = stdinCommands.join("
|
|
12665
|
+
const chain = stdinCommands.join(" ; ");
|
|
12549
12666
|
const cdpEndpoint = argv ? extractCdpFromArgv(argv) : void 0;
|
|
12550
12667
|
const sessionName = argv ? extractSessionNameFromArgv(argv) : "default";
|
|
12551
12668
|
const chainResult = await executeChain(chain, { fileMode: true, cdpEndpoint, sessionName });
|
|
@@ -12808,6 +12925,13 @@ async function routeCommand(argvIn, stdinCommands) {
|
|
|
12808
12925
|
for (const step of chainResult.steps) {
|
|
12809
12926
|
if (step.success) {
|
|
12810
12927
|
console.log(`[OK] ${step.raw}`);
|
|
12928
|
+
if (step.data && typeof step.data === "object") {
|
|
12929
|
+
const d = step.data;
|
|
12930
|
+
for (const [k, v] of Object.entries(d)) {
|
|
12931
|
+
if (k !== "ok" && k !== "success")
|
|
12932
|
+
console.log(` ${k}: ${typeof v === "string" ? v : JSON.stringify(v)}`);
|
|
12933
|
+
}
|
|
12934
|
+
}
|
|
12811
12935
|
if (step.tips?.length) {
|
|
12812
12936
|
for (const tip of step.tips) {
|
|
12813
12937
|
console.log(` \u{1F4A1} ${tip}`);
|
|
@@ -12817,6 +12941,9 @@ async function routeCommand(argvIn, stdinCommands) {
|
|
|
12817
12941
|
console.error(`[FAIL] ${step.raw}: ${step.message}`);
|
|
12818
12942
|
}
|
|
12819
12943
|
}
|
|
12944
|
+
if (chainResult.stoppedReason) {
|
|
12945
|
+
console.error(`Stopped: ${chainResult.stoppedReason}`);
|
|
12946
|
+
}
|
|
12820
12947
|
if (!chainResult.success) throw new Error("Command failed");
|
|
12821
12948
|
return;
|
|
12822
12949
|
}
|
|
@@ -14451,10 +14578,10 @@ var FileDownloadHandler = class {
|
|
|
14451
14578
|
async handle(ctx) {
|
|
14452
14579
|
const msg = ctx.message;
|
|
14453
14580
|
try {
|
|
14454
|
-
const { readFileSync:
|
|
14581
|
+
const { readFileSync: readFileSync11 } = await import("fs");
|
|
14455
14582
|
const { resolve: resolve10, basename: basename3 } = await import("path");
|
|
14456
14583
|
const targetPath = resolve10(msg.path);
|
|
14457
|
-
const data =
|
|
14584
|
+
const data = readFileSync11(targetPath);
|
|
14458
14585
|
const base64 = data.toString("base64");
|
|
14459
14586
|
const ext = targetPath.split(".").pop()?.toLowerCase() || "";
|
|
14460
14587
|
const mimeMap = {
|
|
@@ -15996,7 +16123,7 @@ var DataCollector = class {
|
|
|
15996
16123
|
return results;
|
|
15997
16124
|
}
|
|
15998
16125
|
async createBrowserContext() {
|
|
15999
|
-
const { launch } = await import("./cdp-driver-
|
|
16126
|
+
const { launch } = await import("./cdp-driver-72HOBP4C.js");
|
|
16000
16127
|
const { browser } = await launch({
|
|
16001
16128
|
headless: true,
|
|
16002
16129
|
args: ["--no-sandbox", "--disable-setuid-sandbox"]
|
|
@@ -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-72HOBP4C.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++) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xbrowser/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.1",
|
|
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": {
|