docs: function inspect(target, args, options) {
      options.local = args.local;
      // otherwise the active target
      if (target) {
          if (args.webSocket) {
              // by WebSocket URL
              options.target = target;
          } else {
              // by target id
              options.target = (targets) => {
                  return targets.findIndex((_target) => {
                      return _target.id === target;
                  });
              };
          }
      }
  
      if (args.protocol) {
          options.protocol = JSON.parse(fs.readFileSync(args.protocol));
      }
  
      CDP(options, (client) => {
          // keep track of registered events
          const registeredEvents = {};
  
          const cdpRepl = repl.start({
              prompt: '\x1b[32m>>>\x1b[0m ',
              ignoreUndefined: true,
              writer: display
          });
  
          const homePath = process.env.HOME || process.env.USERPROFILE;
          const historyFile = path.join(homePath, '.cri_history');
          const historySize = 10000;
  
          function loadHistory() {
              // attempt to open the history file
              let fd;
              try {
                  fd = fs.openSync(historyFile, 'r');
              } catch (err) {
                  return; // no history file present
              }
              // populate the REPL history
              fs.readFileSync(fd, 'utf8')
                  .split('\n')
                  .filter((entry) => {
                      return entry.trim();
                  })
                  .reverse() // to be compatible with repl.history files
                  .forEach((entry) => {
                      cdpRepl.history.push(entry);
                  });
          }
  
          function saveHistory() {
              // only store the last chunk
              const entries = cdpRepl.history.slice(0, historySize).reverse().join('\n');
              fs.writeFileSync(historyFile, entries + '\n');
          }
  
          function overridePrompt(string) {
              // hack to get rid of the prompt (clean line and reposition cursor)
              console.log('\x1b[2K\x1b[G%s', string);
              cdpRepl.displayPrompt(true);
          }
  
          function overrideCommand(command) {
              const override = (params, callback) => {
                  if (typeof callback === 'function') {
                      // if a callback is provided the use it as is
                      command(params, callback);
                      return undefined;
                  } else {
                      const promise = command(params);
                      // use a custom inspect to display the outcome
                      promise.inspect = async () => {
                          try {
                              const result = await this;
                              overridePrompt(display(result));
                          } catch (err) {
                              overridePrompt(display(err));
                          }
                          // temporary placeholder
                          return '...';
                      };
                      return promise;
                  }
              };
              // inherit the doc decorations
              inheritProperties(command, override);
              return override;
          }
  
          function overrideEvent(client, domainName, itemName) {
              const event = client[domainName][itemName];
              const eventName = domainName + '.' + itemName;
              // hard code a callback to display the event data
              const override = (callback) => {
                  // remove all the listeners (just one actually) anyway
                  client.removeAllListeners(eventName);
                  const status = {};
                  // a callback will always enable/update the listener
                  if (!callback && registeredEvents[eventName]) {
                      delete registeredEvents[eventName];
                      status[eventName] = false;
                      return status;
                  } else {
                      // use the callback (or true) as a status token
                      const statusToken = (callback ? '<custom>' : true);
                      status[eventName] = registeredEvents[eventName] = statusToken;
                      if (typeof callback === 'function') {
                          // if a callback is provided the use it as is
                          event(callback);
                          return undefined;
                      } else {
                          // the default implementation just shows the params
                          event((params) => {
                              const repr = {};
                              repr[eventName] = params;
                              overridePrompt(display(repr));
                          });
                          return status;
                      }
                  }
              };
              // inherit the doc decorations
              inheritProperties(event, override);
              return override;
          }
  
          // utility custom command
          cdpRepl.defineCommand('target', {
              help: 'Display the current target',
              action: () => {
                  console.log(client.webSocketUrl);
                  cdpRepl.displayPrompt();
              }
          });
  
          // enable history
          loadHistory();
  
          // disconnect on exit
          cdpRepl.on('exit', () => {
              console.log();
              client.close();
              saveHistory();
          });
  
          // exit on disconnection
          client.on('disconnect', () => {
              console.error('Disconnected.');
              saveHistory();
              process.exit(1);
          });
  
          // add protocol API
          client.protocol.domains.forEach((domainObject) => {
              // walk the domain names
              const domainName = domainObject.domain;
              cdpRepl.context[domainName] = {};
              Object.keys(client[domainName]).forEach((itemName) => {
                  // walk the items in the domain and override commands and events
                  let item = client[domainName][itemName];
                  switch (item.category) {
                  case 'command':
                      item = overrideCommand(item);
                      break;
                  case 'event':
                      item = overrideEvent(client, domainName, itemName);
                      break;
                  }
                  cdpRepl.context[domainName][itemName] = item;
              });
          });
      }).on('error', (err) => {
          console.error('Cannot connect to remote endpoint:', err.toString());
      });
  }