#!/usr/bin/env node

'use strict';

process.env.PM2_USAGE = 'CLI';

var cst       = require('../constants.js');

var commander = require('commander');
var fs        = require('fs');
var chalk     = require('chalk');
var async     = require('async');

var debug     = require('debug')('pm2:cli');
var PM2       = require('../lib/API.js');
var pkg       = require('../package.json');
var tabtab    = require('../lib/completion.js');

var pm2 = new PM2();

commander.version(pkg.version)
  .option('-v --version', 'get version')
  .option('-s --silent', 'hide all messages', false)
  .option('-m --mini-list', 'display a compacted list without formatting')
  .option('-f --force', 'force actions')
  .option('--disable-logs', 'do not write logs')
  .option('-n --name <name>', 'set a <name> for script')
  .option('-i --instances <number>', 'launch [number] instances (for networked app)(load balanced)')
  .option('--parallel <number>', 'mumber of parallel actions (for restart/reload)')
  .option('-l --log [path]', 'specify entire log file (error and out are both included)')
  .option('-o --output <path>', 'specify out log file')
  .option('-e --error <path>', 'specify error log file')
  .option('-p --pid <pid>', 'specify pid file')
  .option('-k --kill-timeout <delay>', 'delay before sending final SIGKILL signal to process')
  .option('--listen-timeout <delay>', 'listen timeout on application reload')
  .option('--max-memory-restart <memory>', 'specify max memory amount used to autorestart (in octet or use syntax like 100M)')
  .option('--restart-delay <delay>', 'specify a delay between restarts (in milliseconds)')
  .option('--env <environment_name>', 'specify environment to get specific env variables (for JSON declaration)')
  .option('--log-type <type>', 'specify log output style (raw by default, json optional)')
  .option('-x --execute-command', 'execute a program using fork system')
  .option('--max-restarts [count]', 'only restart the script COUNT times')
  .option('-u --user <username>', 'define user when generating startup script')
  .option('--uid <uid>', 'run target script with <uid> rights')
  .option('--gid <gid>', 'run target script with <gid> rights')
  .option('--cwd <path>', 'run target script as <username>')
  .option('--hp <home path>', 'define home path when generating startup script')
  .option('-c --cron <cron_pattern>', 'restart a running process based on a cron pattern')
  .option('-w --write', 'write configuration in local folder')
  .option('--interpreter <interpreter>', 'the interpreter pm2 should use for executing app (bash, python...)')
  .option('--interpreter-args <arguments>', 'interpret options (alias of --node-args)')
  .option('--log-date-format <momentjs format>', 'add custom prefix timestamp to logs')
  .option('--no-daemon', 'run pm2 daemon in the foreground if it doesn\'t exist already')
  .option('-a --update-env', 'update environmnent on restart/reload (-a <=> apply)')
  .option('--source-map-support', 'force source map support')
  .option('--only <application-name>', 'with json declaration, allow to only act on one application')
  .option('--disable-source-map-support', 'force source map support')
  .option('--wait-ready', 'ask pm2 to wait for ready event from your app')
  .option('--merge-logs', 'merge logs from different instances but keep error and out separated')
  .option('--watch [paths]', 'watch application folder for changes', function(v, m) { m.push(v); return m;}, [])
  .option('--ignore-watch <folders|files>', 'folder/files to be ignored watching, chould be a specific name or regex - e.g. --ignore-watch="test node_modules \"some scripts\""')
  .option('--node-args <node_args>', 'space delimited arguments to pass to node in cluster mode - e.g. --node-args="--debug=7001 --trace-deprecation"')
  .option('--no-color', 'skip colors')
  .option('--no-vizion', 'start an app without vizion feature (versioning control)')
  .option('--no-autorestart', 'start an app without automatic restart')
  .option('--no-treekill', 'Only kill the main process, not detached children')
  .option('--no-pmx', 'start an app without pmx')
  .option('--no-automation', 'start an app without pmx')
  .option('--trace', 'enable transaction tracing with km')
  .option('--disable-trace', 'disable transaction tracing with km')
  .option('--attach', 'attach logging after your start/restart/stop/reload')
  .usage('[cmd] app');

