@xbrowser/cli 1.2.1 → 1.3.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/chunk-2QQDTXDL.js +438 -0
- package/dist/{chunk-XYXCS7JW.js → chunk-JPSFUFPG.js} +4 -2
- package/dist/cli.js +109 -15
- package/dist/{daemon-client-R4QWHD7V.js → daemon-client-COJQESU2.js} +4 -2
- package/dist/{daemon-client-ZHO6NG36.js → daemon-client-XXKMJZZ7.js} +1 -1
- package/dist/daemon-main.js +102 -495
- package/dist/index.d.ts +10 -0
- package/dist/index.js +111 -17
- package/dist/plugin-singleton-SYJF6BD6.js +9 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1497,6 +1497,16 @@ declare class PluginInstaller {
|
|
|
1497
1497
|
* @throws If the plugin already exists without `force`, or if installation fails.
|
|
1498
1498
|
*/
|
|
1499
1499
|
install(source: string, options?: InstallOptions): Promise<InstalledPlugin>;
|
|
1500
|
+
/**
|
|
1501
|
+
* Fix missing `../shared/` dependencies after installation.
|
|
1502
|
+
*
|
|
1503
|
+
* Some marketplace/npm packages import from `../shared/` (e.g. ssr-detect.js,
|
|
1504
|
+
* ai-chat-base.ts) but the `shared/` directory is not included in the package.
|
|
1505
|
+
* This method scans the installed plugin's index.ts for such imports and
|
|
1506
|
+
* copies the missing files from the local repository's `.xcli/plugins/shared/`
|
|
1507
|
+
* directory (if available).
|
|
1508
|
+
*/
|
|
1509
|
+
private fixSharedDeps;
|
|
1500
1510
|
installFromMarketplace(slug: string, options?: InstallOptions): Promise<InstalledPlugin>;
|
|
1501
1511
|
installWithMarketplaceFallback(source: string, options?: InstallOptions): Promise<InstalledPlugin>;
|
|
1502
1512
|
/**
|
package/dist/index.js
CHANGED
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
killAllDaemonProcesses,
|
|
26
26
|
startDaemonProcess,
|
|
27
27
|
stopDaemonProcess
|
|
28
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-JPSFUFPG.js";
|
|
29
29
|
import {
|
|
30
30
|
CaptchaDetector,
|
|
31
31
|
HumanInteractionManager,
|
|
@@ -989,10 +989,10 @@ var setCookieCommand = registerCommand({
|
|
|
989
989
|
description: "Set a cookie",
|
|
990
990
|
scope: "page",
|
|
991
991
|
parameters: z8.object({
|
|
992
|
-
name: z8.string(),
|
|
993
|
-
value: z8.string(),
|
|
994
|
-
domain: z8.string().optional(),
|
|
995
|
-
path: z8.string().optional(),
|
|
992
|
+
name: z8.coerce.string(),
|
|
993
|
+
value: z8.coerce.string(),
|
|
994
|
+
domain: z8.coerce.string().optional(),
|
|
995
|
+
path: z8.coerce.string().optional(),
|
|
996
996
|
expires: z8.number().optional(),
|
|
997
997
|
httpOnly: z8.boolean().optional(),
|
|
998
998
|
secure: z8.boolean().optional(),
|
|
@@ -6648,8 +6648,10 @@ var XBrowserPluginLoader = class {
|
|
|
6648
6648
|
const instance = await this.loadPlugin(indexPath, entry.name);
|
|
6649
6649
|
loaded.push(instance);
|
|
6650
6650
|
} catch (err) {
|
|
6651
|
-
|
|
6652
|
-
|
|
6651
|
+
const errMsg2 = err instanceof Error ? err.message : String(err);
|
|
6652
|
+
console.warn(`\u26A0\uFE0F Plugin "${entry.name}" load failed: ${errMsg2}`);
|
|
6653
|
+
if (errMsg2.includes("Cannot find module") && errMsg2.includes("shared/")) {
|
|
6654
|
+
console.warn(` \u{1F4A1} This plugin needs shared/ dependencies. Try: xbrowser plugin install shared`);
|
|
6653
6655
|
}
|
|
6654
6656
|
}
|
|
6655
6657
|
}
|
|
@@ -7368,7 +7370,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
|
|
|
7368
7370
|
params = result.data;
|
|
7369
7371
|
}
|
|
7370
7372
|
if (command.scope !== "cli" && !process.env.XBROWSER_DAEMON_WORKER) {
|
|
7371
|
-
const { forwardExec } = await import("./daemon-client-
|
|
7373
|
+
const { forwardExec } = await import("./daemon-client-XXKMJZZ7.js");
|
|
7372
7374
|
const result = await forwardExec(commandName, params, sessionName, extraOpts?.cdpEndpoint);
|
|
7373
7375
|
if (result) return result;
|
|
7374
7376
|
}
|
|
@@ -8069,9 +8071,12 @@ import {
|
|
|
8069
8071
|
existsSync as existsSync10,
|
|
8070
8072
|
readdirSync as readdirSync2,
|
|
8071
8073
|
mkdirSync as mkdirSync8,
|
|
8072
|
-
rmSync as rmSync6
|
|
8074
|
+
rmSync as rmSync6,
|
|
8075
|
+
copyFileSync,
|
|
8076
|
+
cpSync as cpSync6,
|
|
8077
|
+
readFileSync as readFileSync8
|
|
8073
8078
|
} from "fs";
|
|
8074
|
-
import { resolve as resolve8, basename as basename2 } from "path";
|
|
8079
|
+
import { resolve as resolve8, basename as basename2, dirname as dirname3 } from "path";
|
|
8075
8080
|
import { homedir as homedir7 } from "os";
|
|
8076
8081
|
|
|
8077
8082
|
// src/plugin/install-sources/local.ts
|
|
@@ -8564,28 +8569,103 @@ var PluginInstaller = class {
|
|
|
8564
8569
|
switch (type) {
|
|
8565
8570
|
case "local":
|
|
8566
8571
|
return await installFromLocal(source, name, targetDir).then((r) => {
|
|
8572
|
+
this.fixSharedDeps(targetDir);
|
|
8567
8573
|
ensurePluginDependencies(this.pluginsDir);
|
|
8568
8574
|
return r;
|
|
8569
8575
|
});
|
|
8570
8576
|
case "npm":
|
|
8571
8577
|
return await installFromNpm(resolvedSource, name, targetDir).then((r) => {
|
|
8578
|
+
this.fixSharedDeps(targetDir);
|
|
8572
8579
|
ensurePluginDependencies(this.pluginsDir);
|
|
8573
8580
|
return r;
|
|
8574
8581
|
});
|
|
8575
8582
|
case "git":
|
|
8576
8583
|
return await installFromGit(source, name, targetDir).then((r) => {
|
|
8584
|
+
this.fixSharedDeps(targetDir);
|
|
8577
8585
|
ensurePluginDependencies(this.pluginsDir);
|
|
8578
8586
|
return r;
|
|
8579
8587
|
});
|
|
8580
8588
|
case "url":
|
|
8581
8589
|
return await installFromUrl(source, name, targetDir).then((r) => {
|
|
8590
|
+
this.fixSharedDeps(targetDir);
|
|
8582
8591
|
ensurePluginDependencies(this.pluginsDir);
|
|
8583
8592
|
return r;
|
|
8584
8593
|
});
|
|
8585
8594
|
}
|
|
8586
8595
|
}
|
|
8596
|
+
/**
|
|
8597
|
+
* Fix missing `../shared/` dependencies after installation.
|
|
8598
|
+
*
|
|
8599
|
+
* Some marketplace/npm packages import from `../shared/` (e.g. ssr-detect.js,
|
|
8600
|
+
* ai-chat-base.ts) but the `shared/` directory is not included in the package.
|
|
8601
|
+
* This method scans the installed plugin's index.ts for such imports and
|
|
8602
|
+
* copies the missing files from the local repository's `.xcli/plugins/shared/`
|
|
8603
|
+
* directory (if available).
|
|
8604
|
+
*/
|
|
8605
|
+
fixSharedDeps(pluginDir) {
|
|
8606
|
+
const indexPath = resolve8(pluginDir, "index.ts");
|
|
8607
|
+
if (!existsSync10(indexPath)) return;
|
|
8608
|
+
let content;
|
|
8609
|
+
try {
|
|
8610
|
+
content = readFileSync8(indexPath, "utf8");
|
|
8611
|
+
} catch {
|
|
8612
|
+
return;
|
|
8613
|
+
}
|
|
8614
|
+
const sharedImportRegex = /from\s+['"]\.\.\/shared\/([^'"]+)['"]/g;
|
|
8615
|
+
const missingFiles = [];
|
|
8616
|
+
let match;
|
|
8617
|
+
while ((match = sharedImportRegex.exec(content)) !== null) {
|
|
8618
|
+
missingFiles.push(match[1]);
|
|
8619
|
+
}
|
|
8620
|
+
if (missingFiles.length === 0) return;
|
|
8621
|
+
const sharedDir = resolve8(pluginDir, "..", "shared");
|
|
8622
|
+
const toCopy = [];
|
|
8623
|
+
for (const file of missingFiles) {
|
|
8624
|
+
const targetPath = resolve8(sharedDir, file);
|
|
8625
|
+
if (!existsSync10(targetPath)) {
|
|
8626
|
+
toCopy.push(file);
|
|
8627
|
+
}
|
|
8628
|
+
}
|
|
8629
|
+
if (toCopy.length === 0) return;
|
|
8630
|
+
const repoSharedDirs = [
|
|
8631
|
+
resolve8(process.cwd(), ".xcli/plugins/shared"),
|
|
8632
|
+
resolve8(homedir7(), ".xbrowser/plugins/shared")
|
|
8633
|
+
];
|
|
8634
|
+
let sourceSharedDir = null;
|
|
8635
|
+
for (const dir of repoSharedDirs) {
|
|
8636
|
+
if (existsSync10(dir)) {
|
|
8637
|
+
sourceSharedDir = dir;
|
|
8638
|
+
break;
|
|
8639
|
+
}
|
|
8640
|
+
}
|
|
8641
|
+
if (!sourceSharedDir) {
|
|
8642
|
+
console.warn(`\u26A0\uFE0F Plugin "${basename2(pluginDir)}" imports shared files but they are missing: ${toCopy.join(", ")}`);
|
|
8643
|
+
console.warn(` To fix: install the "shared" plugin or copy .xcli/plugins/shared/ to ~/.xbrowser/plugins/shared/`);
|
|
8644
|
+
return;
|
|
8645
|
+
}
|
|
8646
|
+
mkdirSync8(sharedDir, { recursive: true });
|
|
8647
|
+
for (const file of toCopy) {
|
|
8648
|
+
const src = resolve8(sourceSharedDir, file);
|
|
8649
|
+
const dst = resolve8(sharedDir, file);
|
|
8650
|
+
if (existsSync10(src)) {
|
|
8651
|
+
try {
|
|
8652
|
+
cpSync6(dirname3(src), dirname3(dst), { recursive: true });
|
|
8653
|
+
console.log(`\u2705 Copied shared/${file} for plugin "${basename2(pluginDir)}"`);
|
|
8654
|
+
} catch {
|
|
8655
|
+
try {
|
|
8656
|
+
copyFileSync(src, dst);
|
|
8657
|
+
console.log(`\u2705 Copied shared/${file} for plugin "${basename2(pluginDir)}"`);
|
|
8658
|
+
} catch {
|
|
8659
|
+
console.warn(`\u26A0\uFE0F Could not copy shared/${file}`);
|
|
8660
|
+
}
|
|
8661
|
+
}
|
|
8662
|
+
}
|
|
8663
|
+
}
|
|
8664
|
+
}
|
|
8587
8665
|
async installFromMarketplace(slug, options) {
|
|
8588
8666
|
const result = await installFromMarketplace(this.pluginsDir, slug, options);
|
|
8667
|
+
const targetDir = resolve8(this.pluginsDir, result.name);
|
|
8668
|
+
this.fixSharedDeps(targetDir);
|
|
8589
8669
|
ensurePluginDependencies(this.pluginsDir);
|
|
8590
8670
|
return result;
|
|
8591
8671
|
}
|
|
@@ -10612,6 +10692,18 @@ async function handlePlugin(args, options, mode) {
|
|
|
10612
10692
|
await (await getPluginLoader()).reloadPlugin(result.name);
|
|
10613
10693
|
} catch {
|
|
10614
10694
|
}
|
|
10695
|
+
try {
|
|
10696
|
+
const { daemonPing } = await import("./daemon-client-XXKMJZZ7.js");
|
|
10697
|
+
if (await daemonPing()) {
|
|
10698
|
+
await fetch("http://localhost:9224/rpc", {
|
|
10699
|
+
method: "POST",
|
|
10700
|
+
headers: { "Content-Type": "application/json" },
|
|
10701
|
+
body: JSON.stringify({ method: "plugins:reload", params: {} }),
|
|
10702
|
+
signal: AbortSignal.timeout(5e3)
|
|
10703
|
+
});
|
|
10704
|
+
}
|
|
10705
|
+
} catch {
|
|
10706
|
+
}
|
|
10615
10707
|
outputResult(
|
|
10616
10708
|
{ ok: true, name: result.name, source: result.source, path: result.path },
|
|
10617
10709
|
mode
|
|
@@ -10767,7 +10859,8 @@ async function handleRecord(args, options, mode) {
|
|
|
10767
10859
|
}
|
|
10768
10860
|
case "stop": {
|
|
10769
10861
|
const sessionName = options.session || "default";
|
|
10770
|
-
const
|
|
10862
|
+
const output = options.output || options.o;
|
|
10863
|
+
const result = await forwardRecordStop(sessionName, output);
|
|
10771
10864
|
if (!result.ok) {
|
|
10772
10865
|
outputError(String(result.error || "Failed to stop recording"));
|
|
10773
10866
|
return;
|
|
@@ -10776,6 +10869,7 @@ async function handleRecord(args, options, mode) {
|
|
|
10776
10869
|
ok: true,
|
|
10777
10870
|
message: "Recording stopped.",
|
|
10778
10871
|
sessionName,
|
|
10872
|
+
output: result.output || (output || SessionRecorder.getRecordingsDir(sessionName) + "/recording.json"),
|
|
10779
10873
|
actions: result.actions,
|
|
10780
10874
|
network: result.network,
|
|
10781
10875
|
durationMs: result.durationMs,
|
|
@@ -11639,7 +11733,7 @@ async function handleNetCommand(args, options, mode, sessionName) {
|
|
|
11639
11733
|
|
|
11640
11734
|
// src/cli/test-routes.ts
|
|
11641
11735
|
import { execSync as execSync3 } from "child_process";
|
|
11642
|
-
import { readFileSync as
|
|
11736
|
+
import { readFileSync as readFileSync9 } from "fs";
|
|
11643
11737
|
import { resolve as resolve9 } from "path";
|
|
11644
11738
|
function findPluginPath(plugin) {
|
|
11645
11739
|
const candidates = [
|
|
@@ -11648,7 +11742,7 @@ function findPluginPath(plugin) {
|
|
|
11648
11742
|
];
|
|
11649
11743
|
for (const p of candidates) {
|
|
11650
11744
|
try {
|
|
11651
|
-
|
|
11745
|
+
readFileSync9(p, "utf-8");
|
|
11652
11746
|
return p;
|
|
11653
11747
|
} catch {
|
|
11654
11748
|
}
|
|
@@ -11659,7 +11753,7 @@ function extractSchema(plugin, command) {
|
|
|
11659
11753
|
const pluginPath = findPluginPath(plugin);
|
|
11660
11754
|
let src;
|
|
11661
11755
|
try {
|
|
11662
|
-
src =
|
|
11756
|
+
src = readFileSync9(pluginPath, "utf-8");
|
|
11663
11757
|
} catch {
|
|
11664
11758
|
return null;
|
|
11665
11759
|
}
|
|
@@ -12889,7 +12983,7 @@ Run "xbrowser ${command} ${subCommand} --help" to see available parameters.`
|
|
|
12889
12983
|
}
|
|
12890
12984
|
const needsBrowser = cmdEntry.scope === "page" || cmdEntry.scope === "browser";
|
|
12891
12985
|
if (needsBrowser && !process.env.XBROWSER_DAEMON_WORKER) {
|
|
12892
|
-
const { forwardExec } = await import("./daemon-client-
|
|
12986
|
+
const { forwardExec } = await import("./daemon-client-XXKMJZZ7.js");
|
|
12893
12987
|
const userTimeout = typeof params.timeout === "number" && params.timeout > 0 ? params.timeout * 1e3 + 3e4 : void 0;
|
|
12894
12988
|
const result = await forwardExec(`${command}.${subCommand}`, params, sessionName, cdpEndpoint, userTimeout);
|
|
12895
12989
|
const resultData = result && typeof result === "object" && "data" in result ? result.data : void 0;
|
|
@@ -14437,10 +14531,10 @@ var FileDownloadHandler = class {
|
|
|
14437
14531
|
async handle(ctx) {
|
|
14438
14532
|
const msg = ctx.message;
|
|
14439
14533
|
try {
|
|
14440
|
-
const { readFileSync:
|
|
14534
|
+
const { readFileSync: readFileSync11 } = await import("fs");
|
|
14441
14535
|
const { resolve: resolve10, basename: basename3 } = await import("path");
|
|
14442
14536
|
const targetPath = resolve10(msg.path);
|
|
14443
|
-
const data =
|
|
14537
|
+
const data = readFileSync11(targetPath);
|
|
14444
14538
|
const base64 = data.toString("base64");
|
|
14445
14539
|
const ext = targetPath.split(".").pop()?.toLowerCase() || "";
|
|
14446
14540
|
const mimeMap = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xbrowser/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.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": {
|