@xbrowser/cli 1.8.3 → 1.8.5

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # xbrowser
2
2
 
3
- > **Browser automation CLI** for web scraping, headless browsing, SEO analysis, and AI agent workflows. 51 commands, 70+ plugins. A command-line alternative to Playwright, Puppeteer, and Selenium — **no code required**.
3
+ > **Browser automation CLI** for web scraping, headless browsing, SEO analysis, and AI agent workflows. 51 commands, 75 plugins. A command-line alternative to Playwright, Puppeteer, and Selenium — **no code required**.
4
4
 
5
5
  [![CI Status](https://github.com/dyyz1993/xbrowser/workflows/CI/badge.svg)](https://github.com/dyyz1993/xbrowser/actions)
6
6
  [![codecov](https://codecov.io/gh/dyyz1993/xbrowser/branch/master/graph/badge.svg)](https://codecov.io/gh/dyyz1993/xbrowser)
@@ -883,7 +883,7 @@ xbrowser/
883
883
  | **Setup** | `npm i -g` — 0 config | Install + browser download | Install + browser download | Install + WebDriver + drivers |
884
884
  | **Web Scraping** | Built-in (`scrape`, `crawl`, `map`) | Write custom scripts | Write custom scripts | Write custom scripts |
885
885
  | **Search** | Built-in multi-engine (`search`) | No | No | No |
886
- | **Plugin Ecosystem** | 70+ plugins | Limited | Limited | No |
886
+ | **Plugin Ecosystem** | 75 plugins | Limited | Limited | No |
887
887
  | **No Code Required** | ✅ CLI commands | ❌ Must write JS/TS | ❌ Must write JS/TS | ❌ Must write code |
888
888
  | **Headless Mode** | ✅ Default | ✅ | ✅ | ✅ |
889
889
  | **Record/Replay** | ✅ Built-in (`record`/`replay`) | ✅ Codegen | ❌ | ❌ |
@@ -20,7 +20,7 @@ import {
20
20
  saveSessionDiskMeta,
21
21
  setActivePage,
22
22
  touchSession
23
- } from "./chunk-6QGZN5U7.js";
23
+ } from "./chunk-TAHJCCME.js";
24
24
  import "./chunk-VJNMAWPZ.js";
25
25
  import "./chunk-TNEN6VQ2.js";
26
26
  import "./chunk-GDKLH7ZY.js";
@@ -20,7 +20,7 @@ import {
20
20
  saveSessionDiskMeta,
21
21
  setActivePage,
22
22
  touchSession
23
- } from "./chunk-IGWWPMEQ.js";
23
+ } from "./chunk-7EOQV6ZW.js";
24
24
  import "./chunk-VJNMAWPZ.js";
25
25
  import "./chunk-TNEN6VQ2.js";
26
26
  import "./chunk-GDKLH7ZY.js";
@@ -20,7 +20,7 @@ import {
20
20
  saveSessionDiskMeta,
21
21
  setActivePage,
22
22
  touchSession
23
- } from "./chunk-35DOHDTA.js";
23
+ } from "./chunk-U25ZKXBJ.js";
24
24
  import "./chunk-TNEN6VQ2.js";
25
25
  import "./chunk-GDKLH7ZY.js";
26
26
  import "./chunk-KFQGP6VL.js";
@@ -656,7 +656,7 @@ async function closeSessionByName(name) {
656
656
  } catch {
657
657
  }
658
658
  try {
659
- const { SessionRecorder } = await import("./session-recorder-RTDGURIJ.js");
659
+ const { SessionRecorder } = await import("./session-recorder-H2WPBCSW.js");
660
660
  SessionRecorder.cleanup(session.name);
661
661
  } catch {
662
662
  }
@@ -1725,6 +1725,16 @@ var SessionRecorder = class _SessionRecorder {
1725
1725
  await this.page.addInitScript(getSelectorGeneratorScript());
1726
1726
  await this.page.addInitScript(ACTION_SIGNAL_SCRIPT);
1727
1727
  await this.page.addInitScript(CHECKPOINT_OVERLAY_SCRIPT);
1728
+ this.context.on("request", this.handleRequest);
1729
+ this.context.on("response", this.handleResponse);
1730
+ this.context.on("page", this.handleNewPage);
1731
+ for (const p of this.context.pages()) {
1732
+ p.on("request", this.handleRequest);
1733
+ p.on("response", this.handleResponse);
1734
+ }
1735
+ this.page.on("framenavigated", this.handleFrameNavigated);
1736
+ this.page.on("dialog", this.handleDialog);
1737
+ this.page.on("filechooser", this.handleFileChooser);
1728
1738
  if (url) {
1729
1739
  await this.page.goto(url, { waitUntil: "domcontentloaded", timeout: 3e4 });
1730
1740
  this.startUrl = url;
@@ -1740,17 +1750,7 @@ var SessionRecorder = class _SessionRecorder {
1740
1750
  };
1741
1751
  writeFileSync2(this.controlFilePath, JSON.stringify(control, null, 2), "utf-8");
1742
1752
  await this.injectActionScript(this.page);
1743
- this.context.on("request", this.handleRequest);
1744
- this.context.on("response", this.handleResponse);
1745
- this.context.on("page", this.handleNewPage);
1746
- for (const p of this.context.pages()) {
1747
- p.on("request", this.handleRequest);
1748
- p.on("response", this.handleResponse);
1749
- }
1750
1753
  this.context.on("page", this.handleNewPage);
1751
- this.page.on("framenavigated", this.handleFrameNavigated);
1752
- this.page.on("dialog", this.handleDialog);
1753
- this.page.on("filechooser", this.handleFileChooser);
1754
1754
  this.pollTimer = setInterval(() => void this.pollActions(), 200);
1755
1755
  this.flushTimer = setInterval(() => this.flushToDisk(), 5e3);
1756
1756
  }
@@ -2066,7 +2066,7 @@ var SessionRecorder = class _SessionRecorder {
2066
2066
  this.activePages.delete(page);
2067
2067
  });
2068
2068
  page.on("framenavigated", async (frame) => {
2069
- if (frame !== frame.page().mainFrame()) return;
2069
+ if (frame.isDetached()) return;
2070
2070
  const url = frame.url();
2071
2071
  if (url && url !== "about:blank" && !url.startsWith("chrome")) {
2072
2072
  try {
@@ -2077,7 +2077,7 @@ var SessionRecorder = class _SessionRecorder {
2077
2077
  });
2078
2078
  };
2079
2079
  handleFrameNavigated = (frame) => {
2080
- if (frame !== frame.page().mainFrame()) return;
2080
+ if (frame.isDetached()) return;
2081
2081
  const newUrl = frame.url();
2082
2082
  this.contextCounter++;
2083
2083
  this.contextChanges.push({
@@ -2021,7 +2021,7 @@ async function closeSessionByName(name) {
2021
2021
  } catch {
2022
2022
  }
2023
2023
  try {
2024
- const { SessionRecorder } = await import("./session-recorder-YI7YYM36.js");
2024
+ const { SessionRecorder } = await import("./session-recorder-CV5EJSDY.js");
2025
2025
  SessionRecorder.cleanup(session.name);
2026
2026
  } catch {
2027
2027
  }
@@ -4965,7 +4965,7 @@ async function closeSessionByName(name) {
4965
4965
  } catch {
4966
4966
  }
4967
4967
  try {
4968
- const { SessionRecorder } = await import("./session-recorder-RTDGURIJ.js");
4968
+ const { SessionRecorder } = await import("./session-recorder-H2WPBCSW.js");
4969
4969
  SessionRecorder.cleanup(session.name);
4970
4970
  } catch {
4971
4971
  }
@@ -1328,6 +1328,16 @@ var SessionRecorder = class _SessionRecorder {
1328
1328
  await this.page.addInitScript(getSelectorGeneratorScript());
1329
1329
  await this.page.addInitScript(ACTION_SIGNAL_SCRIPT);
1330
1330
  await this.page.addInitScript(CHECKPOINT_OVERLAY_SCRIPT);
1331
+ this.context.on("request", this.handleRequest);
1332
+ this.context.on("response", this.handleResponse);
1333
+ this.context.on("page", this.handleNewPage);
1334
+ for (const p of this.context.pages()) {
1335
+ p.on("request", this.handleRequest);
1336
+ p.on("response", this.handleResponse);
1337
+ }
1338
+ this.page.on("framenavigated", this.handleFrameNavigated);
1339
+ this.page.on("dialog", this.handleDialog);
1340
+ this.page.on("filechooser", this.handleFileChooser);
1331
1341
  if (url) {
1332
1342
  await this.page.goto(url, { waitUntil: "domcontentloaded", timeout: 3e4 });
1333
1343
  this.startUrl = url;
@@ -1343,17 +1353,7 @@ var SessionRecorder = class _SessionRecorder {
1343
1353
  };
1344
1354
  writeFileSync(this.controlFilePath, JSON.stringify(control, null, 2), "utf-8");
1345
1355
  await this.injectActionScript(this.page);
1346
- this.context.on("request", this.handleRequest);
1347
- this.context.on("response", this.handleResponse);
1348
- this.context.on("page", this.handleNewPage);
1349
- for (const p of this.context.pages()) {
1350
- p.on("request", this.handleRequest);
1351
- p.on("response", this.handleResponse);
1352
- }
1353
1356
  this.context.on("page", this.handleNewPage);
1354
- this.page.on("framenavigated", this.handleFrameNavigated);
1355
- this.page.on("dialog", this.handleDialog);
1356
- this.page.on("filechooser", this.handleFileChooser);
1357
1357
  this.pollTimer = setInterval(() => void this.pollActions(), 200);
1358
1358
  this.flushTimer = setInterval(() => this.flushToDisk(), 5e3);
1359
1359
  }
@@ -1669,7 +1669,7 @@ var SessionRecorder = class _SessionRecorder {
1669
1669
  this.activePages.delete(page);
1670
1670
  });
1671
1671
  page.on("framenavigated", async (frame) => {
1672
- if (frame !== frame.page().mainFrame()) return;
1672
+ if (frame.isDetached()) return;
1673
1673
  const url = frame.url();
1674
1674
  if (url && url !== "about:blank" && !url.startsWith("chrome")) {
1675
1675
  try {
@@ -1680,7 +1680,7 @@ var SessionRecorder = class _SessionRecorder {
1680
1680
  });
1681
1681
  };
1682
1682
  handleFrameNavigated = (frame) => {
1683
- if (frame !== frame.page().mainFrame()) return;
1683
+ if (frame.isDetached()) return;
1684
1684
  const newUrl = frame.url();
1685
1685
  this.contextCounter++;
1686
1686
  this.contextChanges.push({
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  SessionRecorder
4
- } from "./chunk-ACFE6PKF.js";
4
+ } from "./chunk-XU5WOJIV.js";
5
5
  import {
6
6
  addKnownIssue,
7
7
  getKnowledgePath,
@@ -25,7 +25,7 @@ import {
25
25
  resolveLaunchOpts,
26
26
  saveSessionDiskMeta,
27
27
  setActivePage
28
- } from "./chunk-35DOHDTA.js";
28
+ } from "./chunk-U25ZKXBJ.js";
29
29
  import "./chunk-TNEN6VQ2.js";
30
30
  import {
31
31
  forwardCommandLog,
@@ -7207,7 +7207,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
7207
7207
  }
7208
7208
  let targetPageOverride = null;
7209
7209
  if (_target && extraOpts?.cdpEndpoint) {
7210
- const { findTargetPage } = await import("./browser-KVTJQKBA.js");
7210
+ const { findTargetPage } = await import("./browser-PUAJ3AHG.js");
7211
7211
  targetPageOverride = await findTargetPage(extraOpts.cdpEndpoint, _target);
7212
7212
  if (!targetPageOverride) {
7213
7213
  return errorResult(`Target "${_target}" not found. Use 'xbrowser targets --cdp ${extraOpts.cdpEndpoint}' to list available pages.`);
@@ -7945,7 +7945,8 @@ import {
7945
7945
  rmSync as rmSync6,
7946
7946
  copyFileSync,
7947
7947
  cpSync as cpSync6,
7948
- readFileSync as readFileSync8
7948
+ readFileSync as readFileSync8,
7949
+ writeFileSync as writeFileSync10
7949
7950
  } from "fs";
7950
7951
  import { resolve as resolve8, basename as basename2, dirname as dirname3 } from "path";
7951
7952
  import { homedir as homedir8 } from "os";
@@ -8439,26 +8440,26 @@ var PluginInstaller = class {
8439
8440
  const resolvedSource = type === "npm" ? await resolveNpmPackageWithFallback(source) : source;
8440
8441
  switch (type) {
8441
8442
  case "local":
8442
- return await installFromLocal(source, name, targetDir).then((r) => {
8443
- this.fixSharedDeps(targetDir);
8443
+ return await installFromLocal(source, name, targetDir).then(async (r) => {
8444
+ await this.fixSharedDeps(targetDir);
8444
8445
  ensurePluginDependencies(this.pluginsDir);
8445
8446
  return r;
8446
8447
  });
8447
8448
  case "npm":
8448
- return await installFromNpm(resolvedSource, name, targetDir).then((r) => {
8449
- this.fixSharedDeps(targetDir);
8449
+ return await installFromNpm(resolvedSource, name, targetDir).then(async (r) => {
8450
+ await this.fixSharedDeps(targetDir);
8450
8451
  ensurePluginDependencies(this.pluginsDir);
8451
8452
  return r;
8452
8453
  });
8453
8454
  case "git":
8454
- return await installFromGit(source, name, targetDir).then((r) => {
8455
- this.fixSharedDeps(targetDir);
8455
+ return await installFromGit(source, name, targetDir).then(async (r) => {
8456
+ await this.fixSharedDeps(targetDir);
8456
8457
  ensurePluginDependencies(this.pluginsDir);
8457
8458
  return r;
8458
8459
  });
8459
8460
  case "url":
8460
- return await installFromUrl(source, name, targetDir).then((r) => {
8461
- this.fixSharedDeps(targetDir);
8461
+ return await installFromUrl(source, name, targetDir).then(async (r) => {
8462
+ await this.fixSharedDeps(targetDir);
8462
8463
  ensurePluginDependencies(this.pluginsDir);
8463
8464
  return r;
8464
8465
  });
@@ -8473,7 +8474,7 @@ var PluginInstaller = class {
8473
8474
  * copies the missing files from the local repository's `.xcli/plugins/shared/`
8474
8475
  * directory (if available).
8475
8476
  */
8476
- fixSharedDeps(pluginDir) {
8477
+ async fixSharedDeps(pluginDir) {
8477
8478
  const indexPath = resolve8(pluginDir, "index.ts");
8478
8479
  if (!existsSync10(indexPath)) return;
8479
8480
  let content;
@@ -8510,8 +8511,33 @@ var PluginInstaller = class {
8510
8511
  }
8511
8512
  }
8512
8513
  if (!sourceSharedDir) {
8513
- console.warn(`\u26A0\uFE0F Plugin "${basename2(pluginDir)}" imports shared files but they are missing: ${toCopy.join(", ")}`);
8514
- console.warn(` To fix: install the "shared" plugin or copy .xcli/plugins/shared/ to ~/.xbrowser/plugins/shared/`);
8514
+ const GITHUB_RAW = "https://raw.githubusercontent.com/dyyz1993/xbrowser/master/.xcli/plugins/shared";
8515
+ mkdirSync8(sharedDir, { recursive: true });
8516
+ let allDownloaded = true;
8517
+ for (const file of toCopy) {
8518
+ try {
8519
+ const url = `${GITHUB_RAW}/${file}`;
8520
+ const resp = await fetch(url, { signal: AbortSignal.timeout(1e4) });
8521
+ if (resp.ok) {
8522
+ const content2 = await resp.text();
8523
+ const dst = resolve8(sharedDir, file);
8524
+ const dstDir = dirname3(dst);
8525
+ if (!existsSync10(dstDir)) mkdirSync8(dstDir, { recursive: true });
8526
+ writeFileSync10(dst, content2, "utf-8");
8527
+ console.log(`\u2705 Downloaded shared/${file} from GitHub`);
8528
+ } else {
8529
+ allDownloaded = false;
8530
+ break;
8531
+ }
8532
+ } catch {
8533
+ allDownloaded = false;
8534
+ break;
8535
+ }
8536
+ }
8537
+ if (!allDownloaded) {
8538
+ console.warn(`\u26A0\uFE0F Plugin "${basename2(pluginDir)}" imports shared files but they are missing: ${toCopy.join(", ")}`);
8539
+ console.warn(` Could not download from GitHub. Try: git clone https://github.com/dyyz1993/xbrowser.git && cp -r xbrowser/.xcli/plugins/shared/ ~/.xbrowser/plugins/shared/`);
8540
+ }
8515
8541
  return;
8516
8542
  }
8517
8543
  mkdirSync8(sharedDir, { recursive: true });
@@ -10244,9 +10270,9 @@ async function handleBrowserCommand(command, args, options, sessionName, mode, c
10244
10270
  }
10245
10271
  const outputFile = options.output;
10246
10272
  if (outputFile && result.success && result.data) {
10247
- const { writeFileSync: writeFileSync11 } = await import("fs");
10273
+ const { writeFileSync: writeFileSync12 } = await import("fs");
10248
10274
  const content = typeof result.data === "string" ? result.data : result.data.content || result.data.text || JSON.stringify(result.data, null, 2);
10249
- writeFileSync11(outputFile, content, "utf-8");
10275
+ writeFileSync12(outputFile, content, "utf-8");
10250
10276
  console.log(`
10251
10277
  \u{1F4C4} Written to ${outputFile}`);
10252
10278
  }
@@ -11126,9 +11152,9 @@ async function handleFilter(args, _mode, options) {
11126
11152
  console.log(` Original: ${result.originalCount}, After: ${result.filteredCount}, Removed: ${result.removed} (${result.percentage}%)`);
11127
11153
  }
11128
11154
  async function handleGeneratePlugin(sessionName, pluginName, outputDir) {
11129
- const { SessionRecorder: SessionRecorder2 } = await import("./session-recorder-RTDGURIJ.js");
11155
+ const { SessionRecorder: SessionRecorder2 } = await import("./session-recorder-H2WPBCSW.js");
11130
11156
  const { readSiteKnowledge: readSiteKnowledge2, toMarkdown } = await import("./site-knowledge-SYC6VCDB.js");
11131
- const { mkdirSync: mkdirSync10, writeFileSync: writeFileSync11 } = await import("fs");
11157
+ const { mkdirSync: mkdirSync10, writeFileSync: writeFileSync12 } = await import("fs");
11132
11158
  const { join: join14 } = await import("path");
11133
11159
  const data = SessionRecorder2.readData(sessionName);
11134
11160
  if (!data) {
@@ -11146,9 +11172,9 @@ async function handleGeneratePlugin(sessionName, pluginName, outputDir) {
11146
11172
  const knowledgeMd = knowledge ? toMarkdown(knowledge) : "";
11147
11173
  const pluginCode = generatePluginCode(finalPluginName, domain, data, knowledgeMd);
11148
11174
  mkdirSync10(join14(finalOutputDir), { recursive: true });
11149
- writeFileSync11(join14(finalOutputDir, "index.ts"), pluginCode, "utf-8");
11175
+ writeFileSync12(join14(finalOutputDir, "index.ts"), pluginCode, "utf-8");
11150
11176
  if (knowledgeMd) {
11151
- writeFileSync11(join14(finalOutputDir, "SITE_KNOWLEDGE.md"), knowledgeMd, "utf-8");
11177
+ writeFileSync12(join14(finalOutputDir, "SITE_KNOWLEDGE.md"), knowledgeMd, "utf-8");
11152
11178
  }
11153
11179
  console.log("");
11154
11180
  console.log("=== Plugin Generated ===");
@@ -13333,7 +13359,7 @@ async function main() {
13333
13359
  const command = process.argv[2];
13334
13360
  const isLongRunning = command === "preview" || command === "serve";
13335
13361
  if (!isLongRunning) {
13336
- const { ensureProcessCanExit } = await import("./browser-KVTJQKBA.js");
13362
+ const { ensureProcessCanExit } = await import("./browser-PUAJ3AHG.js");
13337
13363
  await ensureProcessCanExit().catch(() => {
13338
13364
  });
13339
13365
  process.exit(exitCode);
@@ -5,7 +5,7 @@ import {
5
5
  } from "./chunk-VEDJ5XSQ.js";
6
6
  import {
7
7
  SessionRecorder
8
- } from "./chunk-2SVQTI2O.js";
8
+ } from "./chunk-BALOM2HW.js";
9
9
  import {
10
10
  closeEphemeralContext,
11
11
  closeSessionByName,
@@ -21,7 +21,7 @@ import {
21
21
  resolveLaunchOpts,
22
22
  saveSessionDiskMeta,
23
23
  setActivePage
24
- } from "./chunk-6QGZN5U7.js";
24
+ } from "./chunk-TAHJCCME.js";
25
25
  import "./chunk-VJNMAWPZ.js";
26
26
  import "./chunk-TNEN6VQ2.js";
27
27
  import {
@@ -6744,7 +6744,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
6744
6744
  }
6745
6745
  let targetPageOverride = null;
6746
6746
  if (_target && extraOpts?.cdpEndpoint) {
6747
- const { findTargetPage } = await import("./browser-4PIVOYCW.js");
6747
+ const { findTargetPage } = await import("./browser-NSSCH4MJ.js");
6748
6748
  targetPageOverride = await findTargetPage(extraOpts.cdpEndpoint, _target);
6749
6749
  if (!targetPageOverride) {
6750
6750
  return errorResult(`Target "${_target}" not found. Use 'xbrowser targets --cdp ${extraOpts.cdpEndpoint}' to list available pages.`);
package/dist/index.js CHANGED
@@ -57,7 +57,7 @@ import {
57
57
  } from "./chunk-GJAV3QGG.js";
58
58
  import {
59
59
  SessionRecorder
60
- } from "./chunk-ACFE6PKF.js";
60
+ } from "./chunk-XU5WOJIV.js";
61
61
  import {
62
62
  addKnownIssue,
63
63
  getKnowledgePath,
@@ -82,7 +82,7 @@ import {
82
82
  resolveLaunchOpts,
83
83
  saveSessionDiskMeta,
84
84
  setActivePage
85
- } from "./chunk-IGWWPMEQ.js";
85
+ } from "./chunk-7EOQV6ZW.js";
86
86
  import "./chunk-VJNMAWPZ.js";
87
87
  import "./chunk-TNEN6VQ2.js";
88
88
  import {
@@ -7527,7 +7527,7 @@ async function executeCommand(commandName, params, sessionName = "default", extr
7527
7527
  }
7528
7528
  let targetPageOverride = null;
7529
7529
  if (_target && extraOpts?.cdpEndpoint) {
7530
- const { findTargetPage } = await import("./browser-RJIRI2H5.js");
7530
+ const { findTargetPage } = await import("./browser-PTELW3WS.js");
7531
7531
  targetPageOverride = await findTargetPage(extraOpts.cdpEndpoint, _target);
7532
7532
  if (!targetPageOverride) {
7533
7533
  return errorResult(`Target "${_target}" not found. Use 'xbrowser targets --cdp ${extraOpts.cdpEndpoint}' to list available pages.`);
@@ -8263,7 +8263,8 @@ import {
8263
8263
  rmSync as rmSync6,
8264
8264
  copyFileSync,
8265
8265
  cpSync as cpSync6,
8266
- readFileSync as readFileSync8
8266
+ readFileSync as readFileSync8,
8267
+ writeFileSync as writeFileSync10
8267
8268
  } from "fs";
8268
8269
  import { resolve as resolve8, basename as basename2, dirname as dirname3 } from "path";
8269
8270
  import { homedir as homedir7 } from "os";
@@ -8757,26 +8758,26 @@ var PluginInstaller = class {
8757
8758
  const resolvedSource = type === "npm" ? await resolveNpmPackageWithFallback(source) : source;
8758
8759
  switch (type) {
8759
8760
  case "local":
8760
- return await installFromLocal(source, name, targetDir).then((r) => {
8761
- this.fixSharedDeps(targetDir);
8761
+ return await installFromLocal(source, name, targetDir).then(async (r) => {
8762
+ await this.fixSharedDeps(targetDir);
8762
8763
  ensurePluginDependencies(this.pluginsDir);
8763
8764
  return r;
8764
8765
  });
8765
8766
  case "npm":
8766
- return await installFromNpm(resolvedSource, name, targetDir).then((r) => {
8767
- this.fixSharedDeps(targetDir);
8767
+ return await installFromNpm(resolvedSource, name, targetDir).then(async (r) => {
8768
+ await this.fixSharedDeps(targetDir);
8768
8769
  ensurePluginDependencies(this.pluginsDir);
8769
8770
  return r;
8770
8771
  });
8771
8772
  case "git":
8772
- return await installFromGit(source, name, targetDir).then((r) => {
8773
- this.fixSharedDeps(targetDir);
8773
+ return await installFromGit(source, name, targetDir).then(async (r) => {
8774
+ await this.fixSharedDeps(targetDir);
8774
8775
  ensurePluginDependencies(this.pluginsDir);
8775
8776
  return r;
8776
8777
  });
8777
8778
  case "url":
8778
- return await installFromUrl(source, name, targetDir).then((r) => {
8779
- this.fixSharedDeps(targetDir);
8779
+ return await installFromUrl(source, name, targetDir).then(async (r) => {
8780
+ await this.fixSharedDeps(targetDir);
8780
8781
  ensurePluginDependencies(this.pluginsDir);
8781
8782
  return r;
8782
8783
  });
@@ -8791,7 +8792,7 @@ var PluginInstaller = class {
8791
8792
  * copies the missing files from the local repository's `.xcli/plugins/shared/`
8792
8793
  * directory (if available).
8793
8794
  */
8794
- fixSharedDeps(pluginDir) {
8795
+ async fixSharedDeps(pluginDir) {
8795
8796
  const indexPath = resolve8(pluginDir, "index.ts");
8796
8797
  if (!existsSync10(indexPath)) return;
8797
8798
  let content;
@@ -8828,8 +8829,33 @@ var PluginInstaller = class {
8828
8829
  }
8829
8830
  }
8830
8831
  if (!sourceSharedDir) {
8831
- console.warn(`\u26A0\uFE0F Plugin "${basename2(pluginDir)}" imports shared files but they are missing: ${toCopy.join(", ")}`);
8832
- console.warn(` To fix: install the "shared" plugin or copy .xcli/plugins/shared/ to ~/.xbrowser/plugins/shared/`);
8832
+ const GITHUB_RAW = "https://raw.githubusercontent.com/dyyz1993/xbrowser/master/.xcli/plugins/shared";
8833
+ mkdirSync8(sharedDir, { recursive: true });
8834
+ let allDownloaded = true;
8835
+ for (const file of toCopy) {
8836
+ try {
8837
+ const url = `${GITHUB_RAW}/${file}`;
8838
+ const resp = await fetch(url, { signal: AbortSignal.timeout(1e4) });
8839
+ if (resp.ok) {
8840
+ const content2 = await resp.text();
8841
+ const dst = resolve8(sharedDir, file);
8842
+ const dstDir = dirname3(dst);
8843
+ if (!existsSync10(dstDir)) mkdirSync8(dstDir, { recursive: true });
8844
+ writeFileSync10(dst, content2, "utf-8");
8845
+ console.log(`\u2705 Downloaded shared/${file} from GitHub`);
8846
+ } else {
8847
+ allDownloaded = false;
8848
+ break;
8849
+ }
8850
+ } catch {
8851
+ allDownloaded = false;
8852
+ break;
8853
+ }
8854
+ }
8855
+ if (!allDownloaded) {
8856
+ console.warn(`\u26A0\uFE0F Plugin "${basename2(pluginDir)}" imports shared files but they are missing: ${toCopy.join(", ")}`);
8857
+ console.warn(` Could not download from GitHub. Try: git clone https://github.com/dyyz1993/xbrowser.git && cp -r xbrowser/.xcli/plugins/shared/ ~/.xbrowser/plugins/shared/`);
8858
+ }
8833
8859
  return;
8834
8860
  }
8835
8861
  mkdirSync8(sharedDir, { recursive: true });
@@ -10567,9 +10593,9 @@ async function handleBrowserCommand(command, args, options, sessionName, mode, c
10567
10593
  }
10568
10594
  const outputFile = options.output;
10569
10595
  if (outputFile && result.success && result.data) {
10570
- const { writeFileSync: writeFileSync12 } = await import("fs");
10596
+ const { writeFileSync: writeFileSync13 } = await import("fs");
10571
10597
  const content = typeof result.data === "string" ? result.data : result.data.content || result.data.text || JSON.stringify(result.data, null, 2);
10572
- writeFileSync12(outputFile, content, "utf-8");
10598
+ writeFileSync13(outputFile, content, "utf-8");
10573
10599
  console.log(`
10574
10600
  \u{1F4C4} Written to ${outputFile}`);
10575
10601
  }
@@ -11449,9 +11475,9 @@ async function handleFilter(args, _mode, options) {
11449
11475
  console.log(` Original: ${result.originalCount}, After: ${result.filteredCount}, Removed: ${result.removed} (${result.percentage}%)`);
11450
11476
  }
11451
11477
  async function handleGeneratePlugin(sessionName, pluginName, outputDir) {
11452
- const { SessionRecorder: SessionRecorder2 } = await import("./session-recorder-RTDGURIJ.js");
11478
+ const { SessionRecorder: SessionRecorder2 } = await import("./session-recorder-H2WPBCSW.js");
11453
11479
  const { readSiteKnowledge: readSiteKnowledge2, toMarkdown } = await import("./site-knowledge-SYC6VCDB.js");
11454
- const { mkdirSync: mkdirSync11, writeFileSync: writeFileSync12 } = await import("fs");
11480
+ const { mkdirSync: mkdirSync11, writeFileSync: writeFileSync13 } = await import("fs");
11455
11481
  const { join: join14 } = await import("path");
11456
11482
  const data = SessionRecorder2.readData(sessionName);
11457
11483
  if (!data) {
@@ -11469,9 +11495,9 @@ async function handleGeneratePlugin(sessionName, pluginName, outputDir) {
11469
11495
  const knowledgeMd = knowledge ? toMarkdown(knowledge) : "";
11470
11496
  const pluginCode = generatePluginCode(finalPluginName, domain, data, knowledgeMd);
11471
11497
  mkdirSync11(join14(finalOutputDir), { recursive: true });
11472
- writeFileSync12(join14(finalOutputDir, "index.ts"), pluginCode, "utf-8");
11498
+ writeFileSync13(join14(finalOutputDir, "index.ts"), pluginCode, "utf-8");
11473
11499
  if (knowledgeMd) {
11474
- writeFileSync12(join14(finalOutputDir, "SITE_KNOWLEDGE.md"), knowledgeMd, "utf-8");
11500
+ writeFileSync13(join14(finalOutputDir, "SITE_KNOWLEDGE.md"), knowledgeMd, "utf-8");
11475
11501
  }
11476
11502
  console.log("");
11477
11503
  console.log("=== Plugin Generated ===");
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  SessionRecorder
3
- } from "./chunk-2SVQTI2O.js";
3
+ } from "./chunk-BALOM2HW.js";
4
4
  import "./chunk-KFQGP6VL.js";
5
5
  export {
6
6
  SessionRecorder
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  SessionRecorder
3
- } from "./chunk-ACFE6PKF.js";
3
+ } from "./chunk-XU5WOJIV.js";
4
4
  import "./chunk-OZKD3W4X.js";
5
5
  import "./chunk-KFQGP6VL.js";
6
6
  export {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xbrowser/cli",
3
- "version": "1.8.3",
3
+ "version": "1.8.5",
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": {