commander.on('--help', function() {
  console.log('  Basic Examples:');
  console.log('');
  console.log('    Start an app using all CPUs available + set a name :');
  console.log('    $ pm2 start app.js -i 0 --name "api"');
  console.log('');
  console.log('    Restart the previous app launched, by name :');
  console.log('    $ pm2 restart api');
  console.log('');
  console.log('    Stop the app :');
  console.log('    $ pm2 stop api');
  console.log('');
  console.log('    Restart the app that is stopped :');
  console.log('    $ pm2 restart api');
  console.log('');
  console.log('    Remove the app from the process list :');
  console.log('    $ pm2 delete api');
  console.log('');
  console.log('    Kill daemon pm2 :');
  console.log('    $ pm2 kill');
  console.log('');
  console.log('    Update pm2 :');
  console.log('    $ npm install pm2@latest -g ; pm2 update');
  console.log('');
  console.log('    More examples in https://github.com/Unitech/pm2#usagefeatures');
  console.log('');
  console.log('  Deployment help:');
  console.log('');
  console.log('    $ pm2 deploy help');
  console.log('');
  console.log('');
});

if (process.argv.indexOf('-s') > -1) {
  for(var key in console){
    var code = key.charCodeAt(0);
    if(code >= 97 && code <= 122){
      console[key] = function(){};
    }
  }
}

function beginCommandProcessing() {
  pm2.getVersion(function(err, remote_version) {
    if (!err && (pkg.version != remote_version)) {
      console.log('');
      console.log(chalk.red.bold('>>>> In-memory PM2 is out-of-date, do:\n>>>> $ pm2 update'));
      console.log('In memory PM2 version:', chalk.blue.bold(remote_version));
      console.log('Local PM2 version:', chalk.blue.bold(pkg.version));
      console.log('');
    }
  });
  commander.parse(process.argv);
}

function checkCompletion(){
  return tabtab.complete('pm2', function(err, data) {
    if(err || !data) return;
    if(/^--\w?/.test(data.last)) return tabtab.log(commander.options.map(function (data) {
      return data.long;
    }), data);
    if(/^-\w?/.test(data.last)) return tabtab.log(commander.options.map(function (data) {
      return data.short;
    }), data);
    // array containing commands after which process name should be listed
    var cmdProcess = ['stop', 'restart', 'scale', 'reload', 'gracefulReload', 'delete', 'reset', 'pull', 'forward', 'backward', 'logs', 'describe', 'desc', 'show'];

    if (cmdProcess.indexOf(data.prev) > -1) {
      pm2.list(function(err, list){
        tabtab.log(list.map(function(el){ return el.name }), data);
        pm2.disconnect();
      });
    }
    else if (data.prev == 'pm2') {
      tabtab.log(commander.commands.map(function (data) {
        return data._name;
      }), data);
      pm2.disconnect();
    }
    else
      pm2.disconnect();
  });
};

var _arr = process.argv.indexOf('--') > -1 ? process.argv.slice(0, process.argv.indexOf('--')) : process.argv;

if (_arr.indexOf('log') > -1) {
  process.argv[_arr.indexOf('log')] = 'logs';
}

if (_arr.indexOf('--no-daemon') > -1) {
  //
  // Start daemon if it does not exist
  //
  // Function checks if --no-daemon option is present,
  // and starts daemon in the same process if it does not exists
  //
  console.log('pm2 launched in no-daemon mode (you can add DEBUG="*" env variable to get more messages)');

  var pm2NoDaeamon = new PM2({
    daemon_mode : false
  });

  pm2NoDaeamon.connect(function() {
    pm2 = pm2NoDaeamon;
    beginCommandProcessing();
  });

}
else if (_arr.indexOf('startup') > -1 || _arr.indexOf('unstartup') > -1) {
  setTimeout(function() {
    commander.parse(process.argv);
  }, 100);
}
else {
  // HERE we instanciate the Client object
  pm2.connect(function() {
    debug('Now connected to daemon');
    if (process.argv.slice(2)[0] === 'completion') {
      checkCompletion();
      //Close client if completion related installation
      var third = process.argv.slice(3)[0];
      if ( third == null || third === 'install' || third === 'uninstall')
        pm2.disconnect();
    }
    else {
      beginCommandProcessing();
    }
  });
}

//
// Helper function to fail when unknown command arguments are passed
//
function failOnUnknown(fn) {
  return function(arg) {
    if (arguments.length > 1) {
      console.log(cst.PREFIX_MSG + '\nUnknown command argument: ' + arg);
      commander.outputHelp();
      process.exit(cst.ERROR_EXIT);
    }
    return fn.apply(this, arguments);
  };
}

