@xbrowser/cli 0.14.2 → 0.15.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/README.md +63 -1
- package/dist/cli.js +142 -100
- package/dist/daemon-main.js +83 -35
- package/dist/index.js +140 -98
- package/package.json +19 -3
package/dist/index.js
CHANGED
|
@@ -191,7 +191,18 @@ function parsePluginParams(args, schema, base = {}) {
|
|
|
191
191
|
if (value === "true") params[key] = true;
|
|
192
192
|
else if (value === "false") params[key] = false;
|
|
193
193
|
else if (/^\d+$/.test(value)) params[key] = parseInt(value, 10);
|
|
194
|
-
else
|
|
194
|
+
else {
|
|
195
|
+
try {
|
|
196
|
+
const parsed = JSON.parse(value);
|
|
197
|
+
if (Array.isArray(parsed) || typeof parsed === "object" && parsed !== null) {
|
|
198
|
+
params[key] = parsed;
|
|
199
|
+
} else {
|
|
200
|
+
params[key] = value;
|
|
201
|
+
}
|
|
202
|
+
} catch {
|
|
203
|
+
params[key] = value;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
195
206
|
i++;
|
|
196
207
|
} else {
|
|
197
208
|
params[key] = true;
|
|
@@ -6535,8 +6546,48 @@ async function loadHooks() {
|
|
|
6535
6546
|
// src/executor.ts
|
|
6536
6547
|
import { homedir as homedir3 } from "os";
|
|
6537
6548
|
import { join as join3 } from "path";
|
|
6549
|
+
import { existsSync as existsSync6, readFileSync as readFileSync9, writeFileSync as writeFileSync4, mkdirSync as mkdirSync3 } from "fs";
|
|
6538
6550
|
var NAVIGATION_COMMANDS = /* @__PURE__ */ new Set(["goto", "back", "forward", "refresh"]);
|
|
6539
6551
|
var snapshotHintShown = /* @__PURE__ */ new WeakSet();
|
|
6552
|
+
var STORAGE_DIR = join3(homedir3(), ".xbrowser", "storage");
|
|
6553
|
+
var storageCache = /* @__PURE__ */ new Map();
|
|
6554
|
+
function getPluginStorage(pluginName) {
|
|
6555
|
+
if (!storageCache.has(pluginName)) {
|
|
6556
|
+
const filePath = join3(STORAGE_DIR, `${pluginName}.json`);
|
|
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
|
+
});
|
|
6588
|
+
}
|
|
6589
|
+
return storageCache.get(pluginName);
|
|
6590
|
+
}
|
|
6540
6591
|
var archiveInitialized = false;
|
|
6541
6592
|
function ensureArchiveInit() {
|
|
6542
6593
|
if (!archiveInitialized) {
|
|
@@ -6647,16 +6698,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
|
|
|
6647
6698
|
args: [],
|
|
6648
6699
|
options: {},
|
|
6649
6700
|
cwd: process.cwd(),
|
|
6650
|
-
storage:
|
|
6651
|
-
get: async () => null,
|
|
6652
|
-
set: async () => {
|
|
6653
|
-
},
|
|
6654
|
-
delete: async () => {
|
|
6655
|
-
},
|
|
6656
|
-
clear: async () => {
|
|
6657
|
-
},
|
|
6658
|
-
keys: async () => []
|
|
6659
|
-
},
|
|
6701
|
+
storage: getPluginStorage(commandName),
|
|
6660
6702
|
output: {
|
|
6661
6703
|
mode: "text",
|
|
6662
6704
|
showTips: false,
|
|
@@ -6879,16 +6921,7 @@ async function executeChain(input, options) {
|
|
|
6879
6921
|
browser: session.context.browser(),
|
|
6880
6922
|
browserContext: session.context,
|
|
6881
6923
|
sessionId: session.id,
|
|
6882
|
-
storage:
|
|
6883
|
-
get: async (_key) => null,
|
|
6884
|
-
set: async (_key, _value) => {
|
|
6885
|
-
},
|
|
6886
|
-
delete: async (_key) => {
|
|
6887
|
-
},
|
|
6888
|
-
clear: async () => {
|
|
6889
|
-
},
|
|
6890
|
-
keys: async () => []
|
|
6891
|
-
},
|
|
6924
|
+
storage: getPluginStorage(cmdName),
|
|
6892
6925
|
output: { mode: "text", showTips: false, color: false, emoji: false },
|
|
6893
6926
|
error: (msg) => {
|
|
6894
6927
|
throw new Error(msg);
|
|
@@ -6899,8 +6932,21 @@ async function executeChain(input, options) {
|
|
|
6899
6932
|
};
|
|
6900
6933
|
const start2 = Date.now();
|
|
6901
6934
|
try {
|
|
6935
|
+
const hooks = await loadHooks();
|
|
6936
|
+
if (hooks.length > 0) {
|
|
6937
|
+
await Promise.all(hooks.map((h) => h.onBeforeCommand?.({ page: session.page, command: `${cmdName} ${subCommand}`, params: pluginParams })));
|
|
6938
|
+
}
|
|
6902
6939
|
const raw = await cmdEntry.handler(pluginParams, pluginCtx);
|
|
6903
6940
|
const duration2 = Date.now() - start2;
|
|
6941
|
+
let hookOutputs;
|
|
6942
|
+
if (hooks.length > 0) {
|
|
6943
|
+
const outputs = [];
|
|
6944
|
+
for (const h of hooks) {
|
|
6945
|
+
const output = await h.onAfterCommand?.({ page: session.page, command: `${cmdName} ${subCommand}`, params: pluginParams, result: raw, duration: duration2 });
|
|
6946
|
+
if (output) outputs.push({ _hook: h.name, ...output });
|
|
6947
|
+
}
|
|
6948
|
+
if (outputs.length > 0) hookOutputs = outputs;
|
|
6949
|
+
}
|
|
6904
6950
|
const data = raw?.data ?? raw;
|
|
6905
6951
|
recordArchive(session.id, sessionName, {
|
|
6906
6952
|
step: results.length,
|
|
@@ -6915,7 +6961,8 @@ async function executeChain(input, options) {
|
|
|
6915
6961
|
command: `${cmdName} ${subCommand}`,
|
|
6916
6962
|
raw: cmdStr,
|
|
6917
6963
|
...ok25(data),
|
|
6918
|
-
duration: duration2
|
|
6964
|
+
duration: duration2,
|
|
6965
|
+
...hookOutputs ? { hookOutputs } : {}
|
|
6919
6966
|
});
|
|
6920
6967
|
if (type === "or") {
|
|
6921
6968
|
return {
|
|
@@ -6979,7 +7026,8 @@ async function executeChain(input, options) {
|
|
|
6979
7026
|
data: result.data,
|
|
6980
7027
|
message: result.message,
|
|
6981
7028
|
duration,
|
|
6982
|
-
tips: result.tips
|
|
7029
|
+
tips: result.tips,
|
|
7030
|
+
...result.hookOutputs ? { hookOutputs: result.hookOutputs } : {}
|
|
6983
7031
|
};
|
|
6984
7032
|
results.push(stepResult);
|
|
6985
7033
|
if (type === "and" && !result.success) {
|
|
@@ -7285,26 +7333,26 @@ var configBuiltin = {
|
|
|
7285
7333
|
|
|
7286
7334
|
// src/plugin/installer.ts
|
|
7287
7335
|
import {
|
|
7288
|
-
existsSync as
|
|
7336
|
+
existsSync as existsSync13,
|
|
7289
7337
|
readdirSync as readdirSync3,
|
|
7290
|
-
mkdirSync as
|
|
7338
|
+
mkdirSync as mkdirSync8,
|
|
7291
7339
|
rmSync as rmSync7
|
|
7292
7340
|
} from "fs";
|
|
7293
7341
|
import { resolve as resolve15, basename as basename2 } from "path";
|
|
7294
7342
|
import { homedir as homedir4 } from "os";
|
|
7295
7343
|
|
|
7296
7344
|
// src/plugin/install-sources/local.ts
|
|
7297
|
-
import { existsSync as
|
|
7345
|
+
import { existsSync as existsSync8, cpSync as cpSync2, rmSync as rmSync2 } from "fs";
|
|
7298
7346
|
import { resolve as resolve10 } from "path";
|
|
7299
7347
|
|
|
7300
7348
|
// src/plugin/install-utils.ts
|
|
7301
7349
|
import {
|
|
7302
|
-
existsSync as
|
|
7350
|
+
existsSync as existsSync7,
|
|
7303
7351
|
readdirSync as readdirSync2,
|
|
7304
7352
|
cpSync,
|
|
7305
7353
|
rmSync,
|
|
7306
|
-
mkdirSync as
|
|
7307
|
-
readFileSync as
|
|
7354
|
+
mkdirSync as mkdirSync4,
|
|
7355
|
+
readFileSync as readFileSync10,
|
|
7308
7356
|
createWriteStream
|
|
7309
7357
|
} from "fs";
|
|
7310
7358
|
import { resolve as resolve9 } from "path";
|
|
@@ -7375,7 +7423,7 @@ async function downloadToFile(url, destPath) {
|
|
|
7375
7423
|
await pipeline(nodeStream, createWriteStream(destPath));
|
|
7376
7424
|
}
|
|
7377
7425
|
function extractTarGz(tarballPath, targetDir) {
|
|
7378
|
-
|
|
7426
|
+
mkdirSync4(targetDir, { recursive: true });
|
|
7379
7427
|
execSync7(`tar -xzf "${tarballPath}" -C "${targetDir}"`, { stdio: "pipe" });
|
|
7380
7428
|
}
|
|
7381
7429
|
function flattenPackageRoot(targetDir) {
|
|
@@ -7396,18 +7444,18 @@ function flattenPackageRoot(targetDir) {
|
|
|
7396
7444
|
async function verifyPlugin(dir) {
|
|
7397
7445
|
const warnings = [];
|
|
7398
7446
|
const indexPath = resolve9(dir, "index.ts");
|
|
7399
|
-
if (!
|
|
7447
|
+
if (!existsSync7(indexPath)) {
|
|
7400
7448
|
const indexJs = resolve9(dir, "index.js");
|
|
7401
|
-
if (!
|
|
7449
|
+
if (!existsSync7(indexJs)) {
|
|
7402
7450
|
return { valid: false, error: "No index.ts or index.js entry point found", warnings };
|
|
7403
7451
|
}
|
|
7404
7452
|
}
|
|
7405
7453
|
const pkgPath = resolve9(dir, "package.json");
|
|
7406
|
-
if (!
|
|
7454
|
+
if (!existsSync7(pkgPath)) {
|
|
7407
7455
|
warnings.push("No package.json found");
|
|
7408
7456
|
} else {
|
|
7409
7457
|
try {
|
|
7410
|
-
const pkg2 = JSON.parse(
|
|
7458
|
+
const pkg2 = JSON.parse(readFileSync10(pkgPath, "utf-8"));
|
|
7411
7459
|
if (!pkg2.xbrowser) {
|
|
7412
7460
|
warnings.push("No xbrowser metadata in package.json");
|
|
7413
7461
|
}
|
|
@@ -7427,7 +7475,7 @@ function safeCleanup(dir) {
|
|
|
7427
7475
|
// src/plugin/install-sources/local.ts
|
|
7428
7476
|
async function installFromLocal(source, name, targetDir) {
|
|
7429
7477
|
const srcPath = resolve10(source);
|
|
7430
|
-
if (!
|
|
7478
|
+
if (!existsSync8(srcPath)) {
|
|
7431
7479
|
throw new Error(`Local path does not exist: ${srcPath}`);
|
|
7432
7480
|
}
|
|
7433
7481
|
const tmpTarget = `${targetDir}-tmp-${Date.now()}`;
|
|
@@ -7440,7 +7488,7 @@ async function installFromLocal(source, name, targetDir) {
|
|
|
7440
7488
|
safeCleanup(tmpTarget);
|
|
7441
7489
|
throw new Error(`Invalid plugin: ${verify.error}`);
|
|
7442
7490
|
}
|
|
7443
|
-
if (
|
|
7491
|
+
if (existsSync8(targetDir)) {
|
|
7444
7492
|
rmSync2(targetDir, { recursive: true, force: true });
|
|
7445
7493
|
}
|
|
7446
7494
|
cpSync2(tmpTarget, targetDir, { recursive: true, force: true });
|
|
@@ -7460,7 +7508,7 @@ async function installFromLocal(source, name, targetDir) {
|
|
|
7460
7508
|
}
|
|
7461
7509
|
|
|
7462
7510
|
// src/plugin/install-sources/npm.ts
|
|
7463
|
-
import { existsSync as
|
|
7511
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync5, readFileSync as readFileSync11, writeFileSync as writeFileSync5, rmSync as rmSync3, cpSync as cpSync3 } from "fs";
|
|
7464
7512
|
import { resolve as resolve11, join as join4 } from "path";
|
|
7465
7513
|
import { tmpdir } from "os";
|
|
7466
7514
|
async function installFromNpm(packageName, name, targetDir) {
|
|
@@ -7481,7 +7529,7 @@ async function installFromNpm(packageName, name, targetDir) {
|
|
|
7481
7529
|
}
|
|
7482
7530
|
const tarballUrl = versionMeta.dist.tarball;
|
|
7483
7531
|
const tmpDir = join4(tmpdir(), `xbrowser-npm-${Date.now()}`);
|
|
7484
|
-
|
|
7532
|
+
mkdirSync5(tmpDir, { recursive: true });
|
|
7485
7533
|
let warnings = [];
|
|
7486
7534
|
try {
|
|
7487
7535
|
const tarballPath = join4(tmpDir, `${name}.tgz`);
|
|
@@ -7494,16 +7542,16 @@ async function installFromNpm(packageName, name, targetDir) {
|
|
|
7494
7542
|
if (!verify.valid) {
|
|
7495
7543
|
throw new Error(`Invalid npm plugin: ${verify.error}`);
|
|
7496
7544
|
}
|
|
7497
|
-
if (
|
|
7545
|
+
if (existsSync9(targetDir)) {
|
|
7498
7546
|
rmSync3(targetDir, { recursive: true, force: true });
|
|
7499
7547
|
}
|
|
7500
7548
|
cpSync3(extractDir, targetDir, { recursive: true, force: true });
|
|
7501
7549
|
const pkgPath = resolve11(targetDir, "package.json");
|
|
7502
|
-
if (
|
|
7503
|
-
const pkg2 = JSON.parse(
|
|
7550
|
+
if (existsSync9(pkgPath)) {
|
|
7551
|
+
const pkg2 = JSON.parse(readFileSync11(pkgPath, "utf-8"));
|
|
7504
7552
|
if (!pkg2._npmSource) {
|
|
7505
7553
|
pkg2._npmSource = { name: packageName, version: latestVersion };
|
|
7506
|
-
|
|
7554
|
+
writeFileSync5(pkgPath, JSON.stringify(pkg2, null, 2));
|
|
7507
7555
|
}
|
|
7508
7556
|
}
|
|
7509
7557
|
} finally {
|
|
@@ -7520,7 +7568,7 @@ async function installFromNpm(packageName, name, targetDir) {
|
|
|
7520
7568
|
}
|
|
7521
7569
|
|
|
7522
7570
|
// src/plugin/install-sources/git.ts
|
|
7523
|
-
import { existsSync as
|
|
7571
|
+
import { existsSync as existsSync10, readFileSync as readFileSync12, writeFileSync as writeFileSync6, rmSync as rmSync4, cpSync as cpSync4 } from "fs";
|
|
7524
7572
|
import { resolve as resolve12, join as join5 } from "path";
|
|
7525
7573
|
import { tmpdir as tmpdir2 } from "os";
|
|
7526
7574
|
import { execSync as execSync8 } from "child_process";
|
|
@@ -7534,17 +7582,17 @@ async function installFromGit(gitUrl, name, targetDir) {
|
|
|
7534
7582
|
if (!verify.valid) {
|
|
7535
7583
|
throw new Error(`Invalid git plugin: ${verify.error}`);
|
|
7536
7584
|
}
|
|
7537
|
-
if (
|
|
7585
|
+
if (existsSync10(targetDir)) {
|
|
7538
7586
|
rmSync4(targetDir, { recursive: true, force: true });
|
|
7539
7587
|
}
|
|
7540
7588
|
cpSync4(tmpDir, targetDir, { recursive: true, force: true });
|
|
7541
7589
|
rmSync4(resolve12(targetDir, ".git"), { recursive: true, force: true });
|
|
7542
7590
|
const pkgPath = resolve12(targetDir, "package.json");
|
|
7543
|
-
if (
|
|
7544
|
-
const pkg2 = JSON.parse(
|
|
7591
|
+
if (existsSync10(pkgPath)) {
|
|
7592
|
+
const pkg2 = JSON.parse(readFileSync12(pkgPath, "utf-8"));
|
|
7545
7593
|
if (!pkg2._gitSource) {
|
|
7546
7594
|
pkg2._gitSource = { url: gitUrl };
|
|
7547
|
-
|
|
7595
|
+
writeFileSync6(pkgPath, JSON.stringify(pkg2, null, 2));
|
|
7548
7596
|
}
|
|
7549
7597
|
}
|
|
7550
7598
|
} finally {
|
|
@@ -7561,12 +7609,12 @@ async function installFromGit(gitUrl, name, targetDir) {
|
|
|
7561
7609
|
}
|
|
7562
7610
|
|
|
7563
7611
|
// src/plugin/install-sources/url.ts
|
|
7564
|
-
import { existsSync as
|
|
7612
|
+
import { existsSync as existsSync11, readFileSync as readFileSync13, writeFileSync as writeFileSync7, mkdirSync as mkdirSync6, rmSync as rmSync5, cpSync as cpSync5 } from "fs";
|
|
7565
7613
|
import { resolve as resolve13, join as join6, basename } from "path";
|
|
7566
7614
|
import { tmpdir as tmpdir3 } from "os";
|
|
7567
7615
|
async function installFromUrl(url, name, targetDir) {
|
|
7568
7616
|
const tmpDir = join6(tmpdir3(), `xbrowser-url-${Date.now()}`);
|
|
7569
|
-
|
|
7617
|
+
mkdirSync6(tmpDir, { recursive: true });
|
|
7570
7618
|
let warnings = [];
|
|
7571
7619
|
try {
|
|
7572
7620
|
const fileName = basename(new URL(url).pathname) || "plugin.tar.gz";
|
|
@@ -7580,16 +7628,16 @@ async function installFromUrl(url, name, targetDir) {
|
|
|
7580
7628
|
if (!verify.valid) {
|
|
7581
7629
|
throw new Error(`Invalid plugin from URL: ${verify.error}`);
|
|
7582
7630
|
}
|
|
7583
|
-
if (
|
|
7631
|
+
if (existsSync11(targetDir)) {
|
|
7584
7632
|
rmSync5(targetDir, { recursive: true, force: true });
|
|
7585
7633
|
}
|
|
7586
7634
|
cpSync5(extractDir, targetDir, { recursive: true, force: true });
|
|
7587
7635
|
const pkgPath = resolve13(targetDir, "package.json");
|
|
7588
|
-
if (
|
|
7589
|
-
const pkg2 = JSON.parse(
|
|
7636
|
+
if (existsSync11(pkgPath)) {
|
|
7637
|
+
const pkg2 = JSON.parse(readFileSync13(pkgPath, "utf-8"));
|
|
7590
7638
|
if (!pkg2._urlSource) {
|
|
7591
7639
|
pkg2._urlSource = { url };
|
|
7592
|
-
|
|
7640
|
+
writeFileSync7(pkgPath, JSON.stringify(pkg2, null, 2));
|
|
7593
7641
|
}
|
|
7594
7642
|
}
|
|
7595
7643
|
} finally {
|
|
@@ -7607,10 +7655,10 @@ async function installFromUrl(url, name, targetDir) {
|
|
|
7607
7655
|
|
|
7608
7656
|
// src/plugin/install-sources/marketplace.ts
|
|
7609
7657
|
import {
|
|
7610
|
-
existsSync as
|
|
7611
|
-
mkdirSync as
|
|
7612
|
-
writeFileSync as
|
|
7613
|
-
readFileSync as
|
|
7658
|
+
existsSync as existsSync12,
|
|
7659
|
+
mkdirSync as mkdirSync7,
|
|
7660
|
+
writeFileSync as writeFileSync8,
|
|
7661
|
+
readFileSync as readFileSync14,
|
|
7614
7662
|
rmSync as rmSync6,
|
|
7615
7663
|
cpSync as cpSync6
|
|
7616
7664
|
} from "fs";
|
|
@@ -7632,12 +7680,12 @@ async function installFromMarketplace(pluginsDir, slug, options) {
|
|
|
7632
7680
|
const plugin = detailData.data;
|
|
7633
7681
|
const name = options?.name || String(plugin.slug || slug);
|
|
7634
7682
|
const targetDir = resolve14(pluginsDir, name);
|
|
7635
|
-
if (
|
|
7683
|
+
if (existsSync12(targetDir) && !options?.force) {
|
|
7636
7684
|
throw new Error(`Plugin "${name}" already exists. Use --force to overwrite.`);
|
|
7637
7685
|
}
|
|
7638
|
-
|
|
7686
|
+
mkdirSync7(targetDir, { recursive: true });
|
|
7639
7687
|
const tmpDir = join7(tmpdir4(), `xbrowser-marketplace-${Date.now()}`);
|
|
7640
|
-
|
|
7688
|
+
mkdirSync7(tmpDir, { recursive: true });
|
|
7641
7689
|
const realSlug = String(plugin.slug || slug);
|
|
7642
7690
|
try {
|
|
7643
7691
|
await downloadAndExtractMarketplaceTarball(baseUrl, realSlug, tmpDir, targetDir);
|
|
@@ -7667,14 +7715,14 @@ function isManifestArray(data) {
|
|
|
7667
7715
|
return Array.isArray(data) && data.length > 0 && typeof data[0].path === "string" && typeof data[0].content === "string";
|
|
7668
7716
|
}
|
|
7669
7717
|
function extractManifestToDir(manifest, targetDir) {
|
|
7670
|
-
if (
|
|
7718
|
+
if (existsSync12(targetDir)) {
|
|
7671
7719
|
rmSync6(targetDir, { recursive: true, force: true });
|
|
7672
7720
|
}
|
|
7673
|
-
|
|
7721
|
+
mkdirSync7(targetDir, { recursive: true });
|
|
7674
7722
|
for (const file of manifest) {
|
|
7675
7723
|
const filePath = resolve14(targetDir, file.path);
|
|
7676
|
-
|
|
7677
|
-
|
|
7724
|
+
mkdirSync7(dirname2(filePath), { recursive: true });
|
|
7725
|
+
writeFileSync8(filePath, Buffer.from(file.content, "base64"));
|
|
7678
7726
|
}
|
|
7679
7727
|
}
|
|
7680
7728
|
function tryParseAsGzippedManifest(buffer) {
|
|
@@ -7701,7 +7749,7 @@ async function downloadAndExtractMarketplaceTarball(baseUrl, slug, tmpDir, targe
|
|
|
7701
7749
|
const redirectUrl = tarballRes.headers.get("location");
|
|
7702
7750
|
const tarballPath = join7(tmpDir, `${slug}.tar.gz`);
|
|
7703
7751
|
await downloadToFile(redirectUrl, tarballPath);
|
|
7704
|
-
const buffer =
|
|
7752
|
+
const buffer = readFileSync14(tarballPath);
|
|
7705
7753
|
const manifest = tryParseAsGzippedManifest(buffer);
|
|
7706
7754
|
if (manifest) {
|
|
7707
7755
|
extractManifestToDir(manifest, targetDir);
|
|
@@ -7710,7 +7758,7 @@ async function downloadAndExtractMarketplaceTarball(baseUrl, slug, tmpDir, targe
|
|
|
7710
7758
|
const extractDir = join7(tmpDir, "extracted");
|
|
7711
7759
|
extractTarGz(tarballPath, extractDir);
|
|
7712
7760
|
flattenPackageRoot(extractDir);
|
|
7713
|
-
if (
|
|
7761
|
+
if (existsSync12(targetDir)) {
|
|
7714
7762
|
rmSync6(targetDir, { recursive: true, force: true });
|
|
7715
7763
|
}
|
|
7716
7764
|
cpSync6(extractDir, targetDir, { recursive: true, force: true });
|
|
@@ -7722,12 +7770,12 @@ async function downloadAndExtractMarketplaceTarball(baseUrl, slug, tmpDir, targe
|
|
|
7722
7770
|
return;
|
|
7723
7771
|
}
|
|
7724
7772
|
const tarballPath = join7(tmpDir, `${slug}.tar.gz`);
|
|
7725
|
-
|
|
7773
|
+
writeFileSync8(tarballPath, buffer);
|
|
7726
7774
|
try {
|
|
7727
7775
|
const extractDir = join7(tmpDir, "extracted");
|
|
7728
7776
|
extractTarGz(tarballPath, extractDir);
|
|
7729
7777
|
flattenPackageRoot(extractDir);
|
|
7730
|
-
if (
|
|
7778
|
+
if (existsSync12(targetDir)) {
|
|
7731
7779
|
rmSync6(targetDir, { recursive: true, force: true });
|
|
7732
7780
|
}
|
|
7733
7781
|
cpSync6(extractDir, targetDir, { recursive: true, force: true });
|
|
@@ -7763,24 +7811,24 @@ function writeMarketplacePackageJson(plugin, slug, name, baseUrl, targetDir) {
|
|
|
7763
7811
|
}
|
|
7764
7812
|
};
|
|
7765
7813
|
const pkgPath = resolve14(targetDir, "package.json");
|
|
7766
|
-
if (!
|
|
7767
|
-
|
|
7814
|
+
if (!existsSync12(pkgPath)) {
|
|
7815
|
+
writeFileSync8(pkgPath, JSON.stringify(packageJson, null, 2));
|
|
7768
7816
|
} else {
|
|
7769
7817
|
try {
|
|
7770
|
-
const existing = JSON.parse(
|
|
7818
|
+
const existing = JSON.parse(readFileSync14(pkgPath, "utf-8"));
|
|
7771
7819
|
const merged = {
|
|
7772
7820
|
...existing,
|
|
7773
7821
|
xbrowser: { ...existing.xbrowser, ...packageJson.xbrowser },
|
|
7774
7822
|
_marketplace: packageJson._marketplace
|
|
7775
7823
|
};
|
|
7776
|
-
|
|
7824
|
+
writeFileSync8(pkgPath, JSON.stringify(merged, null, 2));
|
|
7777
7825
|
} catch {
|
|
7778
|
-
|
|
7826
|
+
writeFileSync8(pkgPath, JSON.stringify(packageJson, null, 2));
|
|
7779
7827
|
}
|
|
7780
7828
|
}
|
|
7781
7829
|
}
|
|
7782
7830
|
function ensureIndexFile(plugin, name, targetDir) {
|
|
7783
|
-
if (!
|
|
7831
|
+
if (!existsSync12(resolve14(targetDir, "index.ts")) && !existsSync12(resolve14(targetDir, "index.js"))) {
|
|
7784
7832
|
const commands = plugin.commands || [];
|
|
7785
7833
|
const commandHandlers = commands.length > 0 ? commands.map((cmd) => {
|
|
7786
7834
|
return [
|
|
@@ -7795,7 +7843,7 @@ function ensureIndexFile(plugin, name, targetDir) {
|
|
|
7795
7843
|
` handler: async () => ({ data: { message: 'Hello from ${name}!' }, tips: [] }),`,
|
|
7796
7844
|
` });`
|
|
7797
7845
|
].join("\n");
|
|
7798
|
-
|
|
7846
|
+
writeFileSync8(
|
|
7799
7847
|
resolve14(targetDir, "index.ts"),
|
|
7800
7848
|
[
|
|
7801
7849
|
`import type { XCLIAPI } from '@dyyz1993/xcli-core';`,
|
|
@@ -7834,10 +7882,10 @@ var PluginInstaller = class {
|
|
|
7834
7882
|
const type = this.detectSourceType(source);
|
|
7835
7883
|
const name = options?.name || this.deriveName(source, type);
|
|
7836
7884
|
const targetDir = resolve15(this.pluginsDir, name);
|
|
7837
|
-
if (
|
|
7885
|
+
if (existsSync13(targetDir) && !options?.force) {
|
|
7838
7886
|
throw new Error(`Plugin "${name}" already exists. Use --force to overwrite.`);
|
|
7839
7887
|
}
|
|
7840
|
-
|
|
7888
|
+
mkdirSync8(targetDir, { recursive: true });
|
|
7841
7889
|
const resolvedSource = type === "npm" ? await resolveNpmPackageWithFallback(source) : source;
|
|
7842
7890
|
switch (type) {
|
|
7843
7891
|
case "local":
|
|
@@ -7895,7 +7943,7 @@ var PluginInstaller = class {
|
|
|
7895
7943
|
*/
|
|
7896
7944
|
async uninstall(name) {
|
|
7897
7945
|
const targetDir = resolve15(this.pluginsDir, name);
|
|
7898
|
-
if (!
|
|
7946
|
+
if (!existsSync13(targetDir)) {
|
|
7899
7947
|
throw new Error(`Plugin "${name}" not found`);
|
|
7900
7948
|
}
|
|
7901
7949
|
rmSync7(targetDir, { recursive: true, force: true });
|
|
@@ -7906,7 +7954,7 @@ var PluginInstaller = class {
|
|
|
7906
7954
|
* @returns Array of installed plugin information.
|
|
7907
7955
|
*/
|
|
7908
7956
|
async list(_options) {
|
|
7909
|
-
if (!
|
|
7957
|
+
if (!existsSync13(this.pluginsDir)) return [];
|
|
7910
7958
|
const entries = readdirSync3(this.pluginsDir, { withFileTypes: true });
|
|
7911
7959
|
const plugins = [];
|
|
7912
7960
|
for (const entry of entries) {
|
|
@@ -7914,7 +7962,7 @@ var PluginInstaller = class {
|
|
|
7914
7962
|
const pluginPath = resolve15(this.pluginsDir, entry.name);
|
|
7915
7963
|
const indexPath = resolve15(pluginPath, "index.ts");
|
|
7916
7964
|
const indexJsPath = resolve15(pluginPath, "index.js");
|
|
7917
|
-
if (!
|
|
7965
|
+
if (!existsSync13(indexPath) && !existsSync13(indexJsPath)) continue;
|
|
7918
7966
|
const metadata = PluginMetadataParser.parseFromPackageJson(pluginPath);
|
|
7919
7967
|
let source = "local";
|
|
7920
7968
|
const pkg2 = readJsonFile(resolve15(pluginPath, "package.json"), {});
|
|
@@ -7941,10 +7989,10 @@ var PluginInstaller = class {
|
|
|
7941
7989
|
}
|
|
7942
7990
|
if (source.startsWith("file://")) {
|
|
7943
7991
|
const filePath = decodeURIComponent(new URL(source).pathname);
|
|
7944
|
-
if (
|
|
7992
|
+
if (existsSync13(filePath)) return "url";
|
|
7945
7993
|
}
|
|
7946
7994
|
if (source.endsWith(".git") || source.includes("github.com/")) return "git";
|
|
7947
|
-
if (
|
|
7995
|
+
if (existsSync13(resolve15(source))) return "local";
|
|
7948
7996
|
return "npm";
|
|
7949
7997
|
}
|
|
7950
7998
|
deriveName(source, type) {
|
|
@@ -9973,7 +10021,7 @@ async function handleFilter(args, _mode) {
|
|
|
9973
10021
|
|
|
9974
10022
|
// src/stdin.ts
|
|
9975
10023
|
import { createInterface } from "readline";
|
|
9976
|
-
import { readFileSync as
|
|
10024
|
+
import { readFileSync as readFileSync15 } from "fs";
|
|
9977
10025
|
async function readStdin2() {
|
|
9978
10026
|
if (process.stdin.isTTY) return [];
|
|
9979
10027
|
const lines = [];
|
|
@@ -9987,7 +10035,7 @@ async function readStdin2() {
|
|
|
9987
10035
|
return lines;
|
|
9988
10036
|
}
|
|
9989
10037
|
function readCommandFile(filePath) {
|
|
9990
|
-
const content =
|
|
10038
|
+
const content = readFileSync15(filePath, "utf-8");
|
|
9991
10039
|
return content.split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("#"));
|
|
9992
10040
|
}
|
|
9993
10041
|
|
|
@@ -11255,11 +11303,14 @@ Run "xbrowser ${command} --help" to see available commands.`
|
|
|
11255
11303
|
showCommandHelp(command, cmdEntry, { description: site.config.description, name: site.name, url: site.url }, mode);
|
|
11256
11304
|
return;
|
|
11257
11305
|
}
|
|
11258
|
-
const
|
|
11306
|
+
const pluginNameIdx = argv.indexOf(command);
|
|
11307
|
+
const subCmdIdx = pluginNameIdx >= 0 ? argv.indexOf(subCommand, pluginNameIdx + 1) : -1;
|
|
11308
|
+
const rawPluginArgs = subCmdIdx >= 0 ? argv.slice(subCmdIdx + 1) : [];
|
|
11309
|
+
const params = parsePluginParams(rawPluginArgs, cmdEntry.parameters);
|
|
11259
11310
|
if (options.target && !params._target) {
|
|
11260
11311
|
params._target = options.target;
|
|
11261
11312
|
}
|
|
11262
|
-
const needsBrowser = cmdEntry.scope
|
|
11313
|
+
const needsBrowser = cmdEntry.scope === "page";
|
|
11263
11314
|
if (needsBrowser && !process.env.XBROWSER_DAEMON_WORKER) {
|
|
11264
11315
|
const { forwardExec } = await import("./daemon-client-3IJD6X4B.js");
|
|
11265
11316
|
const userTimeout = typeof params.timeout === "number" && params.timeout > 0 ? params.timeout * 1e3 + 3e4 : void 0;
|
|
@@ -11296,16 +11347,7 @@ Run "xbrowser ${command} --help" to see available commands.`
|
|
|
11296
11347
|
browser: needsBrowser ? session.context.browser() : null,
|
|
11297
11348
|
browserContext: needsBrowser ? session.context : null,
|
|
11298
11349
|
sessionId: needsBrowser ? session.id : "",
|
|
11299
|
-
storage:
|
|
11300
|
-
get: async (_key) => null,
|
|
11301
|
-
set: async (_key, _value) => {
|
|
11302
|
-
},
|
|
11303
|
-
delete: async (_key) => {
|
|
11304
|
-
},
|
|
11305
|
-
clear: async () => {
|
|
11306
|
-
},
|
|
11307
|
-
keys: async () => []
|
|
11308
|
-
},
|
|
11350
|
+
storage: getPluginStorage(command),
|
|
11309
11351
|
output: { mode, showTips: true, color: true, emoji: true },
|
|
11310
11352
|
error: (msg) => {
|
|
11311
11353
|
outputError(msg);
|
|
@@ -12560,10 +12602,10 @@ var WSServer = class extends EventEmitter {
|
|
|
12560
12602
|
}
|
|
12561
12603
|
case "file_download": {
|
|
12562
12604
|
try {
|
|
12563
|
-
const { readFileSync:
|
|
12605
|
+
const { readFileSync: readFileSync17 } = await import("fs");
|
|
12564
12606
|
const { resolve: resolve16, basename: basename3 } = await import("path");
|
|
12565
12607
|
const targetPath = resolve16(msg.path);
|
|
12566
|
-
const data =
|
|
12608
|
+
const data = readFileSync17(targetPath);
|
|
12567
12609
|
const base64 = data.toString("base64");
|
|
12568
12610
|
const ext = targetPath.split(".").pop()?.toLowerCase() || "";
|
|
12569
12611
|
const mimeMap = {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xbrowser/cli",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.15.0",
|
|
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": {
|
|
7
7
|
"xbrowser": "dist/cli.js"
|
|
@@ -27,21 +27,37 @@
|
|
|
27
27
|
"record-replay",
|
|
28
28
|
"browser automation",
|
|
29
29
|
"web scraping",
|
|
30
|
+
"web-scraping",
|
|
30
31
|
"playwright alternative",
|
|
32
|
+
"playwright-alternative",
|
|
31
33
|
"puppeteer alternative",
|
|
34
|
+
"puppeteer-alternative",
|
|
32
35
|
"selenium alternative",
|
|
36
|
+
"selenium-alternative",
|
|
33
37
|
"web crawler",
|
|
38
|
+
"web-crawler",
|
|
34
39
|
"headless browser",
|
|
40
|
+
"headless-browser",
|
|
41
|
+
"headless-chrome",
|
|
35
42
|
"scrape",
|
|
36
43
|
"crawl",
|
|
44
|
+
"crawler",
|
|
45
|
+
"scraping",
|
|
37
46
|
"cli tool",
|
|
38
47
|
"command line",
|
|
48
|
+
"command-line",
|
|
39
49
|
"browser cli",
|
|
50
|
+
"browser-cli",
|
|
40
51
|
"anti-detection",
|
|
41
52
|
"record replay",
|
|
42
53
|
"seo tool",
|
|
54
|
+
"seo",
|
|
55
|
+
"search-engine",
|
|
43
56
|
"ai agent",
|
|
44
|
-
"
|
|
57
|
+
"ai-agent",
|
|
58
|
+
"browser plugin",
|
|
59
|
+
"browser-testing",
|
|
60
|
+
"chrome-devtools-protocol"
|
|
45
61
|
],
|
|
46
62
|
"author": "dyyz1993",
|
|
47
63
|
"license": "MIT",
|