@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 CHANGED
@@ -174,7 +174,8 @@ import {
174
174
  isCommandResult,
175
175
  configureArchiveStore,
176
176
  appendCommandToArchive,
177
- checkGuard
177
+ checkGuard,
178
+ PluginStorage
178
179
  } from "@dyyz1993/xcli-core";
179
180
 
180
181
  // src/commands/navigation.ts
@@ -5652,7 +5653,7 @@ import { join as join2 } from "path";
5652
5653
  import { execSync as execSync6 } from "child_process";
5653
5654
  var SHARED_PLUGIN_DEPENDENCIES = {
5654
5655
  "zod": "^3.24.0",
5655
- "@dyyz1993/xcli-core": "^0.9.2"
5656
+ "@dyyz1993/xcli-core": "^0.12.1"
5656
5657
  };
5657
5658
  function ensurePluginDependencies(pluginsDir) {
5658
5659
  const zodPath = join2(pluginsDir, "node_modules", "zod");
@@ -6236,45 +6237,13 @@ async function loadHooks() {
6236
6237
  // src/executor.ts
6237
6238
  import { homedir as homedir3 } from "os";
6238
6239
  import { join as join3 } from "path";
6239
- import { existsSync as existsSync6, readFileSync as readFileSync10, writeFileSync as writeFileSync5, mkdirSync as mkdirSync3 } from "fs";
6240
6240
  var NAVIGATION_COMMANDS = /* @__PURE__ */ new Set(["goto", "back", "forward", "refresh"]);
6241
6241
  var snapshotHintShown = /* @__PURE__ */ new WeakSet();
6242
6242
  var STORAGE_DIR = join3(homedir3(), ".xbrowser", "storage");
6243
6243
  var storageCache = /* @__PURE__ */ new Map();
6244
6244
  function getPluginStorage(pluginName) {
6245
6245
  if (!storageCache.has(pluginName)) {
6246
- const filePath = join3(STORAGE_DIR, `${pluginName}.json`);
6247
- let data = {};
6248
- const load3 = () => {
6249
- if (existsSync6(filePath)) {
6250
- try {
6251
- data = JSON.parse(readFileSync10(filePath, "utf-8"));
6252
- } catch {
6253
- data = {};
6254
- }
6255
- }
6256
- };
6257
- const save = () => {
6258
- mkdirSync3(STORAGE_DIR, { recursive: true });
6259
- writeFileSync5(filePath, JSON.stringify(data, null, 2), "utf-8");
6260
- };
6261
- load3();
6262
- storageCache.set(pluginName, {
6263
- get: async (key) => data[key] ?? null,
6264
- set: async (key, value) => {
6265
- data[key] = value;
6266
- save();
6267
- },
6268
- delete: async (key) => {
6269
- delete data[key];
6270
- save();
6271
- },
6272
- clear: async () => {
6273
- data = {};
6274
- save();
6275
- },
6276
- keys: async () => Object.keys(data)
6277
- });
6246
+ storageCache.set(pluginName, new PluginStorage(pluginName, STORAGE_DIR));
6278
6247
  }
6279
6248
  return storageCache.get(pluginName);
6280
6249
  }
@@ -6371,6 +6340,10 @@ async function executeCommand(commandName, params, sessionName = "default", extr
6371
6340
  session = await createSession(sessionName, params.url, {
6372
6341
  cdpEndpoint: extraOpts?.cdpEndpoint
6373
6342
  });
6343
+ } else if (command.scope === "browser") {
6344
+ session = await createSession(sessionName, void 0, {
6345
+ cdpEndpoint: extraOpts?.cdpEndpoint
6346
+ });
6374
6347
  } else if (command.scope !== "project") {
6375
6348
  return errorResult(
6376
6349
  `Session '${sessionName}' not found. Run "xbrowser session open <url>" first.`
@@ -6901,7 +6874,7 @@ var sessionKillBuiltin = {
6901
6874
  };
6902
6875
 
6903
6876
  // src/config.ts
6904
- import { existsSync as existsSync7, mkdirSync as mkdirSync4, writeFileSync as writeFileSync6 } from "fs";
6877
+ import { existsSync as existsSync6, mkdirSync as mkdirSync3, writeFileSync as writeFileSync5 } from "fs";
6905
6878
  import { join as join4 } from "path";
6906
6879
  import { homedir as homedir4, tmpdir } from "os";
6907
6880
  function getConfigFile() {
@@ -6909,14 +6882,14 @@ function getConfigFile() {
6909
6882
  }
6910
6883
  function loadConfig() {
6911
6884
  const configFile = getConfigFile();
6912
- if (!existsSync7(configFile)) return {};
6885
+ if (!existsSync6(configFile)) return {};
6913
6886
  return readJsonFile(configFile, {});
6914
6887
  }
6915
6888
  function saveConfig(config) {
6916
6889
  const dir = join4(homedir4() || tmpdir(), ".xbrowser");
6917
6890
  const configFile = getConfigFile();
6918
- if (!existsSync7(dir)) mkdirSync4(dir, { recursive: true });
6919
- writeFileSync6(configFile, JSON.stringify(config, null, 2), "utf-8");
6891
+ if (!existsSync6(dir)) mkdirSync3(dir, { recursive: true });
6892
+ writeFileSync5(configFile, JSON.stringify(config, null, 2), "utf-8");
6920
6893
  }
6921
6894
  function getConfigValue(key) {
6922
6895
  return loadConfig()[key];
@@ -7024,26 +6997,26 @@ var configBuiltin = {
7024
6997
 
7025
6998
  // src/plugin/installer.ts
7026
6999
  import {
7027
- existsSync as existsSync14,
7000
+ existsSync as existsSync13,
7028
7001
  readdirSync as readdirSync3,
7029
- mkdirSync as mkdirSync9,
7002
+ mkdirSync as mkdirSync8,
7030
7003
  rmSync as rmSync7
7031
7004
  } from "fs";
7032
7005
  import { resolve as resolve15, basename as basename2 } from "path";
7033
7006
  import { homedir as homedir5 } from "os";
7034
7007
 
7035
7008
  // src/plugin/install-sources/local.ts
7036
- import { existsSync as existsSync9, cpSync as cpSync2, rmSync as rmSync2 } from "fs";
7009
+ import { existsSync as existsSync8, cpSync as cpSync2, rmSync as rmSync2 } from "fs";
7037
7010
  import { resolve as resolve10 } from "path";
7038
7011
 
7039
7012
  // src/plugin/install-utils.ts
7040
7013
  import {
7041
- existsSync as existsSync8,
7014
+ existsSync as existsSync7,
7042
7015
  readdirSync as readdirSync2,
7043
7016
  cpSync,
7044
7017
  rmSync,
7045
- mkdirSync as mkdirSync5,
7046
- readFileSync as readFileSync11,
7018
+ mkdirSync as mkdirSync4,
7019
+ readFileSync as readFileSync10,
7047
7020
  createWriteStream
7048
7021
  } from "fs";
7049
7022
  import { resolve as resolve9 } from "path";
@@ -7114,7 +7087,7 @@ async function downloadToFile(url, destPath) {
7114
7087
  await pipeline(nodeStream, createWriteStream(destPath));
7115
7088
  }
7116
7089
  function extractTarGz(tarballPath, targetDir) {
7117
- mkdirSync5(targetDir, { recursive: true });
7090
+ mkdirSync4(targetDir, { recursive: true });
7118
7091
  execSync7(`tar -xzf "${tarballPath}" -C "${targetDir}"`, { stdio: "pipe" });
7119
7092
  }
7120
7093
  function flattenPackageRoot(targetDir) {
@@ -7135,18 +7108,18 @@ function flattenPackageRoot(targetDir) {
7135
7108
  async function verifyPlugin(dir) {
7136
7109
  const warnings = [];
7137
7110
  const indexPath = resolve9(dir, "index.ts");
7138
- if (!existsSync8(indexPath)) {
7111
+ if (!existsSync7(indexPath)) {
7139
7112
  const indexJs = resolve9(dir, "index.js");
7140
- if (!existsSync8(indexJs)) {
7113
+ if (!existsSync7(indexJs)) {
7141
7114
  return { valid: false, error: "No index.ts or index.js entry point found", warnings };
7142
7115
  }
7143
7116
  }
7144
7117
  const pkgPath = resolve9(dir, "package.json");
7145
- if (!existsSync8(pkgPath)) {
7118
+ if (!existsSync7(pkgPath)) {
7146
7119
  warnings.push("No package.json found");
7147
7120
  } else {
7148
7121
  try {
7149
- const pkg2 = JSON.parse(readFileSync11(pkgPath, "utf-8"));
7122
+ const pkg2 = JSON.parse(readFileSync10(pkgPath, "utf-8"));
7150
7123
  if (!pkg2.xbrowser) {
7151
7124
  warnings.push("No xbrowser metadata in package.json");
7152
7125
  }
@@ -7166,7 +7139,7 @@ function safeCleanup(dir) {
7166
7139
  // src/plugin/install-sources/local.ts
7167
7140
  async function installFromLocal(source, name, targetDir) {
7168
7141
  const srcPath = resolve10(source);
7169
- if (!existsSync9(srcPath)) {
7142
+ if (!existsSync8(srcPath)) {
7170
7143
  throw new Error(`Local path does not exist: ${srcPath}`);
7171
7144
  }
7172
7145
  const tmpTarget = `${targetDir}-tmp-${Date.now()}`;
@@ -7179,7 +7152,7 @@ async function installFromLocal(source, name, targetDir) {
7179
7152
  safeCleanup(tmpTarget);
7180
7153
  throw new Error(`Invalid plugin: ${verify.error}`);
7181
7154
  }
7182
- if (existsSync9(targetDir)) {
7155
+ if (existsSync8(targetDir)) {
7183
7156
  rmSync2(targetDir, { recursive: true, force: true });
7184
7157
  }
7185
7158
  cpSync2(tmpTarget, targetDir, { recursive: true, force: true });
@@ -7199,7 +7172,7 @@ async function installFromLocal(source, name, targetDir) {
7199
7172
  }
7200
7173
 
7201
7174
  // src/plugin/install-sources/npm.ts
7202
- import { existsSync as existsSync10, mkdirSync as mkdirSync6, readFileSync as readFileSync12, writeFileSync as writeFileSync7, rmSync as rmSync3, cpSync as cpSync3 } from "fs";
7175
+ import { existsSync as existsSync9, mkdirSync as mkdirSync5, readFileSync as readFileSync11, writeFileSync as writeFileSync6, rmSync as rmSync3, cpSync as cpSync3 } from "fs";
7203
7176
  import { resolve as resolve11, join as join5 } from "path";
7204
7177
  import { tmpdir as tmpdir2 } from "os";
7205
7178
  async function installFromNpm(packageName, name, targetDir) {
@@ -7220,7 +7193,7 @@ async function installFromNpm(packageName, name, targetDir) {
7220
7193
  }
7221
7194
  const tarballUrl = versionMeta.dist.tarball;
7222
7195
  const tmpDir = join5(tmpdir2(), `xbrowser-npm-${Date.now()}`);
7223
- mkdirSync6(tmpDir, { recursive: true });
7196
+ mkdirSync5(tmpDir, { recursive: true });
7224
7197
  let warnings = [];
7225
7198
  try {
7226
7199
  const tarballPath = join5(tmpDir, `${name}.tgz`);
@@ -7233,16 +7206,16 @@ async function installFromNpm(packageName, name, targetDir) {
7233
7206
  if (!verify.valid) {
7234
7207
  throw new Error(`Invalid npm plugin: ${verify.error}`);
7235
7208
  }
7236
- if (existsSync10(targetDir)) {
7209
+ if (existsSync9(targetDir)) {
7237
7210
  rmSync3(targetDir, { recursive: true, force: true });
7238
7211
  }
7239
7212
  cpSync3(extractDir, targetDir, { recursive: true, force: true });
7240
7213
  const pkgPath = resolve11(targetDir, "package.json");
7241
- if (existsSync10(pkgPath)) {
7242
- const pkg2 = JSON.parse(readFileSync12(pkgPath, "utf-8"));
7214
+ if (existsSync9(pkgPath)) {
7215
+ const pkg2 = JSON.parse(readFileSync11(pkgPath, "utf-8"));
7243
7216
  if (!pkg2._npmSource) {
7244
7217
  pkg2._npmSource = { name: packageName, version: latestVersion };
7245
- writeFileSync7(pkgPath, JSON.stringify(pkg2, null, 2));
7218
+ writeFileSync6(pkgPath, JSON.stringify(pkg2, null, 2));
7246
7219
  }
7247
7220
  }
7248
7221
  } finally {
@@ -7259,7 +7232,7 @@ async function installFromNpm(packageName, name, targetDir) {
7259
7232
  }
7260
7233
 
7261
7234
  // src/plugin/install-sources/git.ts
7262
- import { existsSync as existsSync11, readFileSync as readFileSync13, writeFileSync as writeFileSync8, rmSync as rmSync4, cpSync as cpSync4 } from "fs";
7235
+ import { existsSync as existsSync10, readFileSync as readFileSync12, writeFileSync as writeFileSync7, rmSync as rmSync4, cpSync as cpSync4 } from "fs";
7263
7236
  import { resolve as resolve12, join as join6 } from "path";
7264
7237
  import { tmpdir as tmpdir3 } from "os";
7265
7238
  import { execSync as execSync8 } from "child_process";
@@ -7273,17 +7246,17 @@ async function installFromGit(gitUrl, name, targetDir) {
7273
7246
  if (!verify.valid) {
7274
7247
  throw new Error(`Invalid git plugin: ${verify.error}`);
7275
7248
  }
7276
- if (existsSync11(targetDir)) {
7249
+ if (existsSync10(targetDir)) {
7277
7250
  rmSync4(targetDir, { recursive: true, force: true });
7278
7251
  }
7279
7252
  cpSync4(tmpDir, targetDir, { recursive: true, force: true });
7280
7253
  rmSync4(resolve12(targetDir, ".git"), { recursive: true, force: true });
7281
7254
  const pkgPath = resolve12(targetDir, "package.json");
7282
- if (existsSync11(pkgPath)) {
7283
- const pkg2 = JSON.parse(readFileSync13(pkgPath, "utf-8"));
7255
+ if (existsSync10(pkgPath)) {
7256
+ const pkg2 = JSON.parse(readFileSync12(pkgPath, "utf-8"));
7284
7257
  if (!pkg2._gitSource) {
7285
7258
  pkg2._gitSource = { url: gitUrl };
7286
- writeFileSync8(pkgPath, JSON.stringify(pkg2, null, 2));
7259
+ writeFileSync7(pkgPath, JSON.stringify(pkg2, null, 2));
7287
7260
  }
7288
7261
  }
7289
7262
  } finally {
@@ -7300,12 +7273,12 @@ async function installFromGit(gitUrl, name, targetDir) {
7300
7273
  }
7301
7274
 
7302
7275
  // src/plugin/install-sources/url.ts
7303
- import { existsSync as existsSync12, readFileSync as readFileSync14, writeFileSync as writeFileSync9, mkdirSync as mkdirSync7, rmSync as rmSync5, cpSync as cpSync5 } from "fs";
7276
+ import { existsSync as existsSync11, readFileSync as readFileSync13, writeFileSync as writeFileSync8, mkdirSync as mkdirSync6, rmSync as rmSync5, cpSync as cpSync5 } from "fs";
7304
7277
  import { resolve as resolve13, join as join7, basename } from "path";
7305
7278
  import { tmpdir as tmpdir4 } from "os";
7306
7279
  async function installFromUrl(url, name, targetDir) {
7307
7280
  const tmpDir = join7(tmpdir4(), `xbrowser-url-${Date.now()}`);
7308
- mkdirSync7(tmpDir, { recursive: true });
7281
+ mkdirSync6(tmpDir, { recursive: true });
7309
7282
  let warnings = [];
7310
7283
  try {
7311
7284
  const fileName = basename(new URL(url).pathname) || "plugin.tar.gz";
@@ -7319,16 +7292,16 @@ async function installFromUrl(url, name, targetDir) {
7319
7292
  if (!verify.valid) {
7320
7293
  throw new Error(`Invalid plugin from URL: ${verify.error}`);
7321
7294
  }
7322
- if (existsSync12(targetDir)) {
7295
+ if (existsSync11(targetDir)) {
7323
7296
  rmSync5(targetDir, { recursive: true, force: true });
7324
7297
  }
7325
7298
  cpSync5(extractDir, targetDir, { recursive: true, force: true });
7326
7299
  const pkgPath = resolve13(targetDir, "package.json");
7327
- if (existsSync12(pkgPath)) {
7328
- const pkg2 = JSON.parse(readFileSync14(pkgPath, "utf-8"));
7300
+ if (existsSync11(pkgPath)) {
7301
+ const pkg2 = JSON.parse(readFileSync13(pkgPath, "utf-8"));
7329
7302
  if (!pkg2._urlSource) {
7330
7303
  pkg2._urlSource = { url };
7331
- writeFileSync9(pkgPath, JSON.stringify(pkg2, null, 2));
7304
+ writeFileSync8(pkgPath, JSON.stringify(pkg2, null, 2));
7332
7305
  }
7333
7306
  }
7334
7307
  } finally {
@@ -7346,10 +7319,10 @@ async function installFromUrl(url, name, targetDir) {
7346
7319
 
7347
7320
  // src/plugin/install-sources/marketplace.ts
7348
7321
  import {
7349
- existsSync as existsSync13,
7350
- mkdirSync as mkdirSync8,
7351
- writeFileSync as writeFileSync10,
7352
- readFileSync as readFileSync15,
7322
+ existsSync as existsSync12,
7323
+ mkdirSync as mkdirSync7,
7324
+ writeFileSync as writeFileSync9,
7325
+ readFileSync as readFileSync14,
7353
7326
  rmSync as rmSync6,
7354
7327
  cpSync as cpSync6
7355
7328
  } from "fs";
@@ -7371,12 +7344,12 @@ async function installFromMarketplace(pluginsDir, slug, options) {
7371
7344
  const plugin = detailData.data;
7372
7345
  const name = options?.name || String(plugin.slug || slug);
7373
7346
  const targetDir = resolve14(pluginsDir, name);
7374
- if (existsSync13(targetDir) && !options?.force) {
7347
+ if (existsSync12(targetDir) && !options?.force) {
7375
7348
  throw new Error(`Plugin "${name}" already exists. Use --force to overwrite.`);
7376
7349
  }
7377
- mkdirSync8(targetDir, { recursive: true });
7350
+ mkdirSync7(targetDir, { recursive: true });
7378
7351
  const tmpDir = join8(tmpdir5(), `xbrowser-marketplace-${Date.now()}`);
7379
- mkdirSync8(tmpDir, { recursive: true });
7352
+ mkdirSync7(tmpDir, { recursive: true });
7380
7353
  const realSlug = String(plugin.slug || slug);
7381
7354
  try {
7382
7355
  await downloadAndExtractMarketplaceTarball(baseUrl, realSlug, tmpDir, targetDir);
@@ -7406,14 +7379,14 @@ function isManifestArray(data) {
7406
7379
  return Array.isArray(data) && data.length > 0 && typeof data[0].path === "string" && typeof data[0].content === "string";
7407
7380
  }
7408
7381
  function extractManifestToDir(manifest, targetDir) {
7409
- if (existsSync13(targetDir)) {
7382
+ if (existsSync12(targetDir)) {
7410
7383
  rmSync6(targetDir, { recursive: true, force: true });
7411
7384
  }
7412
- mkdirSync8(targetDir, { recursive: true });
7385
+ mkdirSync7(targetDir, { recursive: true });
7413
7386
  for (const file of manifest) {
7414
7387
  const filePath = resolve14(targetDir, file.path);
7415
- mkdirSync8(dirname2(filePath), { recursive: true });
7416
- writeFileSync10(filePath, Buffer.from(file.content, "base64"));
7388
+ mkdirSync7(dirname2(filePath), { recursive: true });
7389
+ writeFileSync9(filePath, Buffer.from(file.content, "base64"));
7417
7390
  }
7418
7391
  }
7419
7392
  function tryParseAsGzippedManifest(buffer) {
@@ -7440,7 +7413,7 @@ async function downloadAndExtractMarketplaceTarball(baseUrl, slug, tmpDir, targe
7440
7413
  const redirectUrl = tarballRes.headers.get("location");
7441
7414
  const tarballPath = join8(tmpDir, `${slug}.tar.gz`);
7442
7415
  await downloadToFile(redirectUrl, tarballPath);
7443
- const buffer = readFileSync15(tarballPath);
7416
+ const buffer = readFileSync14(tarballPath);
7444
7417
  const manifest = tryParseAsGzippedManifest(buffer);
7445
7418
  if (manifest) {
7446
7419
  extractManifestToDir(manifest, targetDir);
@@ -7449,7 +7422,7 @@ async function downloadAndExtractMarketplaceTarball(baseUrl, slug, tmpDir, targe
7449
7422
  const extractDir = join8(tmpDir, "extracted");
7450
7423
  extractTarGz(tarballPath, extractDir);
7451
7424
  flattenPackageRoot(extractDir);
7452
- if (existsSync13(targetDir)) {
7425
+ if (existsSync12(targetDir)) {
7453
7426
  rmSync6(targetDir, { recursive: true, force: true });
7454
7427
  }
7455
7428
  cpSync6(extractDir, targetDir, { recursive: true, force: true });
@@ -7461,12 +7434,12 @@ async function downloadAndExtractMarketplaceTarball(baseUrl, slug, tmpDir, targe
7461
7434
  return;
7462
7435
  }
7463
7436
  const tarballPath = join8(tmpDir, `${slug}.tar.gz`);
7464
- writeFileSync10(tarballPath, buffer);
7437
+ writeFileSync9(tarballPath, buffer);
7465
7438
  try {
7466
7439
  const extractDir = join8(tmpDir, "extracted");
7467
7440
  extractTarGz(tarballPath, extractDir);
7468
7441
  flattenPackageRoot(extractDir);
7469
- if (existsSync13(targetDir)) {
7442
+ if (existsSync12(targetDir)) {
7470
7443
  rmSync6(targetDir, { recursive: true, force: true });
7471
7444
  }
7472
7445
  cpSync6(extractDir, targetDir, { recursive: true, force: true });
@@ -7502,24 +7475,24 @@ function writeMarketplacePackageJson(plugin, slug, name, baseUrl, targetDir) {
7502
7475
  }
7503
7476
  };
7504
7477
  const pkgPath = resolve14(targetDir, "package.json");
7505
- if (!existsSync13(pkgPath)) {
7506
- writeFileSync10(pkgPath, JSON.stringify(packageJson, null, 2));
7478
+ if (!existsSync12(pkgPath)) {
7479
+ writeFileSync9(pkgPath, JSON.stringify(packageJson, null, 2));
7507
7480
  } else {
7508
7481
  try {
7509
- const existing = JSON.parse(readFileSync15(pkgPath, "utf-8"));
7482
+ const existing = JSON.parse(readFileSync14(pkgPath, "utf-8"));
7510
7483
  const merged = {
7511
7484
  ...existing,
7512
7485
  xbrowser: { ...existing.xbrowser, ...packageJson.xbrowser },
7513
7486
  _marketplace: packageJson._marketplace
7514
7487
  };
7515
- writeFileSync10(pkgPath, JSON.stringify(merged, null, 2));
7488
+ writeFileSync9(pkgPath, JSON.stringify(merged, null, 2));
7516
7489
  } catch {
7517
- writeFileSync10(pkgPath, JSON.stringify(packageJson, null, 2));
7490
+ writeFileSync9(pkgPath, JSON.stringify(packageJson, null, 2));
7518
7491
  }
7519
7492
  }
7520
7493
  }
7521
7494
  function ensureIndexFile(plugin, name, targetDir) {
7522
- if (!existsSync13(resolve14(targetDir, "index.ts")) && !existsSync13(resolve14(targetDir, "index.js"))) {
7495
+ if (!existsSync12(resolve14(targetDir, "index.ts")) && !existsSync12(resolve14(targetDir, "index.js"))) {
7523
7496
  const commands = plugin.commands || [];
7524
7497
  const commandHandlers = commands.length > 0 ? commands.map((cmd) => {
7525
7498
  return [
@@ -7534,7 +7507,7 @@ function ensureIndexFile(plugin, name, targetDir) {
7534
7507
  ` handler: async () => ({ data: { message: 'Hello from ${name}!' }, tips: [] }),`,
7535
7508
  ` });`
7536
7509
  ].join("\n");
7537
- writeFileSync10(
7510
+ writeFileSync9(
7538
7511
  resolve14(targetDir, "index.ts"),
7539
7512
  [
7540
7513
  `import type { XCLIAPI } from '@dyyz1993/xcli-core';`,
@@ -7573,10 +7546,10 @@ var PluginInstaller = class {
7573
7546
  const type = this.detectSourceType(source);
7574
7547
  const name = options?.name || this.deriveName(source, type);
7575
7548
  const targetDir = resolve15(this.pluginsDir, name);
7576
- if (existsSync14(targetDir) && !options?.force) {
7549
+ if (existsSync13(targetDir) && !options?.force) {
7577
7550
  throw new Error(`Plugin "${name}" already exists. Use --force to overwrite.`);
7578
7551
  }
7579
- mkdirSync9(targetDir, { recursive: true });
7552
+ mkdirSync8(targetDir, { recursive: true });
7580
7553
  const resolvedSource = type === "npm" ? await resolveNpmPackageWithFallback(source) : source;
7581
7554
  switch (type) {
7582
7555
  case "local":
@@ -7634,7 +7607,7 @@ var PluginInstaller = class {
7634
7607
  */
7635
7608
  async uninstall(name) {
7636
7609
  const targetDir = resolve15(this.pluginsDir, name);
7637
- if (!existsSync14(targetDir)) {
7610
+ if (!existsSync13(targetDir)) {
7638
7611
  throw new Error(`Plugin "${name}" not found`);
7639
7612
  }
7640
7613
  rmSync7(targetDir, { recursive: true, force: true });
@@ -7645,7 +7618,7 @@ var PluginInstaller = class {
7645
7618
  * @returns Array of installed plugin information.
7646
7619
  */
7647
7620
  async list(_options) {
7648
- if (!existsSync14(this.pluginsDir)) return [];
7621
+ if (!existsSync13(this.pluginsDir)) return [];
7649
7622
  const entries = readdirSync3(this.pluginsDir, { withFileTypes: true });
7650
7623
  const plugins = [];
7651
7624
  for (const entry of entries) {
@@ -7653,7 +7626,7 @@ var PluginInstaller = class {
7653
7626
  const pluginPath = resolve15(this.pluginsDir, entry.name);
7654
7627
  const indexPath = resolve15(pluginPath, "index.ts");
7655
7628
  const indexJsPath = resolve15(pluginPath, "index.js");
7656
- if (!existsSync14(indexPath) && !existsSync14(indexJsPath)) continue;
7629
+ if (!existsSync13(indexPath) && !existsSync13(indexJsPath)) continue;
7657
7630
  const metadata = PluginMetadataParser.parseFromPackageJson(pluginPath);
7658
7631
  let source = "local";
7659
7632
  const pkg2 = readJsonFile(resolve15(pluginPath, "package.json"), {});
@@ -7680,10 +7653,10 @@ var PluginInstaller = class {
7680
7653
  }
7681
7654
  if (source.startsWith("file://")) {
7682
7655
  const filePath = decodeURIComponent(new URL(source).pathname);
7683
- if (existsSync14(filePath)) return "url";
7656
+ if (existsSync13(filePath)) return "url";
7684
7657
  }
7685
7658
  if (source.endsWith(".git") || source.includes("github.com/")) return "git";
7686
- if (existsSync14(resolve15(source))) return "local";
7659
+ if (existsSync13(resolve15(source))) return "local";
7687
7660
  return "npm";
7688
7661
  }
7689
7662
  deriveName(source, type) {
@@ -9707,7 +9680,7 @@ async function handleFilter(args, _mode) {
9707
9680
 
9708
9681
  // src/stdin.ts
9709
9682
  import { createInterface } from "readline";
9710
- import { readFileSync as readFileSync16 } from "fs";
9683
+ import { readFileSync as readFileSync15 } from "fs";
9711
9684
  async function readStdin2() {
9712
9685
  if (process.stdin.isTTY) return [];
9713
9686
  const lines = [];
@@ -9721,7 +9694,7 @@ async function readStdin2() {
9721
9694
  return lines;
9722
9695
  }
9723
9696
  function readCommandFile(filePath) {
9724
- const content = readFileSync16(filePath, "utf-8");
9697
+ const content = readFileSync15(filePath, "utf-8");
9725
9698
  return content.split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("#"));
9726
9699
  }
9727
9700
 
@@ -10618,6 +10591,22 @@ var HTTPServer = class {
10618
10591
  };
10619
10592
 
10620
10593
  // src/router.ts
10594
+ var KNOWN_GLOBAL_OPTIONS = /* @__PURE__ */ new Set([
10595
+ "json",
10596
+ "yaml",
10597
+ "session",
10598
+ "cdp",
10599
+ "cdp-endpoint",
10600
+ "version",
10601
+ "v",
10602
+ "help",
10603
+ "h",
10604
+ "target",
10605
+ "port",
10606
+ "token",
10607
+ "timeout",
10608
+ "headless"
10609
+ ]);
10621
10610
  function showCommandHelp(siteName, cmd, siteConfig, mode) {
10622
10611
  const c = cmd;
10623
10612
  if (mode === "json") {
@@ -10742,8 +10731,23 @@ async function handleEvalMode(argv) {
10742
10731
  }
10743
10732
  async function handleChainInput(input, argv) {
10744
10733
  const cdpEndpoint = argv ? extractCdpFromArgv(argv) : void 0;
10734
+ const jsonMode = argv ? argv.includes("--json") || argv.includes("-j") : false;
10745
10735
  const chainResult = await executeChain(input, { cdpEndpoint });
10746
- printChainResult(chainResult);
10736
+ if (jsonMode) {
10737
+ const output = {
10738
+ success: chainResult.success,
10739
+ steps: chainResult.steps.map((s) => ({
10740
+ command: s.raw,
10741
+ success: s.success,
10742
+ data: s.data,
10743
+ duration: s.duration,
10744
+ ...s.hookOutputs?.length ? { hooks: s.hookOutputs } : {}
10745
+ }))
10746
+ };
10747
+ console.log(JSON.stringify(output, null, 2));
10748
+ } else {
10749
+ printChainResult(chainResult);
10750
+ }
10747
10751
  if (!chainResult.success) throw new Error("Command failed");
10748
10752
  }
10749
10753
  async function routeCommand(argv, stdinCommands) {
@@ -10766,6 +10770,8 @@ async function routeCommand(argv, stdinCommands) {
10766
10770
  }
10767
10771
  const parsed = parseArgs(argv);
10768
10772
  const { positional, options } = parsed;
10773
+ const command = positional[0];
10774
+ const cmdArgs = positional.slice(1);
10769
10775
  const mode = options.json ? "json" : options.yaml ? "yaml" : "text";
10770
10776
  const sessionName = options.session || process.env.XBROWSER_SESSION || "default";
10771
10777
  const cdpEndpoint = options.cdp;
@@ -10777,8 +10783,6 @@ async function routeCommand(argv, stdinCommands) {
10777
10783
  showMainHelp();
10778
10784
  return;
10779
10785
  }
10780
- const command = positional[0];
10781
- const cmdArgs = positional.slice(1);
10782
10786
  if ((options.help || options.h) && positional.length > 0) {
10783
10787
  const loader = await getPluginLoader();
10784
10788
  const internalLoader = loader.getCore().loader;
@@ -10993,10 +10997,30 @@ Run "xbrowser ${command} --help" to see available commands.`
10993
10997
  const subCmdIdx = pluginNameIdx >= 0 ? argv.indexOf(subCommand, pluginNameIdx + 1) : -1;
10994
10998
  const rawPluginArgs = subCmdIdx >= 0 ? argv.slice(subCmdIdx + 1) : [];
10995
10999
  const params = parsePluginParams(rawPluginArgs, cmdEntry.parameters);
11000
+ if (cmdEntry.parameters) {
11001
+ const schemaAny = cmdEntry.parameters;
11002
+ const def = schemaAny._def;
11003
+ const shapeOrFn = def?.shape ?? schemaAny.shape;
11004
+ const shapeObj = typeof shapeOrFn === "function" ? shapeOrFn() : shapeOrFn;
11005
+ if (shapeObj && typeof shapeObj === "object") {
11006
+ const knownKeys = new Set(Object.keys(shapeObj));
11007
+ knownKeys.add("_target");
11008
+ for (const gk of KNOWN_GLOBAL_OPTIONS) knownKeys.add(gk.replace(/-([a-z])/g, (_, c) => c.toUpperCase()));
11009
+ const unknownKeys = Object.keys(params).filter((k) => !knownKeys.has(k));
11010
+ if (unknownKeys.length > 0) {
11011
+ const unknown = unknownKeys.map((k) => `--${k.replace(/([A-Z])/g, "-$1").toLowerCase()}`).join(", ");
11012
+ outputError(
11013
+ `Unknown parameter: ${unknown}
11014
+ Run "xbrowser ${command} ${subCommand} --help" to see available parameters.`
11015
+ );
11016
+ return;
11017
+ }
11018
+ }
11019
+ }
10996
11020
  if (options.target && !params._target) {
10997
11021
  params._target = options.target;
10998
11022
  }
10999
- const needsBrowser = cmdEntry.scope === "page";
11023
+ const needsBrowser = cmdEntry.scope === "page" || cmdEntry.scope === "browser";
11000
11024
  if (needsBrowser && !process.env.XBROWSER_DAEMON_WORKER) {
11001
11025
  const { forwardExec } = await import("./daemon-client-XWSSQBEA.js");
11002
11026
  const userTimeout = typeof params.timeout === "number" && params.timeout > 0 ? params.timeout * 1e3 + 3e4 : void 0;
@@ -11046,38 +11070,46 @@ Run "xbrowser ${command} --help" to see available commands.`
11046
11070
  }
11047
11071
  };
11048
11072
  try {
11073
+ const cmdStart = Date.now();
11074
+ const cmdHooks = await loadHooks();
11075
+ if (cmdHooks.length > 0 && session?.page) {
11076
+ await Promise.all(cmdHooks.map((h) => h.onBeforeCommand?.({ page: session.page, command: `${command} ${subCommand}`, params })));
11077
+ }
11049
11078
  const result = await cmdEntry.handler(params, ctx);
11079
+ const hookOutputs = [];
11080
+ if (cmdHooks.length > 0 && session?.page) {
11081
+ for (const h of cmdHooks) {
11082
+ const output = await h.onAfterCommand?.({ page: session.page, command: `${command} ${subCommand}`, params, result, duration: Date.now() - cmdStart });
11083
+ if (output) hookOutputs.push({ _hook: h.name, ...output });
11084
+ }
11085
+ }
11050
11086
  if (session && result && result.data) {
11051
11087
  const convUrl = result.data.conversationUrl;
11052
11088
  if (convUrl) {
11053
11089
  saveSessionDiskMeta(sessionName, { conversationUrl: convUrl, cdpEndpoint });
11054
11090
  }
11055
11091
  }
11056
- if (isCommandResult2(result)) {
11057
- if (mode === "json" || mode === "yaml") {
11058
- console.log(outputFormatter2.format(result.data, { mode, color: false, emoji: false }));
11059
- if (result.tips?.length) {
11060
- for (const tip of result.tips) console.error(`\u{1F4A1} ${tip}`);
11061
- }
11062
- } else {
11063
- console.log(outputFormatter2.format(result.data, { mode: "text", color: true, emoji: true }));
11064
- if (result.tips?.length) {
11065
- for (const tip of result.tips) console.log(` \u{1F4A1} ${tip}`);
11066
- }
11092
+ const outputData = isCommandResult2(result) ? result.data : result && typeof result === "object" ? result.data ?? result : result;
11093
+ const tips = isCommandResult2(result) ? result.tips : result && typeof result === "object" ? result.tips : void 0;
11094
+ if (mode === "json" || mode === "yaml") {
11095
+ const finalOutput = {
11096
+ data: outputData
11097
+ };
11098
+ if (hookOutputs.length > 0) {
11099
+ finalOutput.hooks = hookOutputs;
11067
11100
  }
11068
- } else if (result && typeof result === "object") {
11069
- const obj = result;
11070
- if (mode === "json" || mode === "yaml") {
11071
- console.log(outputFormatter2.format(obj.data ?? obj, { mode, color: false, emoji: false }));
11072
- const tips = obj.tips;
11073
- if (tips?.length) {
11074
- for (const tip of tips) console.error(`\u{1F4A1} ${tip}`);
11075
- }
11076
- } else {
11077
- if (obj.data) outputResult(obj.data, mode);
11078
- const tips = obj.tips;
11079
- if (tips?.length) {
11080
- for (const tip of tips) console.log(` \u{1F4A1} ${tip}`);
11101
+ console.log(outputFormatter2.format(finalOutput, { mode, color: false, emoji: false }));
11102
+ if (tips?.length) {
11103
+ for (const tip of tips) console.error(`\u{1F4A1} ${tip}`);
11104
+ }
11105
+ } else {
11106
+ console.log(outputFormatter2.format(outputData, { mode: "text", color: true, emoji: true }));
11107
+ if (tips?.length) {
11108
+ for (const tip of tips) console.log(` \u{1F4A1} ${tip}`);
11109
+ }
11110
+ if (hookOutputs.length > 0) {
11111
+ for (const ho of hookOutputs) {
11112
+ console.log(` \u{1F4F8} screenshot: ${ho.screenshot?.url || "captured"}`);
11081
11113
  }
11082
11114
  }
11083
11115
  }