/**
 * @todo to remove at some point once it's fixed in official commander.js
 * https://github.com/tj/commander.js/issues/475
 *
 * Patch Commander.js Variadic feature
 */
function patchCommanderArg(cmd) {
  var argsIndex;
  if ((argsIndex = commander.rawArgs.indexOf('--')) >= 0) {
    var optargs = commander.rawArgs.slice(argsIndex + 1);
    cmd = cmd.slice(0, cmd.indexOf(optargs[0]));
  }
  return cmd;
}

//
// Start command
//
commander.command('start <file|json|stdin|app_name|pm_id...>')
  .option('--watch', 'Watch folder for changes')
  .description('start and daemonize an app')
  .action(function(cmd) {
    if (cmd == "-") {
      process.stdin.resume();
      process.stdin.setEncoding('utf8');
      process.stdin.on('data', function (cmd) {
        process.stdin.pause();
        pm2._startJson(cmd, commander, 'restartProcessId', 'pipe');
      });
    }
    else {
      // Commander.js patch
      cmd = patchCommanderArg(cmd);
      async.forEachLimit(cmd, 1, function(script, next) {
        pm2.start(script, commander, next);
      }, function(err) {
        pm2.speedList(err ? 1 : 0);
      });
    }
  });

//
// Docker mode
//
commander.command('docker:gen <file|config>')
  .description('generate Dockerfile in current folder')
  .option('--force', 'Force regeneration of Dockerfile')
  .action(function(script, opts) {
    pm2.generateDockerfile(script, commander);
  });

commander.command('docker:dev <file|config>')
  .option('--fresh', 'Rebuild Dockerfile')
  .option('--daemon', 'Run container in Daemon mode (debug purposes)')
  .description('wrap application into Docker container')
  .action(function(script, opts) {
    pm2.dockerMode(script, opts, 'development');
  });

commander.command('docker:dist <file|config> <image_name>')
  .option('--fresh', 'Rebuild Dockerfile')
  .option('--daemon', 'Run container in Daemon mode (debug purposes)')
  .description('wrap application into Docker container')
  .action(function(script, image_name, opts) {
    opts.imageName = image_name;
    pm2.dockerMode(script, opts, 'distribution');
  });

commander.command('trigger <proc_name> <action_name> [params]')
  .description('deploy your json')
  .action(function(pm_id, action_name, params) {
    pm2.trigger(pm_id, action_name, params);
  });

commander.command('deploy <file|environment>')
  .description('deploy your json')
  .action(function(cmd) {
    pm2.deploy(cmd, commander);
  });

commander.command('startOrRestart <json>')
  .description('start or restart JSON file')
  .action(function(file) {
    pm2._startJson(file, commander, 'restartProcessId');
  });

commander.command('startOrReload <json>')
  .description('start or gracefully reload JSON file')
  .action(function(file) {
    pm2._startJson(file, commander, 'reloadProcessId');
  });

commander.command('startOrGracefulReload <json>')
  .description('start or gracefully reload JSON file')
  .action(function(file) {
    pm2._startJson(file, commander, 'softReloadProcessId');
  });

//
// Stop specific id
//
commander.command('stop <id|name|all|json|stdin...>')
  .option('--watch', 'Stop watching folder for changes')
  .description('stop a process (to start it again, do pm2 restart <app>)')
  .action(function(param) {
    async.forEachLimit(param, 1, function(script, next) {
      pm2.stop(script, next);
    }, function(err) {
      pm2.speedList(err ? 1 : 0);
    });
  });

//
// Stop All processes
//
commander.command('restart <id|name|all|json|stdin...>')
  .option('--watch', 'Toggle watching folder for changes')
  .description('restart a process')
  .action(function(param) {
    // Commander.js patch
    param = patchCommanderArg(param);
    async.forEachLimit(param, 1, function(script, next) {
      pm2.restart(script, commander, next);
    }, function(err) {
      pm2.speedList(err ? 1 : 0);
    });
  });

//
// Scale up/down a process in cluster mode
//
commander.command('scale <app_name> <number>')
  .description('scale up/down a process in cluster mode depending on total_number param')
  .action(function(app_name, number) {
    pm2.scale(app_name, number);
  });

//
// snapshot PM2
//
commander.command('snapshot')
  .description('snapshot PM2 memory')
  .action(function() {
    pm2.snapshotPM2();
  });

//
// snapshot PM2
//
commander.command('profile <command>')
  .description('profile CPU')
  .action(function(command) {
    pm2.profilePM2(command);
  });

