@xbrowser/cli 0.14.3 → 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/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 params[key] = value;
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);
@@ -6910,7 +6943,7 @@ async function executeChain(input, options) {
6910
6943
  const outputs = [];
6911
6944
  for (const h of hooks) {
6912
6945
  const output = await h.onAfterCommand?.({ page: session.page, command: `${cmdName} ${subCommand}`, params: pluginParams, result: raw, duration: duration2 });
6913
- if (output) outputs.push(output);
6946
+ if (output) outputs.push({ _hook: h.name, ...output });
6914
6947
  }
6915
6948
  if (outputs.length > 0) hookOutputs = outputs;
6916
6949
  }
@@ -7300,26 +7333,26 @@ var configBuiltin = {
7300
7333
 
7301
7334
  // src/plugin/installer.ts
7302
7335
  import {
7303
- existsSync as existsSync12,
7336
+ existsSync as existsSync13,
7304
7337
  readdirSync as readdirSync3,
7305
- mkdirSync as mkdirSync7,
7338
+ mkdirSync as mkdirSync8,
7306
7339
  rmSync as rmSync7
7307
7340
  } from "fs";
7308
7341
  import { resolve as resolve15, basename as basename2 } from "path";
7309
7342
  import { homedir as homedir4 } from "os";
7310
7343
 
7311
7344
  // src/plugin/install-sources/local.ts
7312
- import { existsSync as existsSync7, cpSync as cpSync2, rmSync as rmSync2 } from "fs";
7345
+ import { existsSync as existsSync8, cpSync as cpSync2, rmSync as rmSync2 } from "fs";
7313
7346
  import { resolve as resolve10 } from "path";
7314
7347
 
7315
7348
  // src/plugin/install-utils.ts
7316
7349
  import {
7317
- existsSync as existsSync6,
7350
+ existsSync as existsSync7,
7318
7351
  readdirSync as readdirSync2,
7319
7352
  cpSync,
7320
7353
  rmSync,
7321
- mkdirSync as mkdirSync3,
7322
- readFileSync as readFileSync9,
7354
+ mkdirSync as mkdirSync4,
7355
+ readFileSync as readFileSync10,
7323
7356
  createWriteStream
7324
7357
  } from "fs";
7325
7358
  import { resolve as resolve9 } from "path";
@@ -7390,7 +7423,7 @@ async function downloadToFile(url, destPath) {
7390
7423
  await pipeline(nodeStream, createWriteStream(destPath));
7391
7424
  }
7392
7425
  function extractTarGz(tarballPath, targetDir) {
7393
- mkdirSync3(targetDir, { recursive: true });
7426
+ mkdirSync4(targetDir, { recursive: true });
7394
7427
  execSync7(`tar -xzf "${tarballPath}" -C "${targetDir}"`, { stdio: "pipe" });
7395
7428
  }
7396
7429
  function flattenPackageRoot(targetDir) {
@@ -7411,18 +7444,18 @@ function flattenPackageRoot(targetDir) {
7411
7444
  async function verifyPlugin(dir) {
7412
7445
  const warnings = [];
7413
7446
  const indexPath = resolve9(dir, "index.ts");
7414
- if (!existsSync6(indexPath)) {
7447
+ if (!existsSync7(indexPath)) {
7415
7448
  const indexJs = resolve9(dir, "index.js");
7416
- if (!existsSync6(indexJs)) {
7449
+ if (!existsSync7(indexJs)) {
7417
7450
  return { valid: false, error: "No index.ts or index.js entry point found", warnings };
7418
7451
  }
7419
7452
  }
7420
7453
  const pkgPath = resolve9(dir, "package.json");
7421
- if (!existsSync6(pkgPath)) {
7454
+ if (!existsSync7(pkgPath)) {
7422
7455
  warnings.push("No package.json found");
7423
7456
  } else {
7424
7457
  try {
7425
- const pkg2 = JSON.parse(readFileSync9(pkgPath, "utf-8"));
7458
+ const pkg2 = JSON.parse(readFileSync10(pkgPath, "utf-8"));
7426
7459
  if (!pkg2.xbrowser) {
7427
7460
  warnings.push("No xbrowser metadata in package.json");
7428
7461
  }
@@ -7442,7 +7475,7 @@ function safeCleanup(dir) {
7442
7475
  // src/plugin/install-sources/local.ts
7443
7476
  async function installFromLocal(source, name, targetDir) {
7444
7477
  const srcPath = resolve10(source);
7445
- if (!existsSync7(srcPath)) {
7478
+ if (!existsSync8(srcPath)) {
7446
7479
  throw new Error(`Local path does not exist: ${srcPath}`);
7447
7480
  }
7448
7481
  const tmpTarget = `${targetDir}-tmp-${Date.now()}`;
@@ -7455,7 +7488,7 @@ async function installFromLocal(source, name, targetDir) {
7455
7488
  safeCleanup(tmpTarget);
7456
7489
  throw new Error(`Invalid plugin: ${verify.error}`);
7457
7490
  }
7458
- if (existsSync7(targetDir)) {
7491
+ if (existsSync8(targetDir)) {
7459
7492
  rmSync2(targetDir, { recursive: true, force: true });
7460
7493
  }
7461
7494
  cpSync2(tmpTarget, targetDir, { recursive: true, force: true });
@@ -7475,7 +7508,7 @@ async function installFromLocal(source, name, targetDir) {
7475
7508
  }
7476
7509
 
7477
7510
  // src/plugin/install-sources/npm.ts
7478
- import { existsSync as existsSync8, mkdirSync as mkdirSync4, readFileSync as readFileSync10, writeFileSync as writeFileSync4, rmSync as rmSync3, cpSync as cpSync3 } from "fs";
7511
+ import { existsSync as existsSync9, mkdirSync as mkdirSync5, readFileSync as readFileSync11, writeFileSync as writeFileSync5, rmSync as rmSync3, cpSync as cpSync3 } from "fs";
7479
7512
  import { resolve as resolve11, join as join4 } from "path";
7480
7513
  import { tmpdir } from "os";
7481
7514
  async function installFromNpm(packageName, name, targetDir) {
@@ -7496,7 +7529,7 @@ async function installFromNpm(packageName, name, targetDir) {
7496
7529
  }
7497
7530
  const tarballUrl = versionMeta.dist.tarball;
7498
7531
  const tmpDir = join4(tmpdir(), `xbrowser-npm-${Date.now()}`);
7499
- mkdirSync4(tmpDir, { recursive: true });
7532
+ mkdirSync5(tmpDir, { recursive: true });
7500
7533
  let warnings = [];
7501
7534
  try {
7502
7535
  const tarballPath = join4(tmpDir, `${name}.tgz`);
@@ -7509,16 +7542,16 @@ async function installFromNpm(packageName, name, targetDir) {
7509
7542
  if (!verify.valid) {
7510
7543
  throw new Error(`Invalid npm plugin: ${verify.error}`);
7511
7544
  }
7512
- if (existsSync8(targetDir)) {
7545
+ if (existsSync9(targetDir)) {
7513
7546
  rmSync3(targetDir, { recursive: true, force: true });
7514
7547
  }
7515
7548
  cpSync3(extractDir, targetDir, { recursive: true, force: true });
7516
7549
  const pkgPath = resolve11(targetDir, "package.json");
7517
- if (existsSync8(pkgPath)) {
7518
- const pkg2 = JSON.parse(readFileSync10(pkgPath, "utf-8"));
7550
+ if (existsSync9(pkgPath)) {
7551
+ const pkg2 = JSON.parse(readFileSync11(pkgPath, "utf-8"));
7519
7552
  if (!pkg2._npmSource) {
7520
7553
  pkg2._npmSource = { name: packageName, version: latestVersion };
7521
- writeFileSync4(pkgPath, JSON.stringify(pkg2, null, 2));
7554
+ writeFileSync5(pkgPath, JSON.stringify(pkg2, null, 2));
7522
7555
  }
7523
7556
  }
7524
7557
  } finally {
@@ -7535,7 +7568,7 @@ async function installFromNpm(packageName, name, targetDir) {
7535
7568
  }
7536
7569
 
7537
7570
  // src/plugin/install-sources/git.ts
7538
- import { existsSync as existsSync9, readFileSync as readFileSync11, writeFileSync as writeFileSync5, rmSync as rmSync4, cpSync as cpSync4 } from "fs";
7571
+ import { existsSync as existsSync10, readFileSync as readFileSync12, writeFileSync as writeFileSync6, rmSync as rmSync4, cpSync as cpSync4 } from "fs";
7539
7572
  import { resolve as resolve12, join as join5 } from "path";
7540
7573
  import { tmpdir as tmpdir2 } from "os";
7541
7574
  import { execSync as execSync8 } from "child_process";
@@ -7549,17 +7582,17 @@ async function installFromGit(gitUrl, name, targetDir) {
7549
7582
  if (!verify.valid) {
7550
7583
  throw new Error(`Invalid git plugin: ${verify.error}`);
7551
7584
  }
7552
- if (existsSync9(targetDir)) {
7585
+ if (existsSync10(targetDir)) {
7553
7586
  rmSync4(targetDir, { recursive: true, force: true });
7554
7587
  }
7555
7588
  cpSync4(tmpDir, targetDir, { recursive: true, force: true });
7556
7589
  rmSync4(resolve12(targetDir, ".git"), { recursive: true, force: true });
7557
7590
  const pkgPath = resolve12(targetDir, "package.json");
7558
- if (existsSync9(pkgPath)) {
7559
- const pkg2 = JSON.parse(readFileSync11(pkgPath, "utf-8"));
7591
+ if (existsSync10(pkgPath)) {
7592
+ const pkg2 = JSON.parse(readFileSync12(pkgPath, "utf-8"));
7560
7593
  if (!pkg2._gitSource) {
7561
7594
  pkg2._gitSource = { url: gitUrl };
7562
- writeFileSync5(pkgPath, JSON.stringify(pkg2, null, 2));
7595
+ writeFileSync6(pkgPath, JSON.stringify(pkg2, null, 2));
7563
7596
  }
7564
7597
  }
7565
7598
  } finally {
@@ -7576,12 +7609,12 @@ async function installFromGit(gitUrl, name, targetDir) {
7576
7609
  }
7577
7610
 
7578
7611
  // src/plugin/install-sources/url.ts
7579
- import { existsSync as existsSync10, readFileSync as readFileSync12, writeFileSync as writeFileSync6, mkdirSync as mkdirSync5, rmSync as rmSync5, cpSync as cpSync5 } from "fs";
7612
+ import { existsSync as existsSync11, readFileSync as readFileSync13, writeFileSync as writeFileSync7, mkdirSync as mkdirSync6, rmSync as rmSync5, cpSync as cpSync5 } from "fs";
7580
7613
  import { resolve as resolve13, join as join6, basename } from "path";
7581
7614
  import { tmpdir as tmpdir3 } from "os";
7582
7615
  async function installFromUrl(url, name, targetDir) {
7583
7616
  const tmpDir = join6(tmpdir3(), `xbrowser-url-${Date.now()}`);
7584
- mkdirSync5(tmpDir, { recursive: true });
7617
+ mkdirSync6(tmpDir, { recursive: true });
7585
7618
  let warnings = [];
7586
7619
  try {
7587
7620
  const fileName = basename(new URL(url).pathname) || "plugin.tar.gz";
@@ -7595,16 +7628,16 @@ async function installFromUrl(url, name, targetDir) {
7595
7628
  if (!verify.valid) {
7596
7629
  throw new Error(`Invalid plugin from URL: ${verify.error}`);
7597
7630
  }
7598
- if (existsSync10(targetDir)) {
7631
+ if (existsSync11(targetDir)) {
7599
7632
  rmSync5(targetDir, { recursive: true, force: true });
7600
7633
  }
7601
7634
  cpSync5(extractDir, targetDir, { recursive: true, force: true });
7602
7635
  const pkgPath = resolve13(targetDir, "package.json");
7603
- if (existsSync10(pkgPath)) {
7604
- const pkg2 = JSON.parse(readFileSync12(pkgPath, "utf-8"));
7636
+ if (existsSync11(pkgPath)) {
7637
+ const pkg2 = JSON.parse(readFileSync13(pkgPath, "utf-8"));
7605
7638
  if (!pkg2._urlSource) {
7606
7639
  pkg2._urlSource = { url };
7607
- writeFileSync6(pkgPath, JSON.stringify(pkg2, null, 2));
7640
+ writeFileSync7(pkgPath, JSON.stringify(pkg2, null, 2));
7608
7641
  }
7609
7642
  }
7610
7643
  } finally {
@@ -7622,10 +7655,10 @@ async function installFromUrl(url, name, targetDir) {
7622
7655
 
7623
7656
  // src/plugin/install-sources/marketplace.ts
7624
7657
  import {
7625
- existsSync as existsSync11,
7626
- mkdirSync as mkdirSync6,
7627
- writeFileSync as writeFileSync7,
7628
- readFileSync as readFileSync13,
7658
+ existsSync as existsSync12,
7659
+ mkdirSync as mkdirSync7,
7660
+ writeFileSync as writeFileSync8,
7661
+ readFileSync as readFileSync14,
7629
7662
  rmSync as rmSync6,
7630
7663
  cpSync as cpSync6
7631
7664
  } from "fs";
@@ -7647,12 +7680,12 @@ async function installFromMarketplace(pluginsDir, slug, options) {
7647
7680
  const plugin = detailData.data;
7648
7681
  const name = options?.name || String(plugin.slug || slug);
7649
7682
  const targetDir = resolve14(pluginsDir, name);
7650
- if (existsSync11(targetDir) && !options?.force) {
7683
+ if (existsSync12(targetDir) && !options?.force) {
7651
7684
  throw new Error(`Plugin "${name}" already exists. Use --force to overwrite.`);
7652
7685
  }
7653
- mkdirSync6(targetDir, { recursive: true });
7686
+ mkdirSync7(targetDir, { recursive: true });
7654
7687
  const tmpDir = join7(tmpdir4(), `xbrowser-marketplace-${Date.now()}`);
7655
- mkdirSync6(tmpDir, { recursive: true });
7688
+ mkdirSync7(tmpDir, { recursive: true });
7656
7689
  const realSlug = String(plugin.slug || slug);
7657
7690
  try {
7658
7691
  await downloadAndExtractMarketplaceTarball(baseUrl, realSlug, tmpDir, targetDir);
@@ -7682,14 +7715,14 @@ function isManifestArray(data) {
7682
7715
  return Array.isArray(data) && data.length > 0 && typeof data[0].path === "string" && typeof data[0].content === "string";
7683
7716
  }
7684
7717
  function extractManifestToDir(manifest, targetDir) {
7685
- if (existsSync11(targetDir)) {
7718
+ if (existsSync12(targetDir)) {
7686
7719
  rmSync6(targetDir, { recursive: true, force: true });
7687
7720
  }
7688
- mkdirSync6(targetDir, { recursive: true });
7721
+ mkdirSync7(targetDir, { recursive: true });
7689
7722
  for (const file of manifest) {
7690
7723
  const filePath = resolve14(targetDir, file.path);
7691
- mkdirSync6(dirname2(filePath), { recursive: true });
7692
- writeFileSync7(filePath, Buffer.from(file.content, "base64"));
7724
+ mkdirSync7(dirname2(filePath), { recursive: true });
7725
+ writeFileSync8(filePath, Buffer.from(file.content, "base64"));
7693
7726
  }
7694
7727
  }
7695
7728
  function tryParseAsGzippedManifest(buffer) {
@@ -7716,7 +7749,7 @@ async function downloadAndExtractMarketplaceTarball(baseUrl, slug, tmpDir, targe
7716
7749
  const redirectUrl = tarballRes.headers.get("location");
7717
7750
  const tarballPath = join7(tmpDir, `${slug}.tar.gz`);
7718
7751
  await downloadToFile(redirectUrl, tarballPath);
7719
- const buffer = readFileSync13(tarballPath);
7752
+ const buffer = readFileSync14(tarballPath);
7720
7753
  const manifest = tryParseAsGzippedManifest(buffer);
7721
7754
  if (manifest) {
7722
7755
  extractManifestToDir(manifest, targetDir);
@@ -7725,7 +7758,7 @@ async function downloadAndExtractMarketplaceTarball(baseUrl, slug, tmpDir, targe
7725
7758
  const extractDir = join7(tmpDir, "extracted");
7726
7759
  extractTarGz(tarballPath, extractDir);
7727
7760
  flattenPackageRoot(extractDir);
7728
- if (existsSync11(targetDir)) {
7761
+ if (existsSync12(targetDir)) {
7729
7762
  rmSync6(targetDir, { recursive: true, force: true });
7730
7763
  }
7731
7764
  cpSync6(extractDir, targetDir, { recursive: true, force: true });
@@ -7737,12 +7770,12 @@ async function downloadAndExtractMarketplaceTarball(baseUrl, slug, tmpDir, targe
7737
7770
  return;
7738
7771
  }
7739
7772
  const tarballPath = join7(tmpDir, `${slug}.tar.gz`);
7740
- writeFileSync7(tarballPath, buffer);
7773
+ writeFileSync8(tarballPath, buffer);
7741
7774
  try {
7742
7775
  const extractDir = join7(tmpDir, "extracted");
7743
7776
  extractTarGz(tarballPath, extractDir);
7744
7777
  flattenPackageRoot(extractDir);
7745
- if (existsSync11(targetDir)) {
7778
+ if (existsSync12(targetDir)) {
7746
7779
  rmSync6(targetDir, { recursive: true, force: true });
7747
7780
  }
7748
7781
  cpSync6(extractDir, targetDir, { recursive: true, force: true });
@@ -7778,24 +7811,24 @@ function writeMarketplacePackageJson(plugin, slug, name, baseUrl, targetDir) {
7778
7811
  }
7779
7812
  };
7780
7813
  const pkgPath = resolve14(targetDir, "package.json");
7781
- if (!existsSync11(pkgPath)) {
7782
- writeFileSync7(pkgPath, JSON.stringify(packageJson, null, 2));
7814
+ if (!existsSync12(pkgPath)) {
7815
+ writeFileSync8(pkgPath, JSON.stringify(packageJson, null, 2));
7783
7816
  } else {
7784
7817
  try {
7785
- const existing = JSON.parse(readFileSync13(pkgPath, "utf-8"));
7818
+ const existing = JSON.parse(readFileSync14(pkgPath, "utf-8"));
7786
7819
  const merged = {
7787
7820
  ...existing,
7788
7821
  xbrowser: { ...existing.xbrowser, ...packageJson.xbrowser },
7789
7822
  _marketplace: packageJson._marketplace
7790
7823
  };
7791
- writeFileSync7(pkgPath, JSON.stringify(merged, null, 2));
7824
+ writeFileSync8(pkgPath, JSON.stringify(merged, null, 2));
7792
7825
  } catch {
7793
- writeFileSync7(pkgPath, JSON.stringify(packageJson, null, 2));
7826
+ writeFileSync8(pkgPath, JSON.stringify(packageJson, null, 2));
7794
7827
  }
7795
7828
  }
7796
7829
  }
7797
7830
  function ensureIndexFile(plugin, name, targetDir) {
7798
- if (!existsSync11(resolve14(targetDir, "index.ts")) && !existsSync11(resolve14(targetDir, "index.js"))) {
7831
+ if (!existsSync12(resolve14(targetDir, "index.ts")) && !existsSync12(resolve14(targetDir, "index.js"))) {
7799
7832
  const commands = plugin.commands || [];
7800
7833
  const commandHandlers = commands.length > 0 ? commands.map((cmd) => {
7801
7834
  return [
@@ -7810,7 +7843,7 @@ function ensureIndexFile(plugin, name, targetDir) {
7810
7843
  ` handler: async () => ({ data: { message: 'Hello from ${name}!' }, tips: [] }),`,
7811
7844
  ` });`
7812
7845
  ].join("\n");
7813
- writeFileSync7(
7846
+ writeFileSync8(
7814
7847
  resolve14(targetDir, "index.ts"),
7815
7848
  [
7816
7849
  `import type { XCLIAPI } from '@dyyz1993/xcli-core';`,
@@ -7849,10 +7882,10 @@ var PluginInstaller = class {
7849
7882
  const type = this.detectSourceType(source);
7850
7883
  const name = options?.name || this.deriveName(source, type);
7851
7884
  const targetDir = resolve15(this.pluginsDir, name);
7852
- if (existsSync12(targetDir) && !options?.force) {
7885
+ if (existsSync13(targetDir) && !options?.force) {
7853
7886
  throw new Error(`Plugin "${name}" already exists. Use --force to overwrite.`);
7854
7887
  }
7855
- mkdirSync7(targetDir, { recursive: true });
7888
+ mkdirSync8(targetDir, { recursive: true });
7856
7889
  const resolvedSource = type === "npm" ? await resolveNpmPackageWithFallback(source) : source;
7857
7890
  switch (type) {
7858
7891
  case "local":
@@ -7910,7 +7943,7 @@ var PluginInstaller = class {
7910
7943
  */
7911
7944
  async uninstall(name) {
7912
7945
  const targetDir = resolve15(this.pluginsDir, name);
7913
- if (!existsSync12(targetDir)) {
7946
+ if (!existsSync13(targetDir)) {
7914
7947
  throw new Error(`Plugin "${name}" not found`);
7915
7948
  }
7916
7949
  rmSync7(targetDir, { recursive: true, force: true });
@@ -7921,7 +7954,7 @@ var PluginInstaller = class {
7921
7954
  * @returns Array of installed plugin information.
7922
7955
  */
7923
7956
  async list(_options) {
7924
- if (!existsSync12(this.pluginsDir)) return [];
7957
+ if (!existsSync13(this.pluginsDir)) return [];
7925
7958
  const entries = readdirSync3(this.pluginsDir, { withFileTypes: true });
7926
7959
  const plugins = [];
7927
7960
  for (const entry of entries) {
@@ -7929,7 +7962,7 @@ var PluginInstaller = class {
7929
7962
  const pluginPath = resolve15(this.pluginsDir, entry.name);
7930
7963
  const indexPath = resolve15(pluginPath, "index.ts");
7931
7964
  const indexJsPath = resolve15(pluginPath, "index.js");
7932
- if (!existsSync12(indexPath) && !existsSync12(indexJsPath)) continue;
7965
+ if (!existsSync13(indexPath) && !existsSync13(indexJsPath)) continue;
7933
7966
  const metadata = PluginMetadataParser.parseFromPackageJson(pluginPath);
7934
7967
  let source = "local";
7935
7968
  const pkg2 = readJsonFile(resolve15(pluginPath, "package.json"), {});
@@ -7956,10 +7989,10 @@ var PluginInstaller = class {
7956
7989
  }
7957
7990
  if (source.startsWith("file://")) {
7958
7991
  const filePath = decodeURIComponent(new URL(source).pathname);
7959
- if (existsSync12(filePath)) return "url";
7992
+ if (existsSync13(filePath)) return "url";
7960
7993
  }
7961
7994
  if (source.endsWith(".git") || source.includes("github.com/")) return "git";
7962
- if (existsSync12(resolve15(source))) return "local";
7995
+ if (existsSync13(resolve15(source))) return "local";
7963
7996
  return "npm";
7964
7997
  }
7965
7998
  deriveName(source, type) {
@@ -9988,7 +10021,7 @@ async function handleFilter(args, _mode) {
9988
10021
 
9989
10022
  // src/stdin.ts
9990
10023
  import { createInterface } from "readline";
9991
- import { readFileSync as readFileSync14 } from "fs";
10024
+ import { readFileSync as readFileSync15 } from "fs";
9992
10025
  async function readStdin2() {
9993
10026
  if (process.stdin.isTTY) return [];
9994
10027
  const lines = [];
@@ -10002,7 +10035,7 @@ async function readStdin2() {
10002
10035
  return lines;
10003
10036
  }
10004
10037
  function readCommandFile(filePath) {
10005
- const content = readFileSync14(filePath, "utf-8");
10038
+ const content = readFileSync15(filePath, "utf-8");
10006
10039
  return content.split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("#"));
10007
10040
  }
10008
10041
 
@@ -11270,11 +11303,14 @@ Run "xbrowser ${command} --help" to see available commands.`
11270
11303
  showCommandHelp(command, cmdEntry, { description: site.config.description, name: site.name, url: site.url }, mode);
11271
11304
  return;
11272
11305
  }
11273
- const params = parsePluginParams(cmdArgsForPlugin, cmdEntry.parameters, options);
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);
11274
11310
  if (options.target && !params._target) {
11275
11311
  params._target = options.target;
11276
11312
  }
11277
- const needsBrowser = cmdEntry.scope !== "global";
11313
+ const needsBrowser = cmdEntry.scope === "page";
11278
11314
  if (needsBrowser && !process.env.XBROWSER_DAEMON_WORKER) {
11279
11315
  const { forwardExec } = await import("./daemon-client-3IJD6X4B.js");
11280
11316
  const userTimeout = typeof params.timeout === "number" && params.timeout > 0 ? params.timeout * 1e3 + 3e4 : void 0;
@@ -11311,16 +11347,7 @@ Run "xbrowser ${command} --help" to see available commands.`
11311
11347
  browser: needsBrowser ? session.context.browser() : null,
11312
11348
  browserContext: needsBrowser ? session.context : null,
11313
11349
  sessionId: needsBrowser ? session.id : "",
11314
- storage: {
11315
- get: async (_key) => null,
11316
- set: async (_key, _value) => {
11317
- },
11318
- delete: async (_key) => {
11319
- },
11320
- clear: async () => {
11321
- },
11322
- keys: async () => []
11323
- },
11350
+ storage: getPluginStorage(command),
11324
11351
  output: { mode, showTips: true, color: true, emoji: true },
11325
11352
  error: (msg) => {
11326
11353
  outputError(msg);
@@ -12575,10 +12602,10 @@ var WSServer = class extends EventEmitter {
12575
12602
  }
12576
12603
  case "file_download": {
12577
12604
  try {
12578
- const { readFileSync: readFileSync16 } = await import("fs");
12605
+ const { readFileSync: readFileSync17 } = await import("fs");
12579
12606
  const { resolve: resolve16, basename: basename3 } = await import("path");
12580
12607
  const targetPath = resolve16(msg.path);
12581
- const data = readFileSync16(targetPath);
12608
+ const data = readFileSync17(targetPath);
12582
12609
  const base64 = data.toString("base64");
12583
12610
  const ext = targetPath.split(".").pop()?.toLowerCase() || "";
12584
12611
  const mimeMap = {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@xbrowser/cli",
3
- "version": "0.14.3",
4
- "description": "A self-contained browser automation CLI tool navigate, click, fill forms, extract data, record & replay sessions via YAML scripts",
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
- "browser plugin"
57
+ "ai-agent",
58
+ "browser plugin",
59
+ "browser-testing",
60
+ "chrome-devtools-protocol"
45
61
  ],
46
62
  "author": "dyyz1993",
47
63
  "license": "MIT",