@xbrowser/cli 0.15.0 → 0.16.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/cli.js +160 -128
- package/dist/daemon-main.js +20 -47
- package/dist/index.js +158 -126
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -107,7 +107,8 @@ import {
|
|
|
107
107
|
isCommandResult,
|
|
108
108
|
configureArchiveStore,
|
|
109
109
|
appendCommandToArchive,
|
|
110
|
-
checkGuard
|
|
110
|
+
checkGuard,
|
|
111
|
+
PluginStorage
|
|
111
112
|
} from "@dyyz1993/xcli-core";
|
|
112
113
|
|
|
113
114
|
// src/utils/positional-params.ts
|
|
@@ -5962,7 +5963,7 @@ import { join as join2 } from "path";
|
|
|
5962
5963
|
import { execSync as execSync6 } from "child_process";
|
|
5963
5964
|
var SHARED_PLUGIN_DEPENDENCIES = {
|
|
5964
5965
|
"zod": "^3.24.0",
|
|
5965
|
-
"@dyyz1993/xcli-core": "^0.
|
|
5966
|
+
"@dyyz1993/xcli-core": "^0.12.1"
|
|
5966
5967
|
};
|
|
5967
5968
|
function ensurePluginDependencies(pluginsDir) {
|
|
5968
5969
|
const zodPath = join2(pluginsDir, "node_modules", "zod");
|
|
@@ -6546,45 +6547,13 @@ async function loadHooks() {
|
|
|
6546
6547
|
// src/executor.ts
|
|
6547
6548
|
import { homedir as homedir3 } from "os";
|
|
6548
6549
|
import { join as join3 } from "path";
|
|
6549
|
-
import { existsSync as existsSync6, readFileSync as readFileSync9, writeFileSync as writeFileSync4, mkdirSync as mkdirSync3 } from "fs";
|
|
6550
6550
|
var NAVIGATION_COMMANDS = /* @__PURE__ */ new Set(["goto", "back", "forward", "refresh"]);
|
|
6551
6551
|
var snapshotHintShown = /* @__PURE__ */ new WeakSet();
|
|
6552
6552
|
var STORAGE_DIR = join3(homedir3(), ".xbrowser", "storage");
|
|
6553
6553
|
var storageCache = /* @__PURE__ */ new Map();
|
|
6554
6554
|
function getPluginStorage(pluginName) {
|
|
6555
6555
|
if (!storageCache.has(pluginName)) {
|
|
6556
|
-
|
|
6557
|
-
let data = {};
|
|
6558
|
-
const load3 = () => {
|
|
6559
|
-
if (existsSync6(filePath)) {
|
|
6560
|
-
try {
|
|
6561
|
-
data = JSON.parse(readFileSync9(filePath, "utf-8"));
|
|
6562
|
-
} catch {
|
|
6563
|
-
data = {};
|
|
6564
|
-
}
|
|
6565
|
-
}
|
|
6566
|
-
};
|
|
6567
|
-
const save = () => {
|
|
6568
|
-
mkdirSync3(STORAGE_DIR, { recursive: true });
|
|
6569
|
-
writeFileSync4(filePath, JSON.stringify(data, null, 2), "utf-8");
|
|
6570
|
-
};
|
|
6571
|
-
load3();
|
|
6572
|
-
storageCache.set(pluginName, {
|
|
6573
|
-
get: async (key) => data[key] ?? null,
|
|
6574
|
-
set: async (key, value) => {
|
|
6575
|
-
data[key] = value;
|
|
6576
|
-
save();
|
|
6577
|
-
},
|
|
6578
|
-
delete: async (key) => {
|
|
6579
|
-
delete data[key];
|
|
6580
|
-
save();
|
|
6581
|
-
},
|
|
6582
|
-
clear: async () => {
|
|
6583
|
-
data = {};
|
|
6584
|
-
save();
|
|
6585
|
-
},
|
|
6586
|
-
keys: async () => Object.keys(data)
|
|
6587
|
-
});
|
|
6556
|
+
storageCache.set(pluginName, new PluginStorage(pluginName, STORAGE_DIR));
|
|
6588
6557
|
}
|
|
6589
6558
|
return storageCache.get(pluginName);
|
|
6590
6559
|
}
|
|
@@ -6684,6 +6653,10 @@ async function executeCommand(commandName, params, sessionName = "default", extr
|
|
|
6684
6653
|
session = await createSession(sessionName, params.url, {
|
|
6685
6654
|
cdpEndpoint: extraOpts?.cdpEndpoint
|
|
6686
6655
|
});
|
|
6656
|
+
} else if (command.scope === "browser") {
|
|
6657
|
+
session = await createSession(sessionName, void 0, {
|
|
6658
|
+
cdpEndpoint: extraOpts?.cdpEndpoint
|
|
6659
|
+
});
|
|
6687
6660
|
} else if (command.scope !== "project") {
|
|
6688
6661
|
return errorResult(
|
|
6689
6662
|
`Session '${sessionName}' not found. Run "xbrowser session open <url>" first.`
|
|
@@ -7333,26 +7306,26 @@ var configBuiltin = {
|
|
|
7333
7306
|
|
|
7334
7307
|
// src/plugin/installer.ts
|
|
7335
7308
|
import {
|
|
7336
|
-
existsSync as
|
|
7309
|
+
existsSync as existsSync12,
|
|
7337
7310
|
readdirSync as readdirSync3,
|
|
7338
|
-
mkdirSync as
|
|
7311
|
+
mkdirSync as mkdirSync7,
|
|
7339
7312
|
rmSync as rmSync7
|
|
7340
7313
|
} from "fs";
|
|
7341
7314
|
import { resolve as resolve15, basename as basename2 } from "path";
|
|
7342
7315
|
import { homedir as homedir4 } from "os";
|
|
7343
7316
|
|
|
7344
7317
|
// src/plugin/install-sources/local.ts
|
|
7345
|
-
import { existsSync as
|
|
7318
|
+
import { existsSync as existsSync7, cpSync as cpSync2, rmSync as rmSync2 } from "fs";
|
|
7346
7319
|
import { resolve as resolve10 } from "path";
|
|
7347
7320
|
|
|
7348
7321
|
// src/plugin/install-utils.ts
|
|
7349
7322
|
import {
|
|
7350
|
-
existsSync as
|
|
7323
|
+
existsSync as existsSync6,
|
|
7351
7324
|
readdirSync as readdirSync2,
|
|
7352
7325
|
cpSync,
|
|
7353
7326
|
rmSync,
|
|
7354
|
-
mkdirSync as
|
|
7355
|
-
readFileSync as
|
|
7327
|
+
mkdirSync as mkdirSync3,
|
|
7328
|
+
readFileSync as readFileSync9,
|
|
7356
7329
|
createWriteStream
|
|
7357
7330
|
} from "fs";
|
|
7358
7331
|
import { resolve as resolve9 } from "path";
|
|
@@ -7423,7 +7396,7 @@ async function downloadToFile(url, destPath) {
|
|
|
7423
7396
|
await pipeline(nodeStream, createWriteStream(destPath));
|
|
7424
7397
|
}
|
|
7425
7398
|
function extractTarGz(tarballPath, targetDir) {
|
|
7426
|
-
|
|
7399
|
+
mkdirSync3(targetDir, { recursive: true });
|
|
7427
7400
|
execSync7(`tar -xzf "${tarballPath}" -C "${targetDir}"`, { stdio: "pipe" });
|
|
7428
7401
|
}
|
|
7429
7402
|
function flattenPackageRoot(targetDir) {
|
|
@@ -7444,18 +7417,18 @@ function flattenPackageRoot(targetDir) {
|
|
|
7444
7417
|
async function verifyPlugin(dir) {
|
|
7445
7418
|
const warnings = [];
|
|
7446
7419
|
const indexPath = resolve9(dir, "index.ts");
|
|
7447
|
-
if (!
|
|
7420
|
+
if (!existsSync6(indexPath)) {
|
|
7448
7421
|
const indexJs = resolve9(dir, "index.js");
|
|
7449
|
-
if (!
|
|
7422
|
+
if (!existsSync6(indexJs)) {
|
|
7450
7423
|
return { valid: false, error: "No index.ts or index.js entry point found", warnings };
|
|
7451
7424
|
}
|
|
7452
7425
|
}
|
|
7453
7426
|
const pkgPath = resolve9(dir, "package.json");
|
|
7454
|
-
if (!
|
|
7427
|
+
if (!existsSync6(pkgPath)) {
|
|
7455
7428
|
warnings.push("No package.json found");
|
|
7456
7429
|
} else {
|
|
7457
7430
|
try {
|
|
7458
|
-
const pkg2 = JSON.parse(
|
|
7431
|
+
const pkg2 = JSON.parse(readFileSync9(pkgPath, "utf-8"));
|
|
7459
7432
|
if (!pkg2.xbrowser) {
|
|
7460
7433
|
warnings.push("No xbrowser metadata in package.json");
|
|
7461
7434
|
}
|
|
@@ -7475,7 +7448,7 @@ function safeCleanup(dir) {
|
|
|
7475
7448
|
// src/plugin/install-sources/local.ts
|
|
7476
7449
|
async function installFromLocal(source, name, targetDir) {
|
|
7477
7450
|
const srcPath = resolve10(source);
|
|
7478
|
-
if (!
|
|
7451
|
+
if (!existsSync7(srcPath)) {
|
|
7479
7452
|
throw new Error(`Local path does not exist: ${srcPath}`);
|
|
7480
7453
|
}
|
|
7481
7454
|
const tmpTarget = `${targetDir}-tmp-${Date.now()}`;
|
|
@@ -7488,7 +7461,7 @@ async function installFromLocal(source, name, targetDir) {
|
|
|
7488
7461
|
safeCleanup(tmpTarget);
|
|
7489
7462
|
throw new Error(`Invalid plugin: ${verify.error}`);
|
|
7490
7463
|
}
|
|
7491
|
-
if (
|
|
7464
|
+
if (existsSync7(targetDir)) {
|
|
7492
7465
|
rmSync2(targetDir, { recursive: true, force: true });
|
|
7493
7466
|
}
|
|
7494
7467
|
cpSync2(tmpTarget, targetDir, { recursive: true, force: true });
|
|
@@ -7508,7 +7481,7 @@ async function installFromLocal(source, name, targetDir) {
|
|
|
7508
7481
|
}
|
|
7509
7482
|
|
|
7510
7483
|
// src/plugin/install-sources/npm.ts
|
|
7511
|
-
import { existsSync as
|
|
7484
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync4, readFileSync as readFileSync10, writeFileSync as writeFileSync4, rmSync as rmSync3, cpSync as cpSync3 } from "fs";
|
|
7512
7485
|
import { resolve as resolve11, join as join4 } from "path";
|
|
7513
7486
|
import { tmpdir } from "os";
|
|
7514
7487
|
async function installFromNpm(packageName, name, targetDir) {
|
|
@@ -7529,7 +7502,7 @@ async function installFromNpm(packageName, name, targetDir) {
|
|
|
7529
7502
|
}
|
|
7530
7503
|
const tarballUrl = versionMeta.dist.tarball;
|
|
7531
7504
|
const tmpDir = join4(tmpdir(), `xbrowser-npm-${Date.now()}`);
|
|
7532
|
-
|
|
7505
|
+
mkdirSync4(tmpDir, { recursive: true });
|
|
7533
7506
|
let warnings = [];
|
|
7534
7507
|
try {
|
|
7535
7508
|
const tarballPath = join4(tmpDir, `${name}.tgz`);
|
|
@@ -7542,16 +7515,16 @@ async function installFromNpm(packageName, name, targetDir) {
|
|
|
7542
7515
|
if (!verify.valid) {
|
|
7543
7516
|
throw new Error(`Invalid npm plugin: ${verify.error}`);
|
|
7544
7517
|
}
|
|
7545
|
-
if (
|
|
7518
|
+
if (existsSync8(targetDir)) {
|
|
7546
7519
|
rmSync3(targetDir, { recursive: true, force: true });
|
|
7547
7520
|
}
|
|
7548
7521
|
cpSync3(extractDir, targetDir, { recursive: true, force: true });
|
|
7549
7522
|
const pkgPath = resolve11(targetDir, "package.json");
|
|
7550
|
-
if (
|
|
7551
|
-
const pkg2 = JSON.parse(
|
|
7523
|
+
if (existsSync8(pkgPath)) {
|
|
7524
|
+
const pkg2 = JSON.parse(readFileSync10(pkgPath, "utf-8"));
|
|
7552
7525
|
if (!pkg2._npmSource) {
|
|
7553
7526
|
pkg2._npmSource = { name: packageName, version: latestVersion };
|
|
7554
|
-
|
|
7527
|
+
writeFileSync4(pkgPath, JSON.stringify(pkg2, null, 2));
|
|
7555
7528
|
}
|
|
7556
7529
|
}
|
|
7557
7530
|
} finally {
|
|
@@ -7568,7 +7541,7 @@ async function installFromNpm(packageName, name, targetDir) {
|
|
|
7568
7541
|
}
|
|
7569
7542
|
|
|
7570
7543
|
// src/plugin/install-sources/git.ts
|
|
7571
|
-
import { existsSync as
|
|
7544
|
+
import { existsSync as existsSync9, readFileSync as readFileSync11, writeFileSync as writeFileSync5, rmSync as rmSync4, cpSync as cpSync4 } from "fs";
|
|
7572
7545
|
import { resolve as resolve12, join as join5 } from "path";
|
|
7573
7546
|
import { tmpdir as tmpdir2 } from "os";
|
|
7574
7547
|
import { execSync as execSync8 } from "child_process";
|
|
@@ -7582,17 +7555,17 @@ async function installFromGit(gitUrl, name, targetDir) {
|
|
|
7582
7555
|
if (!verify.valid) {
|
|
7583
7556
|
throw new Error(`Invalid git plugin: ${verify.error}`);
|
|
7584
7557
|
}
|
|
7585
|
-
if (
|
|
7558
|
+
if (existsSync9(targetDir)) {
|
|
7586
7559
|
rmSync4(targetDir, { recursive: true, force: true });
|
|
7587
7560
|
}
|
|
7588
7561
|
cpSync4(tmpDir, targetDir, { recursive: true, force: true });
|
|
7589
7562
|
rmSync4(resolve12(targetDir, ".git"), { recursive: true, force: true });
|
|
7590
7563
|
const pkgPath = resolve12(targetDir, "package.json");
|
|
7591
|
-
if (
|
|
7592
|
-
const pkg2 = JSON.parse(
|
|
7564
|
+
if (existsSync9(pkgPath)) {
|
|
7565
|
+
const pkg2 = JSON.parse(readFileSync11(pkgPath, "utf-8"));
|
|
7593
7566
|
if (!pkg2._gitSource) {
|
|
7594
7567
|
pkg2._gitSource = { url: gitUrl };
|
|
7595
|
-
|
|
7568
|
+
writeFileSync5(pkgPath, JSON.stringify(pkg2, null, 2));
|
|
7596
7569
|
}
|
|
7597
7570
|
}
|
|
7598
7571
|
} finally {
|
|
@@ -7609,12 +7582,12 @@ async function installFromGit(gitUrl, name, targetDir) {
|
|
|
7609
7582
|
}
|
|
7610
7583
|
|
|
7611
7584
|
// src/plugin/install-sources/url.ts
|
|
7612
|
-
import { existsSync as
|
|
7585
|
+
import { existsSync as existsSync10, readFileSync as readFileSync12, writeFileSync as writeFileSync6, mkdirSync as mkdirSync5, rmSync as rmSync5, cpSync as cpSync5 } from "fs";
|
|
7613
7586
|
import { resolve as resolve13, join as join6, basename } from "path";
|
|
7614
7587
|
import { tmpdir as tmpdir3 } from "os";
|
|
7615
7588
|
async function installFromUrl(url, name, targetDir) {
|
|
7616
7589
|
const tmpDir = join6(tmpdir3(), `xbrowser-url-${Date.now()}`);
|
|
7617
|
-
|
|
7590
|
+
mkdirSync5(tmpDir, { recursive: true });
|
|
7618
7591
|
let warnings = [];
|
|
7619
7592
|
try {
|
|
7620
7593
|
const fileName = basename(new URL(url).pathname) || "plugin.tar.gz";
|
|
@@ -7628,16 +7601,16 @@ async function installFromUrl(url, name, targetDir) {
|
|
|
7628
7601
|
if (!verify.valid) {
|
|
7629
7602
|
throw new Error(`Invalid plugin from URL: ${verify.error}`);
|
|
7630
7603
|
}
|
|
7631
|
-
if (
|
|
7604
|
+
if (existsSync10(targetDir)) {
|
|
7632
7605
|
rmSync5(targetDir, { recursive: true, force: true });
|
|
7633
7606
|
}
|
|
7634
7607
|
cpSync5(extractDir, targetDir, { recursive: true, force: true });
|
|
7635
7608
|
const pkgPath = resolve13(targetDir, "package.json");
|
|
7636
|
-
if (
|
|
7637
|
-
const pkg2 = JSON.parse(
|
|
7609
|
+
if (existsSync10(pkgPath)) {
|
|
7610
|
+
const pkg2 = JSON.parse(readFileSync12(pkgPath, "utf-8"));
|
|
7638
7611
|
if (!pkg2._urlSource) {
|
|
7639
7612
|
pkg2._urlSource = { url };
|
|
7640
|
-
|
|
7613
|
+
writeFileSync6(pkgPath, JSON.stringify(pkg2, null, 2));
|
|
7641
7614
|
}
|
|
7642
7615
|
}
|
|
7643
7616
|
} finally {
|
|
@@ -7655,10 +7628,10 @@ async function installFromUrl(url, name, targetDir) {
|
|
|
7655
7628
|
|
|
7656
7629
|
// src/plugin/install-sources/marketplace.ts
|
|
7657
7630
|
import {
|
|
7658
|
-
existsSync as
|
|
7659
|
-
mkdirSync as
|
|
7660
|
-
writeFileSync as
|
|
7661
|
-
readFileSync as
|
|
7631
|
+
existsSync as existsSync11,
|
|
7632
|
+
mkdirSync as mkdirSync6,
|
|
7633
|
+
writeFileSync as writeFileSync7,
|
|
7634
|
+
readFileSync as readFileSync13,
|
|
7662
7635
|
rmSync as rmSync6,
|
|
7663
7636
|
cpSync as cpSync6
|
|
7664
7637
|
} from "fs";
|
|
@@ -7680,12 +7653,12 @@ async function installFromMarketplace(pluginsDir, slug, options) {
|
|
|
7680
7653
|
const plugin = detailData.data;
|
|
7681
7654
|
const name = options?.name || String(plugin.slug || slug);
|
|
7682
7655
|
const targetDir = resolve14(pluginsDir, name);
|
|
7683
|
-
if (
|
|
7656
|
+
if (existsSync11(targetDir) && !options?.force) {
|
|
7684
7657
|
throw new Error(`Plugin "${name}" already exists. Use --force to overwrite.`);
|
|
7685
7658
|
}
|
|
7686
|
-
|
|
7659
|
+
mkdirSync6(targetDir, { recursive: true });
|
|
7687
7660
|
const tmpDir = join7(tmpdir4(), `xbrowser-marketplace-${Date.now()}`);
|
|
7688
|
-
|
|
7661
|
+
mkdirSync6(tmpDir, { recursive: true });
|
|
7689
7662
|
const realSlug = String(plugin.slug || slug);
|
|
7690
7663
|
try {
|
|
7691
7664
|
await downloadAndExtractMarketplaceTarball(baseUrl, realSlug, tmpDir, targetDir);
|
|
@@ -7715,14 +7688,14 @@ function isManifestArray(data) {
|
|
|
7715
7688
|
return Array.isArray(data) && data.length > 0 && typeof data[0].path === "string" && typeof data[0].content === "string";
|
|
7716
7689
|
}
|
|
7717
7690
|
function extractManifestToDir(manifest, targetDir) {
|
|
7718
|
-
if (
|
|
7691
|
+
if (existsSync11(targetDir)) {
|
|
7719
7692
|
rmSync6(targetDir, { recursive: true, force: true });
|
|
7720
7693
|
}
|
|
7721
|
-
|
|
7694
|
+
mkdirSync6(targetDir, { recursive: true });
|
|
7722
7695
|
for (const file of manifest) {
|
|
7723
7696
|
const filePath = resolve14(targetDir, file.path);
|
|
7724
|
-
|
|
7725
|
-
|
|
7697
|
+
mkdirSync6(dirname2(filePath), { recursive: true });
|
|
7698
|
+
writeFileSync7(filePath, Buffer.from(file.content, "base64"));
|
|
7726
7699
|
}
|
|
7727
7700
|
}
|
|
7728
7701
|
function tryParseAsGzippedManifest(buffer) {
|
|
@@ -7749,7 +7722,7 @@ async function downloadAndExtractMarketplaceTarball(baseUrl, slug, tmpDir, targe
|
|
|
7749
7722
|
const redirectUrl = tarballRes.headers.get("location");
|
|
7750
7723
|
const tarballPath = join7(tmpDir, `${slug}.tar.gz`);
|
|
7751
7724
|
await downloadToFile(redirectUrl, tarballPath);
|
|
7752
|
-
const buffer =
|
|
7725
|
+
const buffer = readFileSync13(tarballPath);
|
|
7753
7726
|
const manifest = tryParseAsGzippedManifest(buffer);
|
|
7754
7727
|
if (manifest) {
|
|
7755
7728
|
extractManifestToDir(manifest, targetDir);
|
|
@@ -7758,7 +7731,7 @@ async function downloadAndExtractMarketplaceTarball(baseUrl, slug, tmpDir, targe
|
|
|
7758
7731
|
const extractDir = join7(tmpDir, "extracted");
|
|
7759
7732
|
extractTarGz(tarballPath, extractDir);
|
|
7760
7733
|
flattenPackageRoot(extractDir);
|
|
7761
|
-
if (
|
|
7734
|
+
if (existsSync11(targetDir)) {
|
|
7762
7735
|
rmSync6(targetDir, { recursive: true, force: true });
|
|
7763
7736
|
}
|
|
7764
7737
|
cpSync6(extractDir, targetDir, { recursive: true, force: true });
|
|
@@ -7770,12 +7743,12 @@ async function downloadAndExtractMarketplaceTarball(baseUrl, slug, tmpDir, targe
|
|
|
7770
7743
|
return;
|
|
7771
7744
|
}
|
|
7772
7745
|
const tarballPath = join7(tmpDir, `${slug}.tar.gz`);
|
|
7773
|
-
|
|
7746
|
+
writeFileSync7(tarballPath, buffer);
|
|
7774
7747
|
try {
|
|
7775
7748
|
const extractDir = join7(tmpDir, "extracted");
|
|
7776
7749
|
extractTarGz(tarballPath, extractDir);
|
|
7777
7750
|
flattenPackageRoot(extractDir);
|
|
7778
|
-
if (
|
|
7751
|
+
if (existsSync11(targetDir)) {
|
|
7779
7752
|
rmSync6(targetDir, { recursive: true, force: true });
|
|
7780
7753
|
}
|
|
7781
7754
|
cpSync6(extractDir, targetDir, { recursive: true, force: true });
|
|
@@ -7811,24 +7784,24 @@ function writeMarketplacePackageJson(plugin, slug, name, baseUrl, targetDir) {
|
|
|
7811
7784
|
}
|
|
7812
7785
|
};
|
|
7813
7786
|
const pkgPath = resolve14(targetDir, "package.json");
|
|
7814
|
-
if (!
|
|
7815
|
-
|
|
7787
|
+
if (!existsSync11(pkgPath)) {
|
|
7788
|
+
writeFileSync7(pkgPath, JSON.stringify(packageJson, null, 2));
|
|
7816
7789
|
} else {
|
|
7817
7790
|
try {
|
|
7818
|
-
const existing = JSON.parse(
|
|
7791
|
+
const existing = JSON.parse(readFileSync13(pkgPath, "utf-8"));
|
|
7819
7792
|
const merged = {
|
|
7820
7793
|
...existing,
|
|
7821
7794
|
xbrowser: { ...existing.xbrowser, ...packageJson.xbrowser },
|
|
7822
7795
|
_marketplace: packageJson._marketplace
|
|
7823
7796
|
};
|
|
7824
|
-
|
|
7797
|
+
writeFileSync7(pkgPath, JSON.stringify(merged, null, 2));
|
|
7825
7798
|
} catch {
|
|
7826
|
-
|
|
7799
|
+
writeFileSync7(pkgPath, JSON.stringify(packageJson, null, 2));
|
|
7827
7800
|
}
|
|
7828
7801
|
}
|
|
7829
7802
|
}
|
|
7830
7803
|
function ensureIndexFile(plugin, name, targetDir) {
|
|
7831
|
-
if (!
|
|
7804
|
+
if (!existsSync11(resolve14(targetDir, "index.ts")) && !existsSync11(resolve14(targetDir, "index.js"))) {
|
|
7832
7805
|
const commands = plugin.commands || [];
|
|
7833
7806
|
const commandHandlers = commands.length > 0 ? commands.map((cmd) => {
|
|
7834
7807
|
return [
|
|
@@ -7843,7 +7816,7 @@ function ensureIndexFile(plugin, name, targetDir) {
|
|
|
7843
7816
|
` handler: async () => ({ data: { message: 'Hello from ${name}!' }, tips: [] }),`,
|
|
7844
7817
|
` });`
|
|
7845
7818
|
].join("\n");
|
|
7846
|
-
|
|
7819
|
+
writeFileSync7(
|
|
7847
7820
|
resolve14(targetDir, "index.ts"),
|
|
7848
7821
|
[
|
|
7849
7822
|
`import type { XCLIAPI } from '@dyyz1993/xcli-core';`,
|
|
@@ -7882,10 +7855,10 @@ var PluginInstaller = class {
|
|
|
7882
7855
|
const type = this.detectSourceType(source);
|
|
7883
7856
|
const name = options?.name || this.deriveName(source, type);
|
|
7884
7857
|
const targetDir = resolve15(this.pluginsDir, name);
|
|
7885
|
-
if (
|
|
7858
|
+
if (existsSync12(targetDir) && !options?.force) {
|
|
7886
7859
|
throw new Error(`Plugin "${name}" already exists. Use --force to overwrite.`);
|
|
7887
7860
|
}
|
|
7888
|
-
|
|
7861
|
+
mkdirSync7(targetDir, { recursive: true });
|
|
7889
7862
|
const resolvedSource = type === "npm" ? await resolveNpmPackageWithFallback(source) : source;
|
|
7890
7863
|
switch (type) {
|
|
7891
7864
|
case "local":
|
|
@@ -7943,7 +7916,7 @@ var PluginInstaller = class {
|
|
|
7943
7916
|
*/
|
|
7944
7917
|
async uninstall(name) {
|
|
7945
7918
|
const targetDir = resolve15(this.pluginsDir, name);
|
|
7946
|
-
if (!
|
|
7919
|
+
if (!existsSync12(targetDir)) {
|
|
7947
7920
|
throw new Error(`Plugin "${name}" not found`);
|
|
7948
7921
|
}
|
|
7949
7922
|
rmSync7(targetDir, { recursive: true, force: true });
|
|
@@ -7954,7 +7927,7 @@ var PluginInstaller = class {
|
|
|
7954
7927
|
* @returns Array of installed plugin information.
|
|
7955
7928
|
*/
|
|
7956
7929
|
async list(_options) {
|
|
7957
|
-
if (!
|
|
7930
|
+
if (!existsSync12(this.pluginsDir)) return [];
|
|
7958
7931
|
const entries = readdirSync3(this.pluginsDir, { withFileTypes: true });
|
|
7959
7932
|
const plugins = [];
|
|
7960
7933
|
for (const entry of entries) {
|
|
@@ -7962,7 +7935,7 @@ var PluginInstaller = class {
|
|
|
7962
7935
|
const pluginPath = resolve15(this.pluginsDir, entry.name);
|
|
7963
7936
|
const indexPath = resolve15(pluginPath, "index.ts");
|
|
7964
7937
|
const indexJsPath = resolve15(pluginPath, "index.js");
|
|
7965
|
-
if (!
|
|
7938
|
+
if (!existsSync12(indexPath) && !existsSync12(indexJsPath)) continue;
|
|
7966
7939
|
const metadata = PluginMetadataParser.parseFromPackageJson(pluginPath);
|
|
7967
7940
|
let source = "local";
|
|
7968
7941
|
const pkg2 = readJsonFile(resolve15(pluginPath, "package.json"), {});
|
|
@@ -7989,10 +7962,10 @@ var PluginInstaller = class {
|
|
|
7989
7962
|
}
|
|
7990
7963
|
if (source.startsWith("file://")) {
|
|
7991
7964
|
const filePath = decodeURIComponent(new URL(source).pathname);
|
|
7992
|
-
if (
|
|
7965
|
+
if (existsSync12(filePath)) return "url";
|
|
7993
7966
|
}
|
|
7994
7967
|
if (source.endsWith(".git") || source.includes("github.com/")) return "git";
|
|
7995
|
-
if (
|
|
7968
|
+
if (existsSync12(resolve15(source))) return "local";
|
|
7996
7969
|
return "npm";
|
|
7997
7970
|
}
|
|
7998
7971
|
deriveName(source, type) {
|
|
@@ -10021,7 +9994,7 @@ async function handleFilter(args, _mode) {
|
|
|
10021
9994
|
|
|
10022
9995
|
// src/stdin.ts
|
|
10023
9996
|
import { createInterface } from "readline";
|
|
10024
|
-
import { readFileSync as
|
|
9997
|
+
import { readFileSync as readFileSync14 } from "fs";
|
|
10025
9998
|
async function readStdin2() {
|
|
10026
9999
|
if (process.stdin.isTTY) return [];
|
|
10027
10000
|
const lines = [];
|
|
@@ -10035,7 +10008,7 @@ async function readStdin2() {
|
|
|
10035
10008
|
return lines;
|
|
10036
10009
|
}
|
|
10037
10010
|
function readCommandFile(filePath) {
|
|
10038
|
-
const content =
|
|
10011
|
+
const content = readFileSync14(filePath, "utf-8");
|
|
10039
10012
|
return content.split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("#"));
|
|
10040
10013
|
}
|
|
10041
10014
|
|
|
@@ -10932,6 +10905,22 @@ var HTTPServer = class {
|
|
|
10932
10905
|
};
|
|
10933
10906
|
|
|
10934
10907
|
// src/router.ts
|
|
10908
|
+
var KNOWN_GLOBAL_OPTIONS = /* @__PURE__ */ new Set([
|
|
10909
|
+
"json",
|
|
10910
|
+
"yaml",
|
|
10911
|
+
"session",
|
|
10912
|
+
"cdp",
|
|
10913
|
+
"cdp-endpoint",
|
|
10914
|
+
"version",
|
|
10915
|
+
"v",
|
|
10916
|
+
"help",
|
|
10917
|
+
"h",
|
|
10918
|
+
"target",
|
|
10919
|
+
"port",
|
|
10920
|
+
"token",
|
|
10921
|
+
"timeout",
|
|
10922
|
+
"headless"
|
|
10923
|
+
]);
|
|
10935
10924
|
function showCommandHelp(siteName, cmd, siteConfig, mode) {
|
|
10936
10925
|
const c = cmd;
|
|
10937
10926
|
if (mode === "json") {
|
|
@@ -11056,8 +11045,23 @@ async function handleEvalMode(argv) {
|
|
|
11056
11045
|
}
|
|
11057
11046
|
async function handleChainInput(input, argv) {
|
|
11058
11047
|
const cdpEndpoint = argv ? extractCdpFromArgv(argv) : void 0;
|
|
11048
|
+
const jsonMode = argv ? argv.includes("--json") || argv.includes("-j") : false;
|
|
11059
11049
|
const chainResult = await executeChain(input, { cdpEndpoint });
|
|
11060
|
-
|
|
11050
|
+
if (jsonMode) {
|
|
11051
|
+
const output = {
|
|
11052
|
+
success: chainResult.success,
|
|
11053
|
+
steps: chainResult.steps.map((s) => ({
|
|
11054
|
+
command: s.raw,
|
|
11055
|
+
success: s.success,
|
|
11056
|
+
data: s.data,
|
|
11057
|
+
duration: s.duration,
|
|
11058
|
+
...s.hookOutputs?.length ? { hooks: s.hookOutputs } : {}
|
|
11059
|
+
}))
|
|
11060
|
+
};
|
|
11061
|
+
console.log(JSON.stringify(output, null, 2));
|
|
11062
|
+
} else {
|
|
11063
|
+
printChainResult(chainResult);
|
|
11064
|
+
}
|
|
11061
11065
|
if (!chainResult.success) throw new Error("Command failed");
|
|
11062
11066
|
}
|
|
11063
11067
|
async function routeCommand(argv, stdinCommands) {
|
|
@@ -11080,6 +11084,8 @@ async function routeCommand(argv, stdinCommands) {
|
|
|
11080
11084
|
}
|
|
11081
11085
|
const parsed = parseArgs(argv);
|
|
11082
11086
|
const { positional, options } = parsed;
|
|
11087
|
+
const command = positional[0];
|
|
11088
|
+
const cmdArgs = positional.slice(1);
|
|
11083
11089
|
const mode = options.json ? "json" : options.yaml ? "yaml" : "text";
|
|
11084
11090
|
const sessionName = options.session || process.env.XBROWSER_SESSION || "default";
|
|
11085
11091
|
const cdpEndpoint = options.cdp;
|
|
@@ -11091,8 +11097,6 @@ async function routeCommand(argv, stdinCommands) {
|
|
|
11091
11097
|
showMainHelp();
|
|
11092
11098
|
return;
|
|
11093
11099
|
}
|
|
11094
|
-
const command = positional[0];
|
|
11095
|
-
const cmdArgs = positional.slice(1);
|
|
11096
11100
|
if ((options.help || options.h) && positional.length > 0) {
|
|
11097
11101
|
const loader = await getPluginLoader();
|
|
11098
11102
|
const internalLoader = loader.getCore().loader;
|
|
@@ -11307,10 +11311,30 @@ Run "xbrowser ${command} --help" to see available commands.`
|
|
|
11307
11311
|
const subCmdIdx = pluginNameIdx >= 0 ? argv.indexOf(subCommand, pluginNameIdx + 1) : -1;
|
|
11308
11312
|
const rawPluginArgs = subCmdIdx >= 0 ? argv.slice(subCmdIdx + 1) : [];
|
|
11309
11313
|
const params = parsePluginParams(rawPluginArgs, cmdEntry.parameters);
|
|
11314
|
+
if (cmdEntry.parameters) {
|
|
11315
|
+
const schemaAny = cmdEntry.parameters;
|
|
11316
|
+
const def = schemaAny._def;
|
|
11317
|
+
const shapeOrFn = def?.shape ?? schemaAny.shape;
|
|
11318
|
+
const shapeObj = typeof shapeOrFn === "function" ? shapeOrFn() : shapeOrFn;
|
|
11319
|
+
if (shapeObj && typeof shapeObj === "object") {
|
|
11320
|
+
const knownKeys = new Set(Object.keys(shapeObj));
|
|
11321
|
+
knownKeys.add("_target");
|
|
11322
|
+
for (const gk of KNOWN_GLOBAL_OPTIONS) knownKeys.add(gk.replace(/-([a-z])/g, (_, c) => c.toUpperCase()));
|
|
11323
|
+
const unknownKeys = Object.keys(params).filter((k) => !knownKeys.has(k));
|
|
11324
|
+
if (unknownKeys.length > 0) {
|
|
11325
|
+
const unknown = unknownKeys.map((k) => `--${k.replace(/([A-Z])/g, "-$1").toLowerCase()}`).join(", ");
|
|
11326
|
+
outputError(
|
|
11327
|
+
`Unknown parameter: ${unknown}
|
|
11328
|
+
Run "xbrowser ${command} ${subCommand} --help" to see available parameters.`
|
|
11329
|
+
);
|
|
11330
|
+
return;
|
|
11331
|
+
}
|
|
11332
|
+
}
|
|
11333
|
+
}
|
|
11310
11334
|
if (options.target && !params._target) {
|
|
11311
11335
|
params._target = options.target;
|
|
11312
11336
|
}
|
|
11313
|
-
const needsBrowser = cmdEntry.scope === "page";
|
|
11337
|
+
const needsBrowser = cmdEntry.scope === "page" || cmdEntry.scope === "browser";
|
|
11314
11338
|
if (needsBrowser && !process.env.XBROWSER_DAEMON_WORKER) {
|
|
11315
11339
|
const { forwardExec } = await import("./daemon-client-3IJD6X4B.js");
|
|
11316
11340
|
const userTimeout = typeof params.timeout === "number" && params.timeout > 0 ? params.timeout * 1e3 + 3e4 : void 0;
|
|
@@ -11360,38 +11384,46 @@ Run "xbrowser ${command} --help" to see available commands.`
|
|
|
11360
11384
|
}
|
|
11361
11385
|
};
|
|
11362
11386
|
try {
|
|
11387
|
+
const cmdStart = Date.now();
|
|
11388
|
+
const cmdHooks = await loadHooks();
|
|
11389
|
+
if (cmdHooks.length > 0 && session?.page) {
|
|
11390
|
+
await Promise.all(cmdHooks.map((h) => h.onBeforeCommand?.({ page: session.page, command: `${command} ${subCommand}`, params })));
|
|
11391
|
+
}
|
|
11363
11392
|
const result = await cmdEntry.handler(params, ctx);
|
|
11393
|
+
const hookOutputs = [];
|
|
11394
|
+
if (cmdHooks.length > 0 && session?.page) {
|
|
11395
|
+
for (const h of cmdHooks) {
|
|
11396
|
+
const output = await h.onAfterCommand?.({ page: session.page, command: `${command} ${subCommand}`, params, result, duration: Date.now() - cmdStart });
|
|
11397
|
+
if (output) hookOutputs.push({ _hook: h.name, ...output });
|
|
11398
|
+
}
|
|
11399
|
+
}
|
|
11364
11400
|
if (session && result && result.data) {
|
|
11365
11401
|
const convUrl = result.data.conversationUrl;
|
|
11366
11402
|
if (convUrl) {
|
|
11367
11403
|
saveSessionDiskMeta(sessionName, { conversationUrl: convUrl, cdpEndpoint });
|
|
11368
11404
|
}
|
|
11369
11405
|
}
|
|
11370
|
-
|
|
11371
|
-
|
|
11372
|
-
|
|
11373
|
-
|
|
11374
|
-
|
|
11375
|
-
|
|
11376
|
-
|
|
11377
|
-
|
|
11378
|
-
if (result.tips?.length) {
|
|
11379
|
-
for (const tip of result.tips) console.log(` \u{1F4A1} ${tip}`);
|
|
11380
|
-
}
|
|
11406
|
+
const outputData = isCommandResult2(result) ? result.data : result && typeof result === "object" ? result.data ?? result : result;
|
|
11407
|
+
const tips = isCommandResult2(result) ? result.tips : result && typeof result === "object" ? result.tips : void 0;
|
|
11408
|
+
if (mode === "json" || mode === "yaml") {
|
|
11409
|
+
const finalOutput = {
|
|
11410
|
+
data: outputData
|
|
11411
|
+
};
|
|
11412
|
+
if (hookOutputs.length > 0) {
|
|
11413
|
+
finalOutput.hooks = hookOutputs;
|
|
11381
11414
|
}
|
|
11382
|
-
|
|
11383
|
-
|
|
11384
|
-
|
|
11385
|
-
|
|
11386
|
-
|
|
11387
|
-
|
|
11388
|
-
|
|
11389
|
-
}
|
|
11390
|
-
}
|
|
11391
|
-
|
|
11392
|
-
const
|
|
11393
|
-
|
|
11394
|
-
for (const tip of tips) console.log(` \u{1F4A1} ${tip}`);
|
|
11415
|
+
console.log(outputFormatter2.format(finalOutput, { mode, color: false, emoji: false }));
|
|
11416
|
+
if (tips?.length) {
|
|
11417
|
+
for (const tip of tips) console.error(`\u{1F4A1} ${tip}`);
|
|
11418
|
+
}
|
|
11419
|
+
} else {
|
|
11420
|
+
console.log(outputFormatter2.format(outputData, { mode: "text", color: true, emoji: true }));
|
|
11421
|
+
if (tips?.length) {
|
|
11422
|
+
for (const tip of tips) console.log(` \u{1F4A1} ${tip}`);
|
|
11423
|
+
}
|
|
11424
|
+
if (hookOutputs.length > 0) {
|
|
11425
|
+
for (const ho of hookOutputs) {
|
|
11426
|
+
console.log(` \u{1F4F8} screenshot: ${ho.screenshot?.url || "captured"}`);
|
|
11395
11427
|
}
|
|
11396
11428
|
}
|
|
11397
11429
|
}
|
|
@@ -12602,10 +12634,10 @@ var WSServer = class extends EventEmitter {
|
|
|
12602
12634
|
}
|
|
12603
12635
|
case "file_download": {
|
|
12604
12636
|
try {
|
|
12605
|
-
const { readFileSync:
|
|
12637
|
+
const { readFileSync: readFileSync16 } = await import("fs");
|
|
12606
12638
|
const { resolve: resolve16, basename: basename3 } = await import("path");
|
|
12607
12639
|
const targetPath = resolve16(msg.path);
|
|
12608
|
-
const data =
|
|
12640
|
+
const data = readFileSync16(targetPath);
|
|
12609
12641
|
const base64 = data.toString("base64");
|
|
12610
12642
|
const ext = targetPath.split(".").pop()?.toLowerCase() || "";
|
|
12611
12643
|
const mimeMap = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xbrowser/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.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": {
|
|
@@ -82,7 +82,7 @@
|
|
|
82
82
|
"prepare": "husky"
|
|
83
83
|
},
|
|
84
84
|
"dependencies": {
|
|
85
|
-
"@dyyz1993/xcli-core": "^0.
|
|
85
|
+
"@dyyz1993/xcli-core": "^0.12.1",
|
|
86
86
|
"@types/react-syntax-highlighter": "^15.5.13",
|
|
87
87
|
"@types/turndown": "^5.0.6",
|
|
88
88
|
"cheerio": "^1.2.0",
|