//
// Reload process(es)
//
commander.command('reload <name|all>')
  .description('reload processes (note that its for app using HTTP/HTTPS)')
  .action(function(pm2_id) {
    pm2.reload(pm2_id, commander);
  });

//
// Reload process(es)
//
commander.command('gracefulReload <name|all>')
  .description('gracefully reload a process. Send a "shutdown" message to close all connections.')
  .action(function(pm2_id) {
    pm2.gracefulReload(pm2_id, commander);
  });

commander.command('id <name>')
  .description('get process id by name')
  .action(function(name) {
    pm2.getProcessIdByName(name);
  });

//
// Stop and delete a process by name from database
//
commander.command('delete <name|id|script|all|json|stdin...>')
  .description('stop and delete a process from pm2 process list')
  .action(function(name) {
    if (name == "-") {
      process.stdin.resume();
      process.stdin.setEncoding('utf8');
      process.stdin.on('data', function (param) {
        process.stdin.pause();
        pm2.delete(param, 'pipe');
      });
    } else
      async.forEachLimit(name, 1, function(script, next) {
        pm2.delete(script,'', next);
      }, function(err) {
        pm2.speedList(err ? 1 : 0);
      });
  });

//
// Send system signal to process
//
commander.command('sendSignal <signal> <pm2_id|name>')
  .description('send a system signal to the target process')
  .action(function(signal, pm2_id) {
    if (isNaN(parseInt(pm2_id))) {
      console.log(cst.PREFIX_MSG + 'Sending signal to process name ' + pm2_id);
      pm2.sendSignalToProcessName(signal, pm2_id);
    } else {
      console.log(cst.PREFIX_MSG + 'Sending signal to process id ' + pm2_id);
      pm2.sendSignalToProcessId(signal, pm2_id);
    }
  });

//
// Stop and delete a process by name from database
//
commander.command('ping')
  .description('ping pm2 daemon - if not up it will launch it')
  .action(function() {
    pm2.ping();
  });

commander.command('updatePM2')
  .description('update in-memory PM2 with local PM2')
  .action(function() {
    pm2.update();
  });
commander.command('update')
  .description('(alias) update in-memory PM2 with local PM2')
  .action(function() {
    pm2.update();
  });

/**
 * Module specifics
 */
commander.command('install <module|git:// url>')
  .alias('module:install')
  .description('install or update a module and run it forever')
  .action(function(plugin_name) {
    pm2.install(plugin_name, commander);
  });

commander.command('module:update <module|git:// url>')
  .description('update a module and run it forever')
  .action(function(plugin_name) {
    pm2.install(plugin_name);
  });


commander.command('module:generate [app_name]')
  .description('Generate a sample module in current folder')
  .action(function(app_name) {
    pm2.generateModuleSample(app_name);
  });

commander.command('uninstall <module>')
  .alias('module:uninstall')
  .description('stop and uninstall a module')
  .action(function(plugin_name) {
    pm2.uninstall(plugin_name);
  });


commander.command('publish')
  .alias('module:publish')
  .description('Publish the module you are currently on')
  .action(function() {
    pm2.publish();
  });

commander.command('set [key] [value]')
  .description('sets the specified config <key> <value>')
  .action(function(key, value) {
    pm2.set(key, value);
  });

commander.command('multiset <value>')
  .description('multiset eg "key1 val1 key2 val2')
  .action(function(str) {
    pm2.multiset(str);
  });

commander.command('get [key]')
  .description('get value for <key>')
  .action(function(key) {
    pm2.get(key);
  });

commander.command('conf [key] [value]')
  .description('get / set module config values')
  .action(function(key, value) {
    pm2.conf(key, value);
  });

commander.command('config <key> [value]')
  .description('get / set module config values')
  .action(function(key, value) {
    pm2.conf(key, value);
  });

commander.command('unset <key>')
  .description('clears the specified config <key>')
  .action(function(key) {
    pm2.unset(key);
  });

commander.command('report')
  .description('give a full pm2 report for https://github.com/Unitech/pm2/issues')
  .action(function(key) {
    pm2.report();
  });

//
// Keymetrics CLI integratio
//
commander.command('link [secret_key|command] [public_key] [machine_name]')
  .alias('interact')
  .option('--info-node [url]', 'set url info node')
  .description('linking action to keymetrics.io - command can be stop|info|delete|restart')
  .action(pm2._pre_interact.bind(pm2));

