adhdev 0.7.33 → 0.7.35

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/index.js CHANGED
@@ -505,18 +505,18 @@ function checkPathExists(paths) {
505
505
  return null;
506
506
  }
507
507
  async function detectIDEs() {
508
- const os20 = (0, import_os2.platform)();
508
+ const os21 = (0, import_os2.platform)();
509
509
  const results = [];
510
510
  for (const def of getMergedDefinitions()) {
511
511
  const cliPath = findCliCommand(def.cli);
512
- const appPath = checkPathExists(def.paths[os20] || []);
512
+ const appPath = checkPathExists(def.paths[os21] || []);
513
513
  const installed = !!(cliPath || appPath);
514
514
  let resolvedCli = cliPath;
515
- if (!resolvedCli && appPath && os20 === "darwin") {
515
+ if (!resolvedCli && appPath && os21 === "darwin") {
516
516
  const bundledCli = `${appPath}/Contents/Resources/app/bin/${def.cli}`;
517
517
  if ((0, import_fs2.existsSync)(bundledCli)) resolvedCli = bundledCli;
518
518
  }
519
- if (!resolvedCli && appPath && os20 === "win32") {
519
+ if (!resolvedCli && appPath && os21 === "win32") {
520
520
  const { dirname: dirname10 } = await import("path");
521
521
  const appDir = dirname10(appPath);
522
522
  const candidates = [
@@ -8507,7 +8507,7 @@ function detectCurrentWorkspace(ideId) {
8507
8507
  }
8508
8508
  } else if (plat === "win32") {
8509
8509
  try {
8510
- const fs15 = require("fs");
8510
+ const fs16 = require("fs");
8511
8511
  const appNameMap = getMacAppIdentifiers();
8512
8512
  const appName = appNameMap[ideId];
8513
8513
  if (appName) {
@@ -8516,8 +8516,8 @@ function detectCurrentWorkspace(ideId) {
8516
8516
  appName,
8517
8517
  "storage.json"
8518
8518
  );
8519
- if (fs15.existsSync(storagePath)) {
8520
- const data = JSON.parse(fs15.readFileSync(storagePath, "utf-8"));
8519
+ if (fs16.existsSync(storagePath)) {
8520
+ const data = JSON.parse(fs16.readFileSync(storagePath, "utf-8"));
8521
8521
  const workspaces = data?.openedPathsList?.workspaces3 || data?.openedPathsList?.entries || [];
8522
8522
  if (workspaces.length > 0) {
8523
8523
  const recent = workspaces[0];
@@ -9044,9 +9044,9 @@ var init_router = __esm({
9044
9044
  LOG.info("Upgrade", "Restarting daemon with new version...");
9045
9045
  try {
9046
9046
  const path18 = require("path");
9047
- const fs15 = require("fs");
9047
+ const fs16 = require("fs");
9048
9048
  const pidFile = path18.join(process.env.HOME || process.env.USERPROFILE || "", ".adhdev", "daemon.pid");
9049
- if (fs15.existsSync(pidFile)) fs15.unlinkSync(pidFile);
9049
+ if (fs16.existsSync(pidFile)) fs16.unlinkSync(pidFile);
9050
9050
  } catch {
9051
9051
  }
9052
9052
  const { spawn: spawn4 } = require("child_process");
@@ -18811,12 +18811,12 @@ function findBinary(name) {
18811
18811
  function isScriptBinary(binaryPath) {
18812
18812
  if (!path9.isAbsolute(binaryPath)) return false;
18813
18813
  try {
18814
- const fs15 = require("fs");
18815
- const resolved = fs15.realpathSync(binaryPath);
18814
+ const fs16 = require("fs");
18815
+ const resolved = fs16.realpathSync(binaryPath);
18816
18816
  const head = Buffer.alloc(8);
18817
- const fd = fs15.openSync(resolved, "r");
18818
- fs15.readSync(fd, head, 0, 8, 0);
18819
- fs15.closeSync(fd);
18817
+ const fd = fs16.openSync(resolved, "r");
18818
+ fs16.readSync(fd, head, 0, 8, 0);
18819
+ fs16.closeSync(fd);
18820
18820
  let i = 0;
18821
18821
  if (head[0] === 239 && head[1] === 187 && head[2] === 191) i = 3;
18822
18822
  return head[i] === 35 && head[i + 1] === 33;
@@ -18827,12 +18827,12 @@ function isScriptBinary(binaryPath) {
18827
18827
  function looksLikeMachOOrElf(filePath) {
18828
18828
  if (!path9.isAbsolute(filePath)) return false;
18829
18829
  try {
18830
- const fs15 = require("fs");
18831
- const resolved = fs15.realpathSync(filePath);
18830
+ const fs16 = require("fs");
18831
+ const resolved = fs16.realpathSync(filePath);
18832
18832
  const buf = Buffer.alloc(8);
18833
- const fd = fs15.openSync(resolved, "r");
18834
- fs15.readSync(fd, buf, 0, 8, 0);
18835
- fs15.closeSync(fd);
18833
+ const fd = fs16.openSync(resolved, "r");
18834
+ fs16.readSync(fd, buf, 0, 8, 0);
18835
+ fs16.closeSync(fd);
18836
18836
  let i = 0;
18837
18837
  if (buf[0] === 239 && buf[1] === 187 && buf[2] === 191) i = 3;
18838
18838
  const b2 = buf.subarray(i);
@@ -18953,14 +18953,14 @@ var init_provider_cli_adapter = __esm({
18953
18953
  pty2 = require("node-pty");
18954
18954
  if (os13.platform() !== "win32") {
18955
18955
  try {
18956
- const fs15 = require("fs");
18956
+ const fs16 = require("fs");
18957
18957
  const ptyDir = path9.resolve(path9.dirname(require.resolve("node-pty")), "..");
18958
18958
  const platformArch = `${os13.platform()}-${os13.arch()}`;
18959
18959
  const helper = path9.join(ptyDir, "prebuilds", platformArch, "spawn-helper");
18960
- if (fs15.existsSync(helper)) {
18961
- const stat4 = fs15.statSync(helper);
18960
+ if (fs16.existsSync(helper)) {
18961
+ const stat4 = fs16.statSync(helper);
18962
18962
  if (!(stat4.mode & 73)) {
18963
- fs15.chmodSync(helper, stat4.mode | 493);
18963
+ fs16.chmodSync(helper, stat4.mode | 493);
18964
18964
  LOG.info("CLI", "[node-pty] Fixed spawn-helper permissions");
18965
18965
  }
18966
18966
  }
@@ -43493,8 +43493,8 @@ async function installExtension(ide, extension) {
43493
43493
  const res = await fetch(extension.vsixUrl);
43494
43494
  if (res.ok) {
43495
43495
  const buffer = Buffer.from(await res.arrayBuffer());
43496
- const fs15 = await import("fs");
43497
- fs15.writeFileSync(vsixPath, buffer);
43496
+ const fs16 = await import("fs");
43497
+ fs16.writeFileSync(vsixPath, buffer);
43498
43498
  return new Promise((resolve12) => {
43499
43499
  const cmd = `"${ide.cliCommand}" --install-extension "${vsixPath}" --force`;
43500
43500
  (0, import_child_process8.exec)(cmd, { timeout: 6e4 }, (error48, _stdout, stderr) => {
@@ -44219,10 +44219,10 @@ var init_server_connection = __esm({
44219
44219
  this.setState("disconnected");
44220
44220
  try {
44221
44221
  const path18 = require("path");
44222
- const fs15 = require("fs");
44222
+ const fs16 = require("fs");
44223
44223
  const configPath = path18.join(process.env.HOME || process.env.USERPROFILE || "", ".adhdev", "config.json");
44224
- if (fs15.existsSync(configPath)) {
44225
- fs15.unlinkSync(configPath);
44224
+ if (fs16.existsSync(configPath)) {
44225
+ fs16.unlinkSync(configPath);
44226
44226
  LOG.info("Server", `[ServerConn] Config file removed. Re-run 'adhdev setup'.`);
44227
44227
  }
44228
44228
  } catch {
@@ -45312,19 +45312,70 @@ var init_screenshot_controller = __esm({
45312
45312
  });
45313
45313
 
45314
45314
  // src/session-host.ts
45315
+ var session_host_exports = {};
45316
+ __export(session_host_exports, {
45317
+ ensureSessionHostReady: () => ensureSessionHostReady2,
45318
+ listHostedCliRuntimes: () => listHostedCliRuntimes2,
45319
+ stopSessionHost: () => stopSessionHost
45320
+ });
45315
45321
  function resolveSessionHostEntry() {
45316
- const localCandidates = [
45322
+ const packagedCandidates = [
45317
45323
  path16.resolve(__dirname, "../vendor/session-host-daemon/index.js"),
45318
- path16.resolve(__dirname, "../../vendor/session-host-daemon/index.js"),
45319
- path16.resolve(__dirname, "../../../oss/packages/session-host-daemon/dist/index.js")
45324
+ path16.resolve(__dirname, "../../vendor/session-host-daemon/index.js")
45320
45325
  ];
45321
- for (const candidate of localCandidates) {
45322
- if (require("fs").existsSync(candidate)) {
45326
+ for (const candidate of packagedCandidates) {
45327
+ if (fs14.existsSync(candidate)) {
45323
45328
  return candidate;
45324
45329
  }
45325
45330
  }
45326
45331
  return require.resolve("@adhdev/session-host-daemon");
45327
45332
  }
45333
+ function getSessionHostPidFile() {
45334
+ return path16.join(os19.homedir(), ".adhdev", `${SESSION_HOST_APP_NAME}-session-host.pid`);
45335
+ }
45336
+ function killPid(pid) {
45337
+ try {
45338
+ if (process.platform === "win32") {
45339
+ (0, import_child_process9.execFileSync)("taskkill", ["/PID", String(pid), "/T", "/F"], { stdio: "ignore" });
45340
+ } else {
45341
+ process.kill(pid, "SIGTERM");
45342
+ }
45343
+ return true;
45344
+ } catch {
45345
+ return false;
45346
+ }
45347
+ }
45348
+ function stopSessionHost() {
45349
+ let stopped = false;
45350
+ const pidFile = getSessionHostPidFile();
45351
+ try {
45352
+ if (fs14.existsSync(pidFile)) {
45353
+ const pid = Number.parseInt(fs14.readFileSync(pidFile, "utf8").trim(), 10);
45354
+ if (Number.isFinite(pid)) {
45355
+ stopped = killPid(pid) || stopped;
45356
+ }
45357
+ }
45358
+ } catch {
45359
+ } finally {
45360
+ try {
45361
+ fs14.unlinkSync(pidFile);
45362
+ } catch {
45363
+ }
45364
+ }
45365
+ if (process.platform !== "win32") {
45366
+ try {
45367
+ const raw = (0, import_child_process9.execFileSync)("pgrep", ["-f", "session-host-daemon"], { encoding: "utf8" }).trim();
45368
+ for (const line of raw.split("\n")) {
45369
+ const pid = Number.parseInt(line.trim(), 10);
45370
+ if (Number.isFinite(pid)) {
45371
+ stopped = killPid(pid) || stopped;
45372
+ }
45373
+ }
45374
+ } catch {
45375
+ }
45376
+ }
45377
+ return stopped;
45378
+ }
45328
45379
  async function ensureSessionHostReady2() {
45329
45380
  return ensureSessionHostReady({
45330
45381
  appName: SESSION_HOST_APP_NAME,
@@ -45346,11 +45397,13 @@ async function ensureSessionHostReady2() {
45346
45397
  async function listHostedCliRuntimes2(endpoint) {
45347
45398
  return listHostedCliRuntimes(endpoint);
45348
45399
  }
45349
- var import_child_process9, path16, SESSION_HOST_APP_NAME;
45400
+ var import_child_process9, fs14, os19, path16, SESSION_HOST_APP_NAME;
45350
45401
  var init_session_host = __esm({
45351
45402
  "src/session-host.ts"() {
45352
45403
  "use strict";
45353
45404
  import_child_process9 = require("child_process");
45405
+ fs14 = __toESM(require("fs"));
45406
+ os19 = __toESM(require("os"));
45354
45407
  path16 = __toESM(require("path"));
45355
45408
  init_src();
45356
45409
  SESSION_HOST_APP_NAME = process.env.ADHDEV_SESSION_HOST_NAME || "adhdev";
@@ -45365,24 +45418,24 @@ __export(adhdev_daemon_exports, {
45365
45418
  stopDaemon: () => stopDaemon
45366
45419
  });
45367
45420
  function getDaemonPidFile() {
45368
- const dir = path17.join(os19.homedir(), ".adhdev");
45369
- if (!fs14.existsSync(dir)) fs14.mkdirSync(dir, { recursive: true });
45421
+ const dir = path17.join(os20.homedir(), ".adhdev");
45422
+ if (!fs15.existsSync(dir)) fs15.mkdirSync(dir, { recursive: true });
45370
45423
  return path17.join(dir, "daemon.pid");
45371
45424
  }
45372
45425
  function writeDaemonPid(pid) {
45373
- fs14.writeFileSync(getDaemonPidFile(), String(pid), "utf-8");
45426
+ fs15.writeFileSync(getDaemonPidFile(), String(pid), "utf-8");
45374
45427
  }
45375
45428
  function removeDaemonPid() {
45376
45429
  try {
45377
- fs14.unlinkSync(getDaemonPidFile());
45430
+ fs15.unlinkSync(getDaemonPidFile());
45378
45431
  } catch (e) {
45379
45432
  }
45380
45433
  }
45381
45434
  function isDaemonRunning() {
45382
45435
  const pidFile = getDaemonPidFile();
45383
45436
  try {
45384
- if (!fs14.existsSync(pidFile)) return false;
45385
- const pid = parseInt(fs14.readFileSync(pidFile, "utf-8").trim());
45437
+ if (!fs15.existsSync(pidFile)) return false;
45438
+ const pid = parseInt(fs15.readFileSync(pidFile, "utf-8").trim());
45386
45439
  process.kill(pid, 0);
45387
45440
  return true;
45388
45441
  } catch {
@@ -45393,8 +45446,8 @@ function isDaemonRunning() {
45393
45446
  function stopDaemon() {
45394
45447
  const pidFile = getDaemonPidFile();
45395
45448
  try {
45396
- if (!fs14.existsSync(pidFile)) return false;
45397
- const pid = parseInt(fs14.readFileSync(pidFile, "utf-8").trim());
45449
+ if (!fs15.existsSync(pidFile)) return false;
45450
+ const pid = parseInt(fs15.readFileSync(pidFile, "utf-8").trim());
45398
45451
  process.kill(pid, "SIGTERM");
45399
45452
  removeDaemonPid();
45400
45453
  return true;
@@ -45403,7 +45456,7 @@ function stopDaemon() {
45403
45456
  return false;
45404
45457
  }
45405
45458
  }
45406
- var os19, fs14, path17, import_chalk2, pkgVersion, DANGEROUS_PATTERNS, AdhdevDaemon;
45459
+ var os20, fs15, path17, import_chalk2, pkgVersion, DANGEROUS_PATTERNS, AdhdevDaemon;
45407
45460
  var init_adhdev_daemon = __esm({
45408
45461
  "src/adhdev-daemon.ts"() {
45409
45462
  "use strict";
@@ -45413,11 +45466,11 @@ var init_adhdev_daemon = __esm({
45413
45466
  init_screenshot_controller();
45414
45467
  init_session_host();
45415
45468
  init_dist();
45416
- os19 = __toESM(require("os"));
45417
- fs14 = __toESM(require("fs"));
45469
+ os20 = __toESM(require("os"));
45470
+ fs15 = __toESM(require("fs"));
45418
45471
  path17 = __toESM(require("path"));
45419
45472
  import_chalk2 = __toESM(require("chalk"));
45420
- pkgVersion = "0.7.33";
45473
+ pkgVersion = "0.7.35";
45421
45474
  if (pkgVersion === "unknown") {
45422
45475
  try {
45423
45476
  const possiblePaths = [
@@ -45426,7 +45479,7 @@ var init_adhdev_daemon = __esm({
45426
45479
  ];
45427
45480
  for (const p of possiblePaths) {
45428
45481
  try {
45429
- const data = JSON.parse(fs14.readFileSync(p, "utf-8"));
45482
+ const data = JSON.parse(fs15.readFileSync(p, "utf-8"));
45430
45483
  if (data.version) {
45431
45484
  pkgVersion = data.version;
45432
45485
  break;
@@ -45552,8 +45605,8 @@ ${err?.stack || ""}`);
45552
45605
  cliInfo: {
45553
45606
  type: "adhdev-daemon",
45554
45607
  version: pkgVersion,
45555
- platform: os19.platform(),
45556
- hostname: os19.hostname(),
45608
+ platform: os20.platform(),
45609
+ hostname: os20.hostname(),
45557
45610
  machineId: config2.machineId,
45558
45611
  instanceId
45559
45612
  }
@@ -45993,16 +46046,16 @@ async function loginFlow() {
45993
46046
  let verificationUrl;
45994
46047
  try {
45995
46048
  const config2 = loadConfig();
45996
- const os20 = await import("os");
46049
+ const os21 = await import("os");
45997
46050
  const res = await fetch(`${SERVER_URL}/auth/cli/init`, {
45998
46051
  method: "POST",
45999
46052
  headers: { "Content-Type": "application/json" },
46000
46053
  body: JSON.stringify({
46001
46054
  clientMachineId: config2.machineId,
46002
46055
  registeredMachineId: config2.registeredMachineId,
46003
- hostname: os20.hostname(),
46004
- platform: os20.platform(),
46005
- arch: os20.arch()
46056
+ hostname: os21.hostname(),
46057
+ platform: os21.platform(),
46058
+ arch: os21.arch()
46006
46059
  })
46007
46060
  });
46008
46061
  if (!res.ok) {
@@ -46101,10 +46154,10 @@ async function startDaemonFlow() {
46101
46154
  const { AdhdevDaemon: AdhdevDaemon2 } = await Promise.resolve().then(() => (init_adhdev_daemon(), adhdev_daemon_exports));
46102
46155
  const daemon = new AdhdevDaemon2();
46103
46156
  const { execSync: execSync7 } = await import("child_process");
46104
- const os20 = await import("os");
46157
+ const os21 = await import("os");
46105
46158
  const path18 = await import("path");
46106
- const logPath = path18.join(os20.homedir(), ".adhdev", "daemon.log");
46107
- const platform11 = os20.platform();
46159
+ const logPath = path18.join(os21.homedir(), ".adhdev", "daemon.log");
46160
+ const platform11 = os21.platform();
46108
46161
  try {
46109
46162
  if (platform11 === "win32") {
46110
46163
  execSync7(`start /B adhdev daemon > "${logPath}" 2>&1`, {
@@ -46796,12 +46849,15 @@ function registerDaemonCommands(program2, pkgVersion3) {
46796
46849
  }));
46797
46850
  hideCommand(program2.command("daemon:restart").description("Restart ADHDev Daemon (stop \u2192 start)").option("-p, --port <port>", "Local WS server port", "19222").option("--server <url>", "Override server URL").option("--dev", "Enable Dev Mode").action(async (options) => {
46798
46851
  const { stopDaemon: stopDaemon2, isDaemonRunning: isDaemonRunning2 } = await Promise.resolve().then(() => (init_adhdev_daemon(), adhdev_daemon_exports));
46852
+ const { stopSessionHost: stopSessionHost2 } = await Promise.resolve().then(() => (init_session_host(), session_host_exports));
46799
46853
  const { spawn: spawn4 } = await import("child_process");
46800
46854
  if (isDaemonRunning2()) {
46801
46855
  console.log(import_chalk5.default.yellow("\n Stopping existing daemon..."));
46802
46856
  stopDaemon2();
46803
46857
  await new Promise((r) => setTimeout(r, 2e3));
46804
46858
  }
46859
+ stopSessionHost2();
46860
+ await new Promise((r) => setTimeout(r, 500));
46805
46861
  console.log(import_chalk5.default.cyan(" Starting new daemon..."));
46806
46862
  const args = ["daemon", "-p", options.port || "19222"];
46807
46863
  if (options.server) args.push("--server", options.server);
@@ -46824,6 +46880,7 @@ function registerDaemonCommands(program2, pkgVersion3) {
46824
46880
  }));
46825
46881
  hideCommand(program2.command("daemon:upgrade").description("Upgrade ADHDev to latest version and restart daemon").option("--no-restart", "Upgrade only, skip daemon restart").action(async (options) => {
46826
46882
  const { isDaemonRunning: isDaemonRunning2, stopDaemon: stopDaemon2 } = await Promise.resolve().then(() => (init_adhdev_daemon(), adhdev_daemon_exports));
46883
+ const { stopSessionHost: stopSessionHost2 } = await Promise.resolve().then(() => (init_session_host(), session_host_exports));
46827
46884
  const { execSync: execSync7, spawn: spawn4 } = await import("child_process");
46828
46885
  const fsMod = await import("fs");
46829
46886
  const pathMod = await import("path");
@@ -46880,6 +46937,8 @@ function registerDaemonCommands(program2, pkgVersion3) {
46880
46937
  console.log(import_chalk5.default.yellow("\n Restarting daemon..."));
46881
46938
  stopDaemon2();
46882
46939
  await new Promise((r) => setTimeout(r, 2e3));
46940
+ stopSessionHost2();
46941
+ await new Promise((r) => setTimeout(r, 500));
46883
46942
  const child = spawn4(process.execPath, [process.argv[1], "daemon", "-p", "19222"], {
46884
46943
  detached: true,
46885
46944
  stdio: "ignore",
@@ -47089,14 +47148,14 @@ function registerProviderCommands(program2) {
47089
47148
  let osPaths = {};
47090
47149
  let processNames = {};
47091
47150
  if (category === "ide") {
47092
- const fs15 = await import("fs");
47151
+ const fs16 = await import("fs");
47093
47152
  const path18 = await import("path");
47094
- const os20 = await import("os");
47095
- if (os20.platform() === "darwin") {
47153
+ const os21 = await import("os");
47154
+ if (os21.platform() === "darwin") {
47096
47155
  while (true) {
47097
47156
  const p = (await rl.question(`macOS Application Path (e.g. /Applications/${defaultName}.app): `)).trim() || `/Applications/${defaultName}.app`;
47098
47157
  if (p === "skip") break;
47099
- if (!fs15.existsSync(p)) {
47158
+ if (!fs16.existsSync(p)) {
47100
47159
  console.log(import_chalk6.default.red(` \u2717 Path not found: ${p}`));
47101
47160
  console.log(import_chalk6.default.gray(` (Please provide the exact absolute path to the .app or binary, or type 'skip')`));
47102
47161
  continue;
@@ -47106,11 +47165,11 @@ function registerProviderCommands(program2) {
47106
47165
  processNames["darwin"] = path18.basename(p, ".app");
47107
47166
  break;
47108
47167
  }
47109
- } else if (os20.platform() === "win32") {
47168
+ } else if (os21.platform() === "win32") {
47110
47169
  while (true) {
47111
47170
  const p = (await rl.question(`Windows Executable Path (e.g. C:\\Program Files\\${defaultName}\\${defaultName}.exe): `)).trim();
47112
47171
  if (!p || p === "skip") break;
47113
- if (!fs15.existsSync(p)) {
47172
+ if (!fs16.existsSync(p)) {
47114
47173
  console.log(import_chalk6.default.red(` \u2717 Path not found: ${p}`));
47115
47174
  console.log(import_chalk6.default.gray(` (Please provide the exact absolute path, or type 'skip')`));
47116
47175
  continue;
@@ -47793,8 +47852,8 @@ function registerCdpCommands(program2) {
47793
47852
  }
47794
47853
  const output = typeof result === "string" ? result : JSON.stringify(result, null, 2);
47795
47854
  if (options.output) {
47796
- const fs15 = await import("fs");
47797
- fs15.writeFileSync(options.output, output, "utf-8");
47855
+ const fs16 = await import("fs");
47856
+ fs16.writeFileSync(options.output, output, "utf-8");
47798
47857
  console.log(import_chalk6.default.green(`
47799
47858
  \u2713 Saved to ${options.output} (${output.length} chars)
47800
47859
  `));
@@ -47897,8 +47956,8 @@ function registerCdpCommands(program2) {
47897
47956
  ws2.on("message", async (data) => {
47898
47957
  const msg = JSON.parse(data.toString());
47899
47958
  if (msg.id === 1 && msg.result?.data) {
47900
- const fs15 = await import("fs");
47901
- fs15.writeFileSync(options.output, Buffer.from(msg.result.data, "base64"));
47959
+ const fs16 = await import("fs");
47960
+ fs16.writeFileSync(options.output, Buffer.from(msg.result.data, "base64"));
47902
47961
  console.log(import_chalk6.default.green(`
47903
47962
  \u2713 Screenshot saved to ${options.output}
47904
47963
  `));