commander.command('unmonitor [name]')
  .description('unmonitor target process')
  .action(function(name) {
    pm2.monitorState('unmonitor', name);
  });

commander.command('monitor [name]')
  .description('monitor target process')
  .action(function(name) {
    pm2.monitorState('monitor', name);
  });

commander.command('open')
  .description('open dashboard in browser')
  .action(function(name) {
    pm2.openDashboard();
  });

commander.command('register')
  .description('create an account on keymetrics')
  .action(function(name) {
    pm2.registerToKM();
  });

commander.command('login')
  .description('login to keymetrics and link current PM2')
  .action(function(name) {
    pm2.loginToKM();
  });

//
// Web interface
//
commander.command('web')
  .description('launch a health API on ' + cst.WEB_IPADDR + ':' + cst.WEB_PORT)
  .action(function() {
    console.log('Launching web interface on ' + cst.WEB_IPADDR + ':' + cst.WEB_PORT);
    pm2.web();
  });

//
// Save processes to file
//
commander.command('dump')
  .alias('save')
  .description('dump all processes for resurrecting them later')
  .action(failOnUnknown(function() {
    pm2.dump();
  }));

//
// Save processes to file
//
commander.command('send <pm_id> <line>')
  .description('send stdin to <pm_id>')
  .action(function(pm_id, line) {
    pm2.sendLineToStdin(pm_id, line);
  });

//
// Attach to stdin/stdout
// Not TTY ready
//
commander.command('attach <pm_id> [command separator]')
  .description('attach stdin/stdout to application identified by <pm_id>')
  .action(function(pm_id, separator) {
    pm2.attach(pm_id, separator);
  });

//
// Resurrect
//
commander.command('resurrect')
  .description('resurrect previously dumped processes')
  .action(failOnUnknown(function() {
    console.log(cst.PREFIX_MSG + 'Resurrecting');
    pm2.resurrect();
  }));

//
// Set pm2 to startup
//
commander.command('unstartup [platform]')
  .description('disable and clear auto startup - [platform]=systemd,upstart,launchd,rcd')
  .action(function(platform) {
    pm2.uninstallStartup(platform, commander);
  });

//
// Set pm2 to startup
//
commander.command('startup [platform]')
  .description('setup script for pm2 at boot - [platform]=systemd,upstart,launchd,rcd')
  .action(function(platform) {
    pm2.startup(platform, commander);
  });

//
// Logrotate
//
commander.command('logrotate')
  .description('copy default logrotate configuration')
  .action(function(cmd) {
    pm2.logrotate(commander);
  });

//
// Sample generate
//

commander.command('ecosystem [mode]')
  .alias('generate')
  .description('generate a process conf file. (mode = null or simple)')
  .action(function(mode) {
    pm2.generateSample(mode);
  });

commander.command('reset <name|id|all>')
  .description('reset counters for process')
  .action(function(proc_id) {
    pm2.reset(proc_id);
  });

commander.command('describe <id>')
  .description('describe all parameters of a process id')
  .action(function(proc_id) {
    pm2.describe(proc_id);
  });

commander.command('desc <id>')
  .description('(alias) describe all parameters of a process id')
  .action(function(proc_id) {
    pm2.describe(proc_id);
  });

commander.command('info <id>')
  .description('(alias) describe all parameters of a process id')
  .action(function(proc_id) {
    pm2.describe(proc_id);
  });

commander.command('show <id>')
  .description('(alias) describe all parameters of a process id')
  .action(function(proc_id) {
    pm2.describe(proc_id);
  });

//
// List command
//
commander
  .command('list')
  .alias('ls')
  .description('list all processes')
  .option('--watch', 'constant refresh of process listing')
  .action(function() {
    pm2.list(commander)
  });

commander.command('l')
  .description('(alias) list all processes')
  .action(function() {
    pm2.list()
  });

commander.command('status')
  .description('(alias) list all processes')
  .action(function() {
    pm2.list()
  });


// List in raw json
commander.command('jlist')
  .description('list all processes in JSON format')
  .action(function() {
    pm2.jlist()
  });

// List in prettified Json
commander.command('prettylist')
  .description('print json in a prettified JSON')
  .action(failOnUnknown(function() {
    pm2.jlist(true);
  }));

//
// Dashboard command
//
commander.command('monit')
  .alias('m')
  .description('launch termcaps monitoring')
  .action(function() {
    pm2.dashboard();
  });

commander.command('imonit')
  .description('launch legacy termcaps monitoring')
  .action(function() {
    pm2.monit();
  });

commander.command('dashboard')
  .alias('dash')
  .description('launch dashboard with monitoring and logs')
  .action(function() {
    pm2.dashboard();
  });


//
// Flushing command
//
commander.command('flush')
  .description('flush logs')
  .action(failOnUnknown(function() {
    pm2.flush();
  }));

//
// Reload all logs
//
commander.command('reloadLogs')
  .description('reload all logs')
  .action(function() {
    pm2.reloadLogs();
  });

//
// Log streaming
//
commander.command('logs [id|name]')
  .option('--json', 'json log output')
  .option('--format', 'formated log output')
  .option('--raw', 'raw output')
  .option('--err', 'only shows error output')
  .option('--out', 'only shows standard output')
  .option('--lines <n>', 'output the last N lines, instead of the last 15 by default')
  .option('--timestamp [format]', 'add timestamps (default format YYYY-MM-DD-HH:mm:ss)')
  .option('--nostream', 'print logs without lauching the log stream')
  .description('stream logs file. Default stream all logs')
  .action(function(id, cmd) {
    var Logs = require('../lib/API/Log.js');

    if (!id) id = 'all';

    var line = 15;
    var raw  = false;
    var exclusive = false;
    var timestamp = false;

    if(!isNaN(cmd.lines)){
      line = parseInt(cmd.lines);
    }

    if (cmd.parent.rawArgs.indexOf('--raw') !== -1)
      raw = true;

    if (cmd.timestamp)
      timestamp = typeof cmd.timestamp === 'string' ? cmd.timestamp : 'YYYY-MM-DD-HH:mm:ss';

    if (cmd.out === true)
      exclusive = 'out';

    if (cmd.err === true)
      exclusive = 'err';

    if (cmd.nostream === true)
      pm2.printLogs(id, line, raw, timestamp, exclusive);
    else if (cmd.json === true)
      Logs.jsonStream(pm2.Client, id);
    else if (cmd.format === true)
      Logs.formatStream(pm2.Client, id, false, 'YYYY-MM-DD-HH:mm:ssZZ');
    else
      pm2.streamLogs(id, line, raw, timestamp, exclusive);
  });


//
// Kill
//
commander.command('kill')
  .description('kill daemon')
  .action(failOnUnknown(function(arg) {
    pm2.killDaemon(function() {
      process.exit(cst.SUCCESS_EXIT);
    });
  }));

//
// Update repository for a given app
//

commander.command('pull <name> [commit_id]')
  .description('updates repository for a given app')
  .action(function(pm2_name, commit_id) {

    if (commit_id !== undefined) {
      pm2._pullCommitId({
        pm2_name: pm2_name,
        commit_id: commit_id
      });
    }
    else
      pm2.pullAndRestart(pm2_name);
  });

//
// Update repository to the next commit for a given app
//
commander.command('forward <name>')
  .description('updates repository to the next commit for a given app')
  .action(function(pm2_name) {
    pm2.forward(pm2_name);
  });

//
// Downgrade repository to the previous commit for a given app
//
commander.command('backward <name>')
  .description('downgrades repository to the previous commit for a given app')
  .action(function(pm2_name) {
    pm2.backward(pm2_name);
  });

//
// Force PM2 to trigger garbage collection
//
commander.command('gc')
  .description('force PM2 to trigger garbage collection')
  .action(function() {
    pm2.forceGc();
  });

//
// Perform a deep update of PM2
//
commander.command('deepUpdate')
  .description('performs a deep update of PM2')
  .action(function() {
    pm2.deepUpdate();
  });

//
// Launch a http server that expose a given path on given port
//
commander.command('serve [path] [port]')
  .alias('expose')
  .description('serve a directory over http via port')
  .action(function (path, port) {
    pm2.serve(path, port, commander);
  });

//
// Catch all
//
commander.command('*')
  .action(function() {
    console.log(cst.PREFIX_MSG + '\nCommand not found');
    commander.outputHelp();
    // Check if it does not forget to close fds from RPC
    process.exit(cst.ERROR_EXIT);
  });

//
// Display help if 0 arguments passed to pm2
//
if (process.argv.length == 2) {
  commander.parse(process.argv);
  commander.outputHelp();
  // Check if it does not forget to close fds from RPC
  process.exit(cst.ERROR_EXIT);
}
