var refreshWatchSet = new watch.WatchSet;

var refreshMessages = buildmessage.capture(function () {

var fs = require('fs');
var path = require('path');
var _ = require('underscore');
var util = require('util');
var buildmessage = require('./buildmessage.js');
var tropohouse = require('./tropohouse.js');
var packageCache = require('./package-cache.js');
var localCatalog = require('./catalog-local.js');
var remoteCatalog = require('./catalog-remote.js');
var files = require('./files.js');
var prebuiltBootstrap = require('./catalog-bootstrap-prebuilt.js');
var checkoutBootstrap = require('./catalog-bootstrap-checkout.js');
var project = require('./project.js');
var utils = require('./utils.js');
var config = require('./config.js');
var packageClient = require('./package-client.js');
var Console = require('./console.js').Console;

var catalog = exports;

catalog.Refresh = {};

// Refresh strategy: once at program start
catalog.Refresh.OnceAtStart = function (options) {
  var self = this;
  self.options = _.extend({}, options);
  self.refreshFailed = undefined;
};

catalog.Refresh.OnceAtStart.prototype.beforeCommand = function () {
  var self = this;
  try {
    catalog.official.refresh(self.options);
    self.refreshFailed = false;
  } catch (err) {
    // XXX: Should we fail-hard if the catalog is empty???
    self.refreshFailed = true;

    var errorType = err.errorType;

    // Example errors:

    // Offline, with name-based host:
    //   errorType: DDP.ConnectionError
    //   Network error: ws://packages.meteor.com/websocket: getaddrinfo ENOTFOUND

    // Offline, with IP-based host:
    //   errorType: DDP.ConnectionError
    //   Network error: ws://8.8.8.8/websocket: connect ENETUNREACH

    // Online, bad port:
    //    errorType: DDP.ConnectionError
    //    Network error: wss://packages.meteor.com:8888/websocket: connect ECONNREFUSED

    // Online, socket hangup:
    //   errorType: DDP.ConnectionError
    //   Network error: wss://packages.meteor.com:80/websocket: socket hang up
    Console.warn("Unable to refresh catalog (are you offline?)\n");

    // XXX: Make this Console.debug(err)
    if (Console.isDebugEnabled()) {
      Console.printError(err);
    }

    // XXX: Support refreshCanFail?
    //if (!self.options.refreshCanFail) {
    //  throw err;
    //}
  }
};

// Refresh strategy: never (we don't use the package catalog)
catalog.Refresh.Never = function (options) {
  var self = this;
  self.options = _.extend({}, options);
};

// As a work-around for [] !== [], we use a function to check whether values are acceptable
var ACCEPT_NON_EMPTY = function (result) {
  // null, undefined
  if (result === null || result === undefined) {
    return false;
  }
  // []
  if (result.length === 0) {
    return false;
  }
  return true;
};

// The LayeredCatalog provides a way to query multiple catalogs in a uniform way
// A LayeredCatalog typically contains:
//  - a local catalog referencing the packages of the project
//  - a reference to the official catalog
var LayeredCatalog = function() {
  var self = this;

  self.localCatalog = null;
  self.otherCatalog = null;

  // Constraint solver using this catalog.
  self.resolver = null;

  // Each complete catalog needs its own package cache.
  self.packageCache = new packageCache.PackageCache(self);
};

_.extend(LayeredCatalog.prototype, {
  toString: function () {
    var self = this;
    return "LayeredCatalog []";
  },

  setCatalogs: function(local, remote) {
    var self = this;
    self.localCatalog = local;
    self.otherCatalog = remote;
  },

  addLocalPackage: function (directory) {
    var self = this;
    self.localCatalog.addLocalPackage(directory);
  },

  getAllBuilds: function (name, version) {
    var self = this;
    return self._returnFirst("getAllBuilds", arguments, ACCEPT_NON_EMPTY);
  },

  getLatestVersion: function (name) {
    var self = this;
    return self._returnFirst("getLatestVersion", arguments, ACCEPT_NON_EMPTY);
  },

  getAllPackageNames: function () {
    var self = this;
    return _.union(self.localCatalog.getAllPackageNames(), self.otherCatalog.getAllPackageNames());
  },

  _returnFirst: function(f, args, validityOracle) {
    var self = this;
    var splittedArgs = Array.prototype.slice.call(args,0);
    var result = self.localCatalog[f].apply(self.localCatalog, splittedArgs);
    if (validityOracle(result)) {
      return result;
    }
    return self.otherCatalog[f].apply(self.otherCatalog, splittedArgs);
  },

  getBuildsForArches: function (name, version, arches) {
    return this._returnFirst("getBuildsForArches", arguments, ACCEPT_NON_EMPTY);
  },

  getBuildWithPreciseBuildArchitectures: function (versionRecord, buildArchitectures) {
    return this._returnFirst("getBuildWithPreciseBuildArchitectures", arguments, ACCEPT_NON_EMPTY);
  },

  getForgottenECVs: function (packageName) {
    var self = this;
    var versions = self.otherCatalog.getSortedVersions(packageName);
    var forgottenECVs = {};
    _.each(versions, function (v) {
      var vr = self.otherCatalog.getVersion(packageName, v);
      forgottenECVs[v] = vr.earliestCompatibleVersion;
    });
    return forgottenECVs;
  },

  getLoadPathForPackage: function (name, version, constraintSolverOpts) {
    var self = this;
    return self.localCatalog.getLoadPathForPackage(name, version, constraintSolverOpts);
  },

  getLocalPackageNames: function () {
    return this.localCatalog.getLocalPackageNames();
  },

  getPackage: function (name) {
    return this._returnFirst("getPackage", arguments, ACCEPT_NON_EMPTY);
  },

  // Returns general (non-version-specific) information about a
  // release track, or null if there is no such release track.
  getReleaseTrack: function (name) {
    return this.otherCatalog.getReleaseTrack(name);
  },

  getReleaseVersion: function (track, version) {
    return this.otherCatalog.getReleaseVersion(track, version);
  },

  getSortedRecommendedReleaseVersions: function (track, laterThanOrderKey) {
    return this.otherCatalog.getSortedRecommendedReleaseVersions(track, laterThanOrderKey);
  },

  getSortedVersions: function (name) {
    return this._returnFirst("getSortedVersions", arguments, ACCEPT_NON_EMPTY);
  },

  getVersion: function (name, version) {
    var self = this;
    var result = self.localCatalog.getVersion(name, version);
    if (!result) {
      if (/\+/.test(version)) {
        return null;
      }
      result = self.otherCatalog.getVersion(name, version);
    }
    return result;
  },

  initialize: function (options) {
    this.localCatalog.initialize(options);
  },

  isLocalPackage: function (name) {
    return this.localCatalog.isLocalPackage(name);
  },

  rebuildLocalPackages: function (namedPackages) {
    this.packageCache.refresh();
    return this.localCatalog.rebuildLocalPackages(namedPackages);
  },

  reset: function () {
    this.localCatalog.reset();
  },

  // As getVersion, but returns info on the latest version of the
  // package, or null if the package doesn't exist or has no versions.
  // It does not include prereleases (with dashes in the version);
  getLatestMainlineVersion: function (name) {
    var self = this;
    buildmessage.assertInCapture();

    var versions = self.getSortedVersions(name);
    versions.reverse();
    var latest = _.find(versions, function (version) {
      return !/-/.test(version);
    });
    if (!latest)
      return null;
    return self.getVersion(name, latest);
  },

  resolveConstraints: function (constraints, resolverOpts, opts) {
    var self = this;
    opts = opts || {};
      // OK, since we are the complete catalog, the uniload catalog must be fully
      // initialized, so it's safe to load a resolver if we didn't
      // already. (Putting this off until the first call to resolveConstraints
      // also helps with performance: no need to build this package and load the
      // large mori module unless we actually need it.)
      self.resolver || self._initializeResolver();

      // Looks like we are not going to be able to avoid calling the constraint
      // solver, so let's process the input (constraints) into the correct
      // arguments to the constraint solver.
      //
      // -deps: list of package names that we depend on
      // -constr: constraints of the proper form from parseConstraint in utils.js
      //
      // Weak dependencies are constraints (they constrain the result), but not
      // dependencies.
      var deps = [];
      var constr = [];
      _.each(constraints, function (constraint) {
        constraint = _.clone(constraint);
        if (!constraint.weak) {
          deps.push(constraint.name);
        }
        delete constraint.weak;
        constr.push(constraint);
      });

      // If we are called with 'ignore projectDeps', then we don't even look to
      // see what the project thinks and recalculate everything. Similarly, if the
      // project root path has not been initialized, we are probably running
      // outside of a project, and have nothing to look at for guidance.
      if (!opts.ignoreProjectDeps && project.project &&
        project.project.viableDepSource) {
        // Anything in the project's dependencies was calculated based on a
        // previous constraint solver run, and needs to be taken as absolute truth
        // for now: we can't use any packages that are of different versions from
        // what we've already decided from the project!
        _.each(project.project.getVersions(), function (version, name) {
            constr.push(utils.parseConstraint(name + "@=" + version));
        });
      }

      // Local packages can only be loaded from the version we have the source
      // for: that's a weak exact constraint.
      _.each(self.packageSources, function (packageSource, name) {
        constr.push(utils.parseConstraint(name + "@=" + packageSource.version));
      });

      var ret = buildmessage.enterJob({
          title: "Figuring out the best package versions to use." },
        function () {
          // Then, call the constraint solver, to get the valid transitive
          // subset of those versions to record for our solution. (We don't just
          // return the original version lock because we want to record the
          // correct transitive dependencies)
          return self.resolver.resolve(deps, constr, resolverOpts);
        });
      if (ret["usedRCs"]) {
        var expPackages = [];
        _.each(ret.answer, function(version, package) {
          if (self.isLocalPackage(package))
            return;
          if (version.split('-').length > 1) {
            if (!(resolverOpts.previousSolution &&
              resolverOpts.previousSolution[package] === version)) {
                var oldConstraints = _.where(constr, { name: package } );
                var printMe = true;
                _.each(oldConstraints, function (oC) {
                  _.each(oC.constraints, function (specOC) {
                    if (specOC.version === version) {
                      printMe = false;
                    }
                  });
                });
                if (printMe) {
                  expPackages.push({
                    name: "  " + package + "@" + version,
                    description: self.getVersion(package, version).description
                  });
                };
          }}
        });
        if (!_.isEmpty(expPackages)) {
          // XXX: Couldn't figure out how to word this better for better tenses.
          //
          // XXX: this shouldn't be here. This is library code... it
          // shouldn't be printing.
          // https://github.com/meteor/meteor/wiki/Meteor-Style-Guide#only-user-interface-code-should-engage-with-the-user
          Console.info(
            "\nIn order to resolve constraints, we had to use the following\n"+
            "experimental package versions:");
          utils.printPackageList(expPackages);
        }
      }
      return ret.answer;
  },

  // Refresh the catalogs referenced by this catalog.
  // options:
  // - forceRefresh: even if there is a future in progress, refresh the catalog
  //   anyway. When we are using hot code push, we may be restarting the app
  //   because of a local package change that impacts that catalog. Don't wait
  //   on the official catalog to refresh data.json, in this case.
  // - watchSet: if provided, any files read in reloading packages will be added
  //   to this set.
  refresh: function (options) {
    var self = this;
    self.localCatalog.refresh(options);

    // Note that otherCatalog can throw, if we fail to connect
    // XXX: Order of refreshes?  Continue on error?
    self.otherCatalog.refresh(options);
    self.packageCache.refresh();
    self.resolver = null;
  },

  _initializeResolver: function () {
    var self = this;
    var uniload = require('./uniload.js');

    var constraintSolverPackage =  uniload.load({
      packages: [ 'constraint-solver']
    })['constraint-solver'];
    self.resolver =
      new constraintSolverPackage.ConstraintSolver.PackagesResolver(self, {
        nudge: function () {
          Console.nudge(true);
        }
      });
  },

  watchLocalPackageDirs: function (watchSet) {
    var self = this;
    self.localCatalog.watchLocalPackageDirs(watchSet);
  }
});

exports.DEFAULT_TRACK = remoteCatalog.DEFAULT_TRACK;
exports.official = remoteCatalog.official;

//Instantiate the various catalogs
if (files.inCheckout()) {
  exports.uniload = new checkoutBootstrap.BootstrapCatalogCheckout();
} else {
  exports.uniload = new prebuiltBootstrap.BootstrapCatalogPrebuilt();
}

// This is the catalog that's used to actually drive the constraint solver: it
// contains local packages, and since local packages always beat server
// packages, it doesn't contain any information about the server version of
// local packages.
exports.complete = new LayeredCatalog();
exports.complete.setCatalogs(new localCatalog.LocalCatalog({containingCatalog : exports.complete}), remoteCatalog.official);


keep keep keep replace keep keep keep keep

Tinytest.add("spacebars-compiler - compiler output", function (test) {

  var run = function (input, expected) {
    if (expected.fail) {
      var expectedMessage = expected.fail;
      // test for error starting with expectedMessage
      var msg = '';
      test.throws(function () {
        try {
          SpacebarsCompiler.compile(input, {isTemplate: true});
        } catch (e) {
          msg = e.message;
          throw e;
        }
      });
      test.equal(msg.slice(0, expectedMessage.length),
                 expectedMessage);
    } else {
      var output = SpacebarsCompiler.compile(input, {isTemplate: true});
      var postProcess = function (string) {
        // remove initial and trailing parens
        string = string.replace(/^\(([\S\s]*)\)$/, '$1');
        if (! (Package.minifiers && Package.minifiers.UglifyJSMinify)) {
          // these tests work a lot better with access to beautification,
          // but let's at least do some sort of test without it.
          // These regexes may have to be adjusted if new tests are added.

          // Remove single-line comments, including line nums from build system.
          string = string.replace(/\/\/.*$/mg, '');
          string = string.replace(/\s+/g, ''); // kill whitespace
        }
        return string;
      };
      // compare using Function .toString()!
      test._stringEqual(
        postProcess(output.toString()),
        postProcess(
          SpacebarsCompiler._beautify('(' + expected.toString() + ')')),
        input);
    }
  };

  coffee.runCompilerOutputTests(run);
});

coffee = {
  runCompilerOutputTests: null // implemented in compiler_output_tests.coffee
};

Tinytest.add("spacebars-compiler - compiler errors", function (test) {

  var getError = function (input) {
    try {
      SpacebarsCompiler.compile(input);
    } catch (e) {
      return e.message;
    }
    test.fail("Didn't throw an error: " + input);
  };

  var assertStartsWith = function (a, b) {
    test.equal(a.substring(0, b.length), b);
  };

  var isError = function (input, errorStart) {
    assertStartsWith(getError(input), errorStart);
  };

  isError("<input></input>",
          "Unexpected HTML close tag.  <input> should have no close tag.");
  isError("{{#each foo}}<input></input>{{/foo}}",
          "Unexpected HTML close tag.  <input> should have no close tag.");

  isError("{{#if}}{{/if}}", "#if requires an argument");
  isError("{{#with}}{{/with}}", "#with requires an argument");
  isError("{{#each}}{{/each}}", "#each requires an argument");
  isError("{{#unless}}{{/unless}}", "#unless requires an argument");

  isError("{{0 0}}", "Expected IDENTIFIER");

  isError("{{> foo 0 0}}",
          "First argument must be a function");
  isError("{{> foo 0 x=0}}",
          "First argument must be a function");
  isError("{{#foo 0 0}}{{/foo}}",
          "First argument must be a function");
  isError("{{#foo 0 x=0}}{{/foo}}",
          "First argument must be a function");

  _.each(['asdf
', '{{!foo}}
', '{{!foo}}
',
          'asdf', '{{!foo}}', '{{!foo}} '], function (badFrag) {
            isError(badFrag, "Unexpected HTML close tag");
          });
});


// Define an object named exports. This will cause underscore.js to put `_` as a
// field on it, instead of in the global namespace.  See also post.js.
exports = {};


OAuth.registerService("meteor-developer", 2, null, function (query) {
  var response = getTokens(query);
  var accessToken = response.accessToken;
  var identity = getIdentity(accessToken);

  var serviceData = {
    accessToken: OAuth.sealSecret(accessToken),
    expiresAt: (+new Date) + (1000 * response.expiresIn)
  };

  _.extend(serviceData, identity);

  // only set the token in serviceData if it's there. this ensures
  // that we don't lose old ones (since we only get this on the first
  // log in attempt)
  if (response.refreshToken)
    serviceData.refreshToken = OAuth.sealSecret(response.refreshToken);

  return {
    serviceData: serviceData,
    options: {profile: {name: serviceData.username}}
    // XXX use username for name until meteor accounts has a profile with a name
  };
});

// returns an object containing:
// - accessToken
// - expiresIn: lifetime of token in seconds
// - refreshToken, if this is the first authorization request and we got a
//   refresh token from the server
var getTokens = function (query) {
  var config = ServiceConfiguration.configurations.findOne({
    service: 'meteor-developer'
  });
  if (!config)
    throw new ServiceConfiguration.ConfigError();

  var response;
  try {
    response = HTTP.post(
      MeteorDeveloperAccounts._server + "/oauth2/token", {
        params: {
          grant_type: "authorization_code",
          code: query.code,
          client_id: config.clientId,
          client_secret: OAuth.openSecret(config.secret),
          redirect_uri: OAuth._redirectUri('meteor-developer', config)
        }
      }
    );
  } catch (err) {
    throw _.extend(
      new Error(
        "Failed to complete OAuth handshake with Meteor developer accounts. "
          + err.message
      ),
      {response: err.response}
    );
  }

  if (! response.data || response.data.error) {
    // if the http response was a json object with an error attribute
    throw new Error(
      "Failed to complete OAuth handshake with Meteor developer accounts. " +
        (response.data ? response.data.error :
         "No response data")
    );
  } else {
    return {
      accessToken: response.data.access_token,
      refreshToken: response.data.refresh_token,
      expiresIn: response.data.expires_in
    };
  }
};

var getIdentity = function (accessToken) {
  try {
    return HTTP.get(
      MeteorDeveloperAccounts._server + "/api/v1/identity",
      {
        headers: { Authorization: "Bearer " + accessToken }
      }
    ).data;
  } catch (err) {
    throw _.extend(
      new Error("Failed to fetch identity from Meteor developer accounts. " +
                err.message),
      {response: err.response}
    );
  }
};

MeteorDeveloperAccounts.retrieveCredential = function (credentialToken,
                                                       credentialSecret) {
  return OAuth.retrieveCredential(credentialToken, credentialSecret);
};


var updateTestPackage = function (npmDependencies) {

keep keep keep replace keep keep keep keep keep


#menu {
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;

  .btns-group,
  .btns-group-vertical {
    margin: 2em auto 2em;
    width: 80%;

    .btn-secondary {
      .font-s1;
      padding-top: .5em;
      padding-bottom: .5em;
    }
  }

  .btns-group-vertical .btn-secondary {
    .force-wrap;
    padding-right: 2.5em;
    text-align: left;
    text-indent: 0;
    white-space: normal; // Resets wrapping
    width: 100%;

    & + .btn-secondary {
      margin-top: .5rem;

      &:before {
        .position(absolute, -.5rem, 50%, auto, auto, 1px, .5rem);
        background: lighten(#517096, 5%);
        content: '';
      }
    }

    [class^="icon-"],
    [class*=" icon-"] {
      .position(absolute, .5em, .5em, auto, auto);
      line-height: 20px;
    }
  }

  .list-todos {
    a {
      box-shadow: rgba(255,255,255,.15) 0 1px 0 0;
      display: block;
      line-height: 1.5em;
      padding: .75em 2.5em;
      position: relative;
    }

    .count-list {
      .transition( all 200ms ease-in );
      background: rgba(255,255,255,.1);
      border-radius: 1em;
      float: right;
      font-size: .7rem;
      line-height: 1;
      margin-top: .25rem;
      margin-right: -1.5em;
      padding: .3em .5em;
    }

    [class^="icon-"],
    [class*=" icon-"] {
      .font-s2;
      float: left;
      margin-left: -1.5rem;
      margin-right: .5rem;
      margin-top: .1rem;
      width: 1em;
    }

    .icon-lock {
      .font-s1;
      margin-top: .2rem;
      opacity: .8;
    }

    .list-todo {
      color: rgba(255,255,255,.4);

      &:hover,
      &:active,
      &.active {
        color: @color-empty;
        .count-list { background: @color-primary; }
      }

      .cordova &:hover {
        // Prevent hover states from being noticeable on Cordova apps
        color: rgba(255,255,255,.4);
      }
    }
  }
}


Tinytest.add("less - presence", function(test) {

  var div = document.createElement('div');
  Blaze.render(Template.less_test_presence, div);
  div.style.display = 'block';
  document.body.appendChild(div);

  var p = div.querySelector('p');
  test.equal(getStyleProperty(p, 'border-left-style'), "dashed");

  // test @import
  test.equal(getStyleProperty(p, 'border-right-style'), "dotted");
  test.equal(getStyleProperty(p, 'border-bottom-style'), "double");

  document.body.removeChild(div);
});


- #2994 allow to specify typescript version to be installed

## 2.6

- #2144 #1060 #2957 #2033 #1872 #2938 #971 Select application uid/gid via --uid --gid (CLI+JSON) + display user via pm2 ls
- #2998 pm2 report command for automated system inspection
- #2997 --disable-logs option to suppress error
- #2290 allow to declare apps under "pm2" attribute (eq "apps"). Nicer in package.json
- pm2 install module-name --uid <uid> --gid <gid> possible
- #2501 low memory environment pm2 setting via PM2_OPTIMIZE_MEMORY (beta)
- #2968 pm2 attach <pm_id> to attach to process stdin / stdout
- pm2-runtime -> drop in replacement for the node.js binary
- #2951 pm2 reload command locker via timestamped lock file
- #2977 pm2 reloadLogs protected
- #2958 Allow to delete attribute via --attribute null
- #2980 PM2_SILENT=true pm2 startup
- #2690 --parallel <number> command allows to change the nb of concurrent actions (reload/restart)
- expose cwd on CLI via --cwd
- multiple pm2-docker enhacements
- Alias pm2.link and pm2.unlink to pm2.interact and pm2._pre_interact
- Allow to customize kill signal via PM2_KILL_SIGNAL
- Support git+http in module installation
- force reverse interaction reconnection on internet discovery
- `--instances -1` when having a 1 cpu is no-longer spawning no processes #2953
- refactor the context retrieving from error
- add a TTL for file cache entry
- #2956 Fix listen_timeout in combination with wait_ready

|!| need to be documented when released

### Breaking

- Drop pm2-daemon CLI

## 2.5

- `pm2 register|login` to create new account / login on Keymetrics + auto link
- `pm2 open` to open dashboard on browser
- `pm2 monitor|unmonitor <pm_id|name|all>` for selective monitoring
- #2818 alias pm2-docker to pm2-daemon
- #2809 correctly resolve git/npm repo when running pm2 install
- #2861 better auto exit check for docker
- #2870 avoid null error when preparing app config
- #2872 avoid showing useless warning
- #438 allow to override daemon config paths via env (example: `PM2_PID_FILE_PATH` to override pid file of the daemon)
- #2849 better gentoo template for pm2 startup
- #2868 allow tailing log with `--raw` flag
- #452 Add `PM2_WEB_STRIP_ENV_VARS` to remove environnement vars from `pm2 web` endpoint
- #2890 Fix wait-ready for cluster mode
- #2906 randomize machine name with default pm2 link
- #2888 allow to use regex for pm2 logs
- #2045 allow to rename NODE_APP_INSTANCE env variable
- #2809 add `increment_var` options to ask for a environnement variable to be incremented for each application started
- more informations when failing to deploy on custom ecosystem file
- fix tests for node 8
- fix missing callback when overriding console.log
- allow to rename daemon process name via `PM2_DAEMON_NAME`
- few typo in the readme

### Breaking change

- the NODE_APP_INSTANCE var behavior has been changed :
    - old behavior : when starting multiples instances of an app each one get an unique number, but its not working anymore if you are using `pm2 scale` (simply put its possible to have two application with the same number)
    - new behavior : the number are consistent, if you scale up/down it will take a number that isn't used by another application (so two application should never have the same number)

## 2.4.5/6

- #2818 alias pm2-docker to pm2-runtime
- #2815 polyfill for path.isAbsolute for node v0.11

### Breaking change

- rundev command has been dropped because of too low adoption

## 2.4.4

- #2806 fix reconnection to keymetrics

## 2.4.3

- #2759 disable default require of vxx in pmx
- #2651 always spawn pm2 daemon with `node` binary
- #2745 new issue template
- #2761 Make JSON log stream timestamp in consistent format
- #2770 Fix trigger API never calling callback
- #2796 Fix absolute path on windows
- [KM] profiler installation via `pm2 install v8-profiler` or `pm2 install profiler`
- [KM] Agent rescue system

## 2.4.2

- [KM] Disable pm2-server-monit auto install

## 2.4.1

- #2720 multi user startup script
- #2266 start and tail logs via `pm2 start app.js --attach`
- #2699 add back previous termcaps interface via `pm2 imonit`
- #2681 fix log folder create
- #2724 make sure process is stopped even if there is a restart_delay
- #2706 install pm2 modules via yarn if available
- #2719 show 15 logs line bu default
- #2703 allow custom timestamp with pm2-docker
- #2698 fix unicode on pm2 monit
- #2715 handle treekill edge case bug
- Optimize CPU usage of pm2 monit command
- [KM] URL web access dashboard
- [KM] Auto install pm2-server-monit on keymetrics linking
- [KM] Error reporting: add context (-B3 -A3 code lines)
- [KM] Transaction Tracer: reset routes on app restart / wait some time before sending

## 2.4.0

- #2631 new pm2 monit command (blessed dashboard!)
- #2670 allow to expose a folder over http via `pm2 serve <path> <port>`
- #2617 fix startup script generation on macosx (launchd)
- #2650 new option to append env name to app name (used to allow the same app to be launched in different environement w/o name conflict)
- #2671 allow to pass a delay to pm2-docker (`pm2-docker process.json --delay 10`)
- `pm2 ecosystem simple` to generate a simple ecosystem file
- aliasing: `pm2-dev <script>` <=> `pm2-dev start <script>`
- fix git parsing when using cwd
- #2663 allow to directly output json when logging (via log_type for JSON and --log-type via CLI)
- #2675 fix path when installing language module like typescript
- #2674 increase restart timeout for systemd startup
- #2564 allow to operate process (restart/reload/stop/delete) with regex

## 2.3.0

- Drop Node.js 0.10 support
- (CLI) remove immutability of CLI parameters on restart (critical for ux)
- Keymetrics VXX beta
- Alias "exec" to "script"
- `pm2 logs --nostream` allow to print last logs of application without attaching to logs bus #2620
- Added startup script for gentoo v2.3 via PR #2625
- optionalDependencies from http to https
- remove agent pid on exit
- #2646 check ps.stdout on treekil

## 2.2.3

- Various startup refactor fixes (#2598, #2587, #2590)

## 2.2.2

- #2574 Support Amazon systemv

## 2.2.1 (rc: 2.2.0@next)

- #2559 New startup system. Supported init system: systemd, upstart, launchd

  $ pm2 startup   # Auto detect available init system + Setup init scripts
  $ pm2 unstartup # Disable and Remove init scripts

*SystemD, Upstart and Launchd scripts work like a charm*

- #2515 New way to install PM2 on Debian based system:

```
$ wget -O - http://apt.pm2.io/ubuntu/apt.pm2.io.gpg.key | sudo apt-key add -
$ echo "deb http://apt.pm2.io/ubuntu xenial main" | sudo tee /etc/apt/sources.list.d/pm2.list
$ sudo apt-get update
$ sudo apt-get install pm2
```

- #1090 pm2 resurrect does not respawn the same processes
- #2544 Attach logs to exception
- #2545 Right exit code via pm2 api
- #2543 Fix module pid/mem monitoring
- #2537 Remove duplicated code in Configuration subsystem
- Responsive pm2 list (shortened list when < 90 columns)
- If not TTY do not print ascii table
- #2509 Trigger functions inside Node.js application from the PM2 CLI
- Rename pm2.triggerCustomAction() by pm2.trigger(<app_id>, <action_name>, [params], [cb])

## 2.1.6

- #2509 Trigger functions inside Node.js application from the PM2 CLI
- #2474 Resolve home path in configuration file
- #2526 Expose .launchAll() method to API
- #2351 inner pm2 actions - drop autorestart and node_args options
- #2530 Make sure all processes are killed on system signal to PM2
- #281 allow to combine PM2_SILENT + pm2 jlist to avoid extra data
- Alias attributes error_file to err_file + err_log + err, alias out_file to out, out_log
- Do not ask for pass for set/multiset from KM

## 2.1.5

- #2502 fix SIGTERM signal catch on pm2-docker
- #2498 #2500 global log rotation

## 2.1.4

- #2486 add --web option to pm2-docker command to expose web process api
- #2333 #2478 #1732 #1346 #1311 #1101 Fix GracefulShutdown SIGINT output + Better Stop process flow
- #2353 --wait-ready will wait that the application sends 'ready' event process.send('ready')
- #2425 allow to specify node.js version to be used or installed via interpreter 'node@VERSION'
- #2471 Make app environment immutable on application restart/reload by default for CLI actions
- #2451 Config file can be javascript files
- #2484 fix pm2 kill on windows
- #2101 pm2 ecosystem now generates a javascript configuration file
- #2422 allow to pass none to exec_interpreter
- Faster CLI load time, reduce load time by 1/4 (downgrade cli-table2 -> cli-table)
- Do not use disconnect() anymore on cluster processes
- Better Stop process flow: Upgrade TreeKill system + Wait for check
- Fix deploy issue with Windows
- Expose -i <instances> to pm2-docker
- Drop npm-shrinkwrap
- Upgrade chokidar (fix symlink), cron, fclone, shelljs
- Add yarn.lock

## 2.0.19

- #2466 skip cluster workaround / fix cluster mode for Node.js v7
- Enable Node v7 in travis

## 2.0.16/17/18

- #2400 Create log/pid default folder even if the root folder is already created
- #2395 CRON feature now call PM2 for app to be killed (allow to use SIGINT)
- #2413 #2405 #2406 do not exit on unhandledRejection auto catch
- pidusage upgrade to 1.0.8 to avoid util exception on windows when wmic fail
- Do no display error when pidusage try to monitor an unknow PID (modules)
- pm2-docker binary does not need the start option

## 2.0.15

- process.on('unhandledRejection'): allow to catch promise error that have not been catched
- upgrade fclone and pidusage (faster windows CPU/Mem monitoring)
- allow to call pm2 CLI from bash script managed by pm2
- #2394 fix pm2 id command
- #2385 ts-node upgraded to latest
- #2381 autocompletion fix

## 2.0.12 Bradbury

- Memory usage reduced by 40%
- CPU usage in overall situations reduced by 60%
- Refined pm2 logs command with --json, --format and --raw options
- Faster process management with CONCURRENT_ACTIONs enabled
- Faster installation (v1: ~30secs, v2: ~10secs)
- Faster `pm2 update` with Keymetrics linking delayed at the end
- Much better Module system with raw NPM feedback
- Better Windows support
- **pm2-docker** command with his official [Docker image](https://github.com/keymetrics/pm2-docker-alpine) + json output + auto exit
- **pm2-dev -> pmd** command enhanced (better log output, post-exec cmd)
- Watch and Reload instead of Watch and Restart
- New PM2 API, backward compatible with previous PM2 versions

The new PM2 API is greatly tested and well designed:

```javascript
var PM2 = require('pm2');

// Or instanciate a custom PM2 instance

var pm2 = new PM2.custom({
  pm2_home :    // Default is the legacy $USER/.pm2. Now you can override this value
  cwd      :    // Move to CWD,
  daemon_mode : // Should the process stay attached to this application,
  independant : // Create new random instance available for current session
  secret_key  : // Keymetrics secret key
  public_key  : // Keymetrics public key
  machine_name: // Keymetrics instance name
});

// Start an app
pm2.start('myapp.js');

// Start an app with options
pm2.start({
  script   : 'api.js',
  instances: 4
}, function(err, processes) {
});

// Stop all apps
pm2.stop('all');

// Bus system to detect events
pm2.launchBus((err, bus) => {
  bus.on('log:out', (message) => {
    console.log(message);
  });

  bus.on('log:err', (message) => {
    console.log(message);
  });
});

// Connect to different keymetrics bucket
pm2.interact(opts, cb)

// PM2 auto closes connection if no processing is done but manually:

pm2.disconnect(cb) // Close connection with current pm2 instance
pm2.destroy(cb)    // Close and delete all pm2 related files of this session
```

- Better CLI/API code structure
- PM2 isolation for multi PM2 instance management

### Bug fixes

- #2093 #2092 #2059 #1906 #1758 #1696 replace optional git module with tgz one
- #2077 fix calling pm2.restart inside pm2
- #2261 GRACEFUL_LISTEN_TIMEOUT for app reload configurable via --listen-timeout
- #2256 fix deploy command for yaml files
- #2105 alias pm2 logs with pm2 log
- Extra module display http://pm2.keymetrics.io/docs/advanced/pm2-module-system/#extra-display
- Yamljs + Chokidar Security fixes
- pm2 update / pm2 resurrect is now faster on Node > 4.0
- keymetrics linking after pm2 update is done once all apps are started
- pm2 list processes are now sorted by name instead id
- #2248 livescript support added in development mode
- The client/server file called Satan.js does not exists anymore. It has been replaced by the file combo ./lib/Client.js and ./lib/Daemon.js
- PM2 --no-daemon is better now

### Breaking change

- Coffeescript must be installed via `pm2 install coffeescript`

## 1.1.3

- Node v6 compatibility

## 1.1.2

- [#2071 #2075] Fix pm2-dev command

## 1.1.0: Galactica release

This release is about PM2's internals refactoring, homogenization in action commands (in terms of behavior and outputs).
Some interesting features has been added, as YAML file support (for application declaration) and some syntaxic sugar.
The Keymetrics interface has been enhanced, dividing by two the memory usage and avoiding any possible leak in any potential scenarios. Reconnection system has been refactored too, we kindly ask our Keymetrics users to upgrade to this version ASAP.

**This version has been heavily tested in testing, production environments and deeply monitored in terms of CPU and Memory usage.**

- [#133 #1568] Allow to rename a process via pm2 restart app --name "new-name"
- [#2002 #1921 #1366] Fix CLI/JSON arguments update on restart (args, node_args, name, max-memory)
- [#578] Add YAML support for application configuration file (in extent to JSON and JSON5 support)
- [Keymetrics agent refactoring] TCP wait, memory consumption divided by two, reconnection refactoring, keep alive ping system
- [Keymetrics agent refactoring] Fix random no response from pm2 link and pm2 unlink
- [#2061] Kill ESRCH of processes in cluster mode with SIGINT catcher fixed
- [#2012 #1650 #1743] CLI/JSON arguments update on reload
- [#1613] Reload all reload ALL applications (stopped, errored...)
- [#1961] Fix kill timeout info log
- [#1987] Fix FreeBSD startup script
- [#2011] Respect process.stdout/.stderr signature
- [#1602] Fix zombie process when using babel-node as interpreter
- [#1283] --skip-env option to not merge update with system env
- Homogeneize actions commands outputs
- Option --interpreter-args added (alias of node-args)
- Allow to use exactly the same option in JSON declaration and CLI (e.g. interpreter) to avoid confusion
- pm2 show, now shows more commands to manage processes
- Refactor programmatic system

## 1.0.2

- [#1035 #1055] Deactivate automatic dump on startup scripts
- [#1980] Add Javascript source map resolution when exceptions occurs [Documentation](http://pm2.keymetrics.io/docs/usage/source-map-support/)
- [#1937] Allow to act on application having numerics as app name
- [#1945] Fix post_update commands section when file contains Javascript
- [#624] --only <app-name> to act only on specified app name in json app declaration
- [0.6.1](https://github.com/keymetrics/pmx/releases/tag/0.6.1) PMX upgrade

## 1.0.1

- [#1895] pm2 id <app_name>: output array of ids for app_name @soyuka
- [#1800] pm2 show <app_name>: now also display node.js version @soyuka

## 1.0.0

- [#1844][#1845][#1850] Load configuration in /etc/default/pm2 + add ulimit -n override
- [#1810] Add --kill-timeout <number> option (delay before process receive a final SIGKILL)
- [#1830] Add tests for PM2_KILL_TIMEOUT (SIGKILL delay) + default SIGINT to any kind of procs
- [#1825] Process management commands (start/restart/stop/delete) can take multiple arguments
- [#1822] Add new method pm2.sendDataToProcessId(type|data|id) to send data to processes
- [#1819] Send SIGINT signal to process instead of SIGTERM
- [#1819][#1794][#1765] Avoid writing on std err/out when process is disconnected

- Add default attribute in schema.json to allow to configure default value when passing a JSON
- JSON and CLI starts are now consistent in terms of option size, attribute number
- pm2.restart(json_data, function(err, data) now returns an array of process instead of simple object (success:true))
- Now pm2 restart process.json --env <X>, refresh environment variable on each restart depending of the X environment
- prepareJSON method in PM2 code (God.js) removed
- partition Common.prepareAppConf (duplicate with verifyConfs)
- Change signature of Common.prepareAppConf
- Centralize Interpreter resolution via Common.sink.resolveInterpreter(app) in Common.js

- Better meta information when process restart/reload/stop (signal + exit code)
- Upgrade pm2-axon, cron, should, mocha, coffee-script, chokidar, semver NPM packages
- Show process configuration option when describing process
- Add --no-automation flag
- Fix when starting application with illegal names (#1764)
- Fix management of app starting with numerics in the filename (#1769)
- Fix versiong system (reset to default on resurrect/prepare)
- Increase buffer size for versioning meta parsing

## 0.15.10

- Hot fix #1746

## 0.15.9

- Chokidar upgraded to 1.2
- Fix startup script via new --hp option
- Fix JSON refresh system

## 0.15.1-8

- JSON refresh available
- New module system backward compatible and compatible with NPM 3.x
- Possibility to install module from tgz (#1713)
- ecosystem generated file via pm2 generate uptaded (not json5 prefix anymore, and updated comments)
- always prefix logs #1695
- blessed dependency removed
- drop locking system
- add callback to deploy (#1673)
- typo fixes
- pm2.update added
- small db for pm2 modules added (solve npm 3.x issue)
- pm2 multiset "k1 v1 k2 v2 k3 v3"
- babel dependency removed
- blessed dependency removed
- chalk, safe-clone-deep, shelljs, semver upgraded
- New command: pm2 module:update <module_name> -> Update a module
- New command: pm2 module:publish  -> Publish module in current folder + Git push
- New command: pm2 module:generate [module name] -> Generate a sample module
- Feature: configuration system for raw Node.js applications
- alias pm2 install with pm2 i
- JSON declaration: You can now use process.env in application declaration file
- watch has been refactored for windows and tests
- allow installation of specific module version
- wrap final process kill intro try catch (c4aecc8)
- Appveyor to test PM2 under Windows added (+ fix some incorect file name)
- Allow to escape key name when using pm2 conf system

## 0.14.7

- New flag `--no-pmx` : starts an app without injecting pmx
- New feature : cron restart now works in fork mode as well
- Disabled auto-gc on interactor
- Allow PM2 to execute binaries in $PATH
- pm2 link priv pub --recyle for elastic infrastructure
- pm2 deploy now check default file ecosystem.js[on|on5], package.json

## 0.14.6

- Scoped PM2 actions
- Password encryption via pm2 set pm2:passwd xxxx
- Interactor Remote action refactor
- .getSync method to get configuration variable synchronously
- Add password protected PM2 methods (install, delete)
- pm2 get|pm2 conf display all confs
- Password protected PM2 flag
- New flag : `--restart-delay <ms>` (or `restart_delay` in JSON declaration)
- New command : `pm2 deepUpdate`
- New command (beta) : `pm2 logrotate`
- Enhancement : pm2 handles processes that can't be killed in a better way
- Fix : some ignore_watch issues
- Fix : some pm2 startup systemd issues

## 0.14.5

- Hot fix

## 0.14.4

- New command : `pm2 iprobe [app_name|app_id|'ALL']`
- Feature: FreeBSD startup script
- Fix: Remove forced GC
- Fix: ##1444 --next-gen-js in fork mode
- Fix: Windows path fix

## 0.14.3 (Current Stable)

- `pm2 flush` now flushes pm2.log as well
- New flag : `--no-treekill` : when used PM2 won't kill children processes
- New flags : `pm2 logs ['all'|'PM2'|app_name|app_id] [--err|--out] [--lines <n>] [--raw] [--timestamp [format]]`
- Enhancement: Modules installable via Github: `pm2 install username/repository`
- Feature: PMX has *scoped function* -> pm2 stores temporary output from custom functions
- Fix: Interactor issue when doing an heapdump
- Feature: PM2 CLI autocompletion

## 0.14.2

- Improved pm2-dev
- Now when apps list is empty, the `id` counter is set to 0
- Removed pres/keymetrics.js post-install script
- Fix : `pm2 logs` allocation error
- Fix : `pm2 prettylist|jlist` truncated output

## 0.14.0 - CrystalClear (pre 1.0)

- Removed: pm2.startJSON() method, now call pm2.start()
- API Change: pm2 start <app_name|app_id> restart an application already launched
- API Change: pm2 start <json> restart all json apps if already launched
- pm2 start all - restart all applications
- pm2 reload <json_file> possible
- pm2 gracefulReload <json_file> possible
- Smart start (pm2 start app.js ; pm2 stop app ; pm2 start app)
- Reduced memory footprint
- Reduced pipelined data
- Reduced CPU usage
- Faster command processing
- Upgrade shelljs, semver, colors, chalk, coffee-script, async, json-stringify-safe, cron, debug, commander
- Fix: launchBus() only connects and disconnects once

- Refactored `pm2 logs` :
  - Now you don't need to install tail on Windows
  - You don't need to Ctrl^C and `pm2 logs` again when a new app is launched (this one will be detected and added to the real-time logs output)
  - Logs are shown in chronological order at a file level (modified date)
  - More verbosity : tailed logs are explicitely separated from the real-time logs
  - Real-time logs now use the `bus` event emitter
  - PM2 logs added to the `bus`
  - `--lines <n>` and `--raw` flags available for `pm2 logs` command
  - New flag : '--timestamp [format]' // default format is 'YYYY-MM-DD-HH:mm:ss'
  - Now you can exclusively show PM2 logs by doing `pm2 logs PM2`

## 0.12.16

- Feature : File transmission added in Agent
- Feature : Transmit Node.js/io.js version in Agent
- Feature : Parameters can be passed to remote actions
- Feature : Support JS in addition to JSON and JSON5 config files #1298
- Enhanced: pm2 conf display all configuration values
- Enhanced: pm2-dev
- Enhanced: Better error messages when validating data passed via CLI
- Enhanced: Smaller memory footprint for PM2 (~30%)
- Fix #1285 : PID file was deleted after a reload/gracefulReload
- Fix : ENOMEM made PM2 crash

## 0.12.15

- Fix #941 : Env variables overrided when an app is restarted
- max_memory_restart now performs a graceful reload
- `pm2 logs --raw` now shows 20 last lines of each log file
- pm2-dev run app.js : start an app in dev mode (--no-daemon --watch and stream logs of all launched apps)
- --no-daemon command now display logs of all processes (Docker)

## 0.12.14

- `ilogs` is no longer part of PM2
- Improved interaction with Keymetrics
- BabelJS is now integrated into PM2 (`--next-gen-js` flag)

## 0.12.13

- Enhanced  : PM2 doesn't leave processes behind when it crashes
- Enhanced  : Call reload instead of restart when max-memory-limit reached
- Enhanced  : Modules are compatible ES6 by default by adding --harmony flag
- Enhanced  : Dump feature is now smarter
- Fix #1206 : fix `pm2 logs` bug when merged_logs
- Fix       : pm2 scale doesn't try to scale a fork_mode process

## 0.12.12

- `pm2 logs --raw` flag : show logs in raw format
- New command: pm2 scale <app_name> <number> - scale up/down an application
- Fix #1177 : no concurrent vizion.parse() for the same process event when it restarts
- Added: Expose kill method programmatically
- Added: Call disconnect without a function
- Added: Programmatic call to .connect can now take no-daemon-option
- Fixed: starting a JSON programmatically return a process list coming from God
- Fixed: Reflect dump functions from CLI and God
- Enhanced: New CLI API for configuring modules (pm2 conf module.option [value])
- Added: Using Keymetrics harden PM2 by enabling a WatchDog that auto restart PM2 in case of crash
- Added: Expose pm2 gc programmatically
- Added: pm2 install <module_name> update the module
- Enhanced: 4 new test suits for PM2 programmatics call
- Enhanced: Documentation restructured

## 0.12.11

- `--no-autorestart` flag : starts an app without automatic restart feature
(`"autorestart" : false` in JSON declaration)

- `--no-vizion` flag : starts an app completely without vizion features
(`"vizion" : false` in JSON declaration)

- Fix #1146 : add module._initPaths() on ProcessContainer.js so it forces each
new process to take the current NODE_PATH env value in account

- New: pm2.start() now handles json objects as param

- Added: timestamps to KM agent logs

- Fix: now properly closes all fds after logging has finished.

- New command: pm2 gc (manually triggers garbage collection for PM2)

- VersioningManagment: exec() timeout configurable via .json

- Fix #1143 :
If we start let's say 4 instances of an app (cluster_mode),
Each app will have a value in process.env.NODE_APP_INSTANCE which will be 0 for the first one,
1, 2 and 3 for the next ones.

- Fix #1154 :
Negative arguments to '-i' are substracted to CPU cores number.
E.g: 'pm2 start app.js -i -3' in a 8 cpus environment will start 5 instances (8 - 3).

## 0.12.10

- Fix : PM2 interactor doesn't send data about dead processes ('_old_') anymore.
- Fix #1137 : Safe params for 'pm2 list' so cli-table won't fail
- Refactored reverse interaction with keymetrics for better stability and more verbosity on Rollback/Pull/Upgrade operations

## 0.12.9

- Fix #1124 : PM2_PROGRAMMATIC flag wasn't handled properly
- Fix #1121 : NODE_PATH before PATH so custom node versions come first
- Fix #1119 : Safe params so cli-table won't fail
- Fix #1099 : Bug when app name starts by digit (e.g '1-myApp')
- Fix #1111 : More verbosity on writeFileSync errors
- New env setting: PM2_KILL_TIMEOUT (ms) : time to wait before a process is considered dead
- New env setting: PM2_CONCURRENT_ACTIONS : use it with care, value bigger than 1 is considered unstable
- Refactored reload/gracefulReload for better stability

## 0.12.8

- Fix : `Channel closed error`
- Fix : `Resource leak error`
- Fix#1091 : when passing a wrong formated number to `-i` infinite loop
- Fix #1068 #1096 : restart fails after reloadLogs()
- New : When PM2 is being killed, all restarts are blocked to avoid conflict
- New : PM2 dumps the process list before exiting if it is killed by signal
- Refactored stop/restart for better stability

## 0.12.7

- pm2 logs : Now shows merged logs
- Fix #929 #1043 : Bug pm2 stop/restart not working properly
- Fix #1039 : Better algorithm for vision recursive parsing to avoid infinite loops
- Automatize #858 #905: Directly init pm2 folder if not present when using it programmatically
- Add Bus system from PM2 programmatic API

## 0.12.6

- Enhancement of startJson command (force_name and additional_env options)
- Fix #990 : pm2 flush while pm2 logs was open bug
- Fix #1002 : pm2 monit bug
- Fix #1024 : enhancement
- Fix #1011 : json-stringify-safe bug
- Fix #1007 ##1028 #1013 #1009 : pm2 desc bug
- Fix : pm2 interact delete when file doesn't exist bug

## 0.12.5

- Windows support

## 0.12.4

- Never start a process that already has a PID [#938]
- 1. Make platform auto detecting. 2. Support darwin startup script. [#936]
- Fix #857 #935, add scriptArgs back [d61d710]
- Fix broken link upstart [f8ff296]
- Fixed: multiple calls to vizion.parse() for the same process [0e798b1]
- fix 2015 test easter egg - Happy New Year! [85d11d5]
- fixes #906 [#911]
- Add back automatic coffee interpreter #488 #901 [e9a69fe]
- Upgrade cli-table, commander, colors, moment dependencies [0cc58ce][a4b7d8d]
- Domain system to patch fix the exception thrown by the cluster module
- Fix #830 #249 #954 when there is no HOME env to default to /etc/.pm2 [17d022c]

## 0.12.3

- fixed critical bug: `process.env` flattens all env-vars [#898]
- npm maintainers format [#894]
- fix `pm2 desc` crash bug [#892]
- fix CLI typo [#888]
- `port` config [#885]

## 0.12.2

- treeKill copyright and update [#848] [#849]
- Allow environment variables per each ecosystem deploy [#847]
- max-memory-restart option [#697] [#141]
- JSON validation (cf ADVANCED_README.md) [#768] [#838]
- CLI/JSON refactoring
- watch fixes
- execute binary softwares
- node_args refactored (ESC support) [#838]
- reload env graceful and peaceful [#838]
- min_uptime added [#838]
- startOrRestart conf.json does update environment variables [#805]
- vizion only refresh ahead and unstaged flags [f1f829c]
- worker restart cluster process if it's equal to 0 && online [c2e3581]
- pm2 pull <name> [commit_id] [c2e3581] [4021902]
- fix reloadLogs for fork mode [c0143cc][197781e]
- waterfall logs stream [#822]
- --log option to have a merged error and out output [#822]
- God core refactors
- test refactoring
- update isBinaryFile [636fd99]
- pid deletion has been resurected [f2ce631]
- worker refactor [29fc72b]
- fix no color [3feead2]
- upgrade chokidar 0.12 with follow symlink [4ac0e74]
- refactor Reload [cf94517][f1eb17]
- avoid truncate with pm2 logs command [26aff8b]
- God print log with timestamp via PM2_LOG_DATE_FORMAT [bf2bf8a][3eaed07]
- better test suit
- new treekill system [11fe5f4]

Big thanks to @Tjatse !

## 0.12.1

- Harden Lock system
- Fix Worker bug / Refactor Worker
- Cleanly close interactor sockets on end
- Add backward compatibility for older PM2 on kill action via system signal SIGQUIT
- once listener for killDaemon

## 0.12.0 - clear water ops

- better ecosystem.json5 file with embedded comments
- startOrRestart conf.json update environment variables #805 #812
- pm2 start my/bin/file work out of the box
- JSON5 support
- PM2_HOME supported - PM2 files paths relocation (logs, pid) via PM2_HOME option
- post_updates commands are searched in process.json/ecosystem.json/package.json
- Worker system to verify up to date repositories
- Rename process running with PM2 <version> - app_name
- Process Lock system
- Inner iteraction with PM2 possible #782
- Better vizion system
- backward / forward / pull command
- Doc moved to doc
- remove uidnumber module
- pre install / post install scripts removed
- Remote Lock System
- More God tests
- GRACEFUL_LISTEN_TIMEOUT constant configurable
- Logs are closed in Fork mode when reloading
- Fix not tty
- Fix cluster structure nullification
- Pre Windows Support
- Send revision process on each process event
- Upgrade Commander (better help display)
- Upgrade chokidar to 0.10.x
- Better interactor
- Better revision parsing
- Configuration file
- Close fd in fork mode while reloading
- Remove --run-as-user option
- Better CLI interface for interactor
- axm:monitor axm:dynamic
- Temporaly merge pm2-interface with pm2
- Cache cpu infos
- Make revision transit in God.bus broadcast
- Ignore useless events in God.bus broadcast

## 0.11.0-1

- Multi user support and privilege containment: UNIX sockets instead of TCP
- Reload refactoring
- Process on uncaughtexcption to flush process list
- pm2 logs display state change of processes

## 0.10.x

- multi host for pm2 deploy
- fork mode by default
- fix watch on clusters
- refactor watch
- env option via programmatic interface
- fix watch system
- correct pm2 describe command
- close file used via pm2 flush
- add startOrReload
- better closing events

## 0.10.0 - PM2 Hellfire release

- PM2 hearth code has been refactored and now it handles extreme scenario without any leak or bug
- PM2 restart <json|id|name|all> refresh current environment variables #528
- PM2 delete all more verbose
- PM2 reset <all|id|name> reset restart numbers
- Auto update script at PM2 installation
- --watch enhanced to avoid zombie processes
- Restart app when reaching a limit of memory by using --max-memory-restart (and max_memory_restart via JSON)(https://github.com/Unitech/pm2#max-memory-restart)
- PM2 respects strong unix standard process management
- Remove timestamps by default with pm2 logs
- Coffeescript not enabled by default anymore (enhance memory usage)
- PM2 Programmatic interface enhanced
- PM2 hearth refactor
- PM2 describe show node-args
- node_args for V8 options is now available via JSON declaration
- Watch system avoid ghost processes
- Memory leak fixes
- Better performance on interface
- Fix tests
- Enable PM2_NODE_OPTIONS and node-args for fork mode
- Dependencies updated
- Faster monitoring system
- AXM actions unification
- Socket errors handled
- Watchdog via Agent - restart automatically PM2 with previous processes in case of crash
- PM2_NODE_OPTIONS deprecation (use --node-args instead)

## 0.9.6 - 0.9.5 - 0.9.4

- Bash test auto exit when failure
- Bump fix log streaming
- Bump fix to display old logs streaming by default
- Bump fix

## 0.9.3

- Critical bug on fork mode fixed (stream close)
- Advanced log display interface pm2-logs #589
- Simple log timestamp via --log-date-format (with momentJS formating) #183
- Possible to pass arguments via scriptArg with programmatic PM2 #591
- Gentoo startup script generation #592
- Fix run-as-user and run-as-group in fork mode #582
- Documentation update

## 0.9.2

- max_restart enabled
- sudo fix for init scripts
- some startup refactoring
- Possibility to specify the configuration folder for PM2 via process.env.PM2_HOME
- Fix date format
- N/A for undefined date
- Evented interactions with PM2, available via pm2-interface
- Deep Interactor refactoring
- Force reload for upstart script

## 0.9.0-0.9.1

- CLI flattening
- require('pm2') possible to interact with
- deployment system
- Remove builtin monitoring feature
- Fix watch on delete #514
- Gracefull reload now rightly handled #502
- Allow path in watch option #501
- Allow management of non-interpreted binaries #499
- Documentation fixes

## 0.8.12-0.8.15

- Version bumping

## 0.8.12

- Fix CWD option #295

## 0.8.10-0.8.11

- Builtin monitoring feature with email (with pm2 subscribe)
- Reload Logs for Fork
- Deletion of possible circular dependencies error
- pm2 updatePM2 command to update in-memory pm2
- notification message if the in-memory pm2 is outdated
- cwd option in json #405 #417 #295
- README updates
- ipc channel for fork mode
- re enable process event loggin for interactor
- avoid possible stream error
- watch ignore option in JSON

## 0.8.5-6

- Update monitoring module

## 0.8.4

- Remove C++ binding for monitoring
- Update axon and axon-rpc

## 0.8.2

- Adds option to switch to a different user/group before starting a managed process #329
- watch doesnt watch node_module folder
- default log files and pid files location can be overrided by PM2_LOG_DIR / PM2_PID_DIR

## 0.8.1

- Readme changes #400 #398
- Fix describe command #403
- reload/gracefulReload throw error if no process has been reloaded #340

## 0.8.0

- More verbosity to pm2.log
- Fast Watch & Reload
- New README.md
- --merge-logs option to merge logs for a group of process
- logs reload with SIGUSR2 or `pm2 reloadLogs`
- return failure code when no process has been reloaded
- Upgrade of outdated packages
- Silent (-s) flag remove all possible pm2 output to CLI
- New display for list, more compact
- `pm2 describe <id>` to get more details about a process
- Fixed 0.10.x issue when stop/kill
- Helper shown when -h
- Linter errors
- Systemd support for Fedora / ArchLinux
- #381 Add support for Amazon Linux startup script
- Fixed rendering
- Interaction possible with VitalSigns.io
- Avoid exception when dump file is not present

## 0.7.8

- List processes with user right `service pm2-init.sh status`

## 0.7.7

- Bug fixes, stability fixes

## 0.7.2

- harmony can be enabled [Enabling harmony](#a66)
- can pass any options to node via PM2_NODE_OPTIONS, configurable via ~/.pm2/custom_options.sh
- pid file written in ~/.pm2/pm2.pid
- startup script support for CentOS
- --no-daemon option (Alex Kocharin)
- json file now can be : started/stoped/restarted/deleted
- coffeescript support for new versions (Hao-kang Den)
- accept JSON via pipe from standard input (Ville Walveranta)
- adjusting logical when process got an uncaughtException (Ethanz)

### Update from 0.x -> 0.7.2

- CentOS crontab option should not be used anymore and use the new init script with `pm2 startup centos`
- If you use the configuration file or the harmonoy option, you should regenerate the init script

## 0.7.1

- Integrates hardened reload, graceful reload and strengthened process management

## 0.7.0

- Reload works at 100%
- Logs are now separated by process id
- Minimal listing with -m option
- pid files are deleted once process exit
- ping method to launch or knwo if pm2 is alive
- more tests
- coffeescript is supported in cluster mode
- clean exit
- clean process stopping
- speed process management enhanced
- async used instead of recuresive loops
- broad test for node 0.11.10 0.11.9 0.11.8 0.11.7 0.11.5 0.10.24 0.10.23 0.10.22 0.10.21 0.10.20 0.10.19 0.10.18 0.10.17 0.10.16 0.10.15 0.10.14 0.10.13 0.10.12 0.10.11 0.8

## 0.6.8

- Homogeneize JSON #186
- Auto intepreter selection (you can do pm2 start app.php)

## 0.5.6

- Coffeescript support
- Updating dependencies - axon - commander
- Log feature enhanced - duplicates removed - name or id can be passed to pm2 logs xxx

## 0.5.5

- Ability to set a name to a launched script + tests
    - with the --name option when launching file
    - with the "name" parameter for JSON files
- Ability to restart a script by name + tests
- Upgrade node-usage to 0.3.8 - fix monitoring feedback for MacOSx
- require.main now require the right file (activate it by modifying MODIFY_REQUIRE in constants.js)
- CentOS startup script with pm2 startup centos
- 0 downtime reload

## 0.5.4

- Remove unused variable in startup script
- Add options min_uptime max_restarts when configuring an app with JSON
- Remove pid file on process exit
- Command stopAll -> stop all | restartAll -> restart all (backward compatible with older versions)

## 0.5.0

- Hardening tests
- Cron mode to restart a script
- Arguments fully supported
- MacOSx monitoring possible


module.exports = {
  /**
   * Application configuration section
   * http://pm2.keymetrics.io/docs/usage/application-declaration/
   */
  apps : [

    // First application
    {
      name      : "API",
      script    : "app.js",
      env: {
        COMMON_VARIABLE: "true"
      },
      env_production : {
        NODE_ENV: "production"
      }
    }
  ],

  /**
   * Deployment section
   * http://pm2.keymetrics.io/docs/usage/deployment/
   */
  deploy : {
    production : {
      user : "node",
      host : "212.83.163.1",
      ref  : "origin/master",
      repo : "git@github.com:repo.git",
      path : "/var/www/production",
      "post-deploy" : "npm install && pm2 startOrRestart ecosystem.config.js --env production"
    },
    dev : {
      user : "node",
      host : "212.83.163.1",
      ref  : "origin/master",
      repo : "git@github.com:repo.git",
      path : "/var/www/development",
      "post-deploy" : "npm install && pm2 startOrRestart ecosystem.config.js --env dev",
      env  : {
        NODE_ENV: "dev"
      }
    }
  }
};


var pmx = require('..');

function forkApp(script) {
  var app = require('child_process').fork(__dirname + (script || '/proc.mock.js'), []);
  return app;
}

function forkAppWithOptions() {
  var app = require('child_process').fork(__dirname + '/proc-option.mock.js', []);
  return app;
}

describe('Action module', function() {

  describe('Action without option', function() {
    var app;
    var action_name;

    after(function() {
      process.kill(app.pid);
    });

    it('should notify PM2 of a new action available', function(done) {
      app = forkApp();

      app.once('message', function(dt) {
        dt.type.should.eql('axm:action');
        dt.data.action_name.should.eql('test:nab');
        dt.data.opts.comment.should.eql('This is a test');
        dt.data.opts.display.should.eql(true);

        action_name = dt.data.action_name;

        done();
      });
    });

    it('should trigger the action', function(done) {
      app.once('message', function(dt) {
        dt.type.should.eql('axm:reply');
        dt.data.return.res.should.eql('hello moto');
        done();
      });

      app.send(action_name);
    });

    it('should trigger the action via Object arity 1 (FALLBACK)', function(done) {
      app.once('message', function(dt) {
        dt.type.should.eql('axm:reply');
        dt.data.return.res.should.eql('hello moto');
        done();
      });

      app.send({msg : action_name, opts : { sisi : 'true' }});
    });

    it('should not trigger the action if wrong action name', function(done) {
      app.once('message', function(dt) {
        throw new Error('Should not be called');
      });

      app.send({
        action_name : 'im unknown'
      });

      setTimeout(done, 200);
    });
  });

  describe('Action with extra options (parameters)', function() {
    var app;
    var action_name;

    after(function() {
      process.kill(app.pid);
    });

    it('should notify PM2 of a new action available', function(done) {
      app = forkAppWithOptions();

      app.once('message', function(dt) {
        dt.type.should.eql('axm:action');
        dt.data.action_name.should.eql('test:with:options');
        action_name = dt.data.action_name;
        done();
      });
    });

    it('should trigger the action without failing (2 args without option)', function(done) {
      app.once('message', function(dt) {
        dt.type.should.eql('axm:reply');
        dt.data.return.res.should.eql('hello moto');
        done();
      });

      app.send(action_name);
    });

    it('should trigger the action', function(done) {
      app.once('message', function(dt) {
        dt.type.should.eql('axm:reply');
        dt.data.return.res.should.eql('hello moto');
        dt.data.return.options.f1.should.eql('ab');
        done();
      });

      app.send({ msg : action_name, opts : { f1 : 'ab', f2 : 'cd'}});
    });

    it('should not trigger the action if wrong action name', function(done) {
      app.once('message', function(dt) {
        throw new Error('Should not be called');
      });

      app.send('im unknown');

      setTimeout(done, 200);
    });

  });

  describe('Scoped Action (option, emitter, callback)', function() {
    var app;
    var action_name;

    after(function() {
      process.kill(app.pid);
    });

    it('should notify PM2 of a new action available', function(done) {
      app = forkApp('/fixtures/scoped-action.fixture.js');

      app.once('message', function(dt) {
        dt.type.should.eql('axm:action');
        dt.data.action_name.should.eql('scoped:action');
        dt.data.action_type.should.eql('scoped');
        action_name = dt.data.action_name;
        done();
      });
    });

    it('should stream data', function(done) {
      app.once('message', function(dt) {
        dt.type.should.eql('axm:scoped_action:stream');
        dt.data.data.should.eql('data random');
        done();
      });

      app.send({ action_name : action_name, uuid : 'Random nb'});
    });

    it('should trigger the action', function(done) {
      app.on('message', function(dt) {
        if (dt.type == 'axm:scoped_action:end')
          done();
      });
    });

  });

});


if (module_name.indexOf('typescript') > -1) {

/**
 * Copyright 2013 the PM2 project authors. All rights reserved.
 * Use of this source code is governed by a license that
 * can be found in the LICENSE file.
 */
var shelljs       = require('shelljs');
var path          = require('path');
var fs            = require('fs');
var async         = require('async');
var p             = path;
var readline      = require('readline');
var spawn         = require('child_process').spawn;
var chalk         = require('chalk');
var Configuration = require('../../Configuration.js');
var cst           = require('../../../constants.js');
var Common        = require('../../Common');
var UX            = require('../CliUx.js');
var Utility       = require('../../Utility.js');
var semver        = require('semver');

var Modularizer = module.exports = {};

var MODULE_CONF_PREFIX = 'module-db';

function startModule(CLI, opts, cb) {
  /** SCRIPT
   * Open file and make the script detection smart
   */

  if (!opts.cmd) throw new Error('module package.json not defined');
  if (!opts.development_mode) opts.development_mode = false;

  var package_json = require(opts.cmd);

  /**
   * Script file detection
   * 1- *apps* field (default pm2 json configuration)
   * 2- *bin* field
   * 3- *main* field
   */
  if (!package_json.apps) {
    package_json.apps = {};

    if (package_json.bin) {
      var bin = Object.keys(package_json.bin)[0];

      package_json.apps.script = package_json.bin[bin];
    }
    else if (package_json.main) {
      package_json.apps.script = package_json.main;
    }
  }

  /**
   * Verify that the module is valid
   * If not, delete
   */
  if (isValidModule(package_json) === false) {
    if (!opts.development_mode) shelljs.rm('-rf', opts.proc_pabth);
    Common.printError(cst.PREFIX_MSG_MOD + 'Module uninstalled');
    return cb(new Error('Invalid module (script is missing)'));
  }

  Common.extend(opts, {
    cwd               : opts.proc_path,
    watch             : opts.development_mode,
    force_name        : package_json.name,
    started_as_module : true
  });

  // Start the module
  CLI.start(package_json, opts, function(err, data) {
    if (err) return cb(err);
    return cb(null, data);
  });
};

function installModule(CLI, module_name, opts, cb) {
  var proc_path = '',
      cmd  = '',
      conf = {},
      development_mode = false;

  if (typeof(opts) == 'function') {
    cb = opts;
    opts = {};
  }

  if (module_name == '.') {
    /**
     * Development mode
     */
    Common.printOut(cst.PREFIX_MSG_MOD + 'Installing local module in DEVELOPMENT MODE with WATCH auto restart');
    development_mode = true;
    proc_path = process.cwd();

    cmd = p.join(proc_path, cst.DEFAULT_MODULE_JSON);

    Common.extend(opts, {
      cmd : cmd,
      development_mode : development_mode,
      proc_path : proc_path
    });

    startModule(CLI, opts, function(err, dt) {
      if (err) return cb(err);
      Common.printOut(cst.PREFIX_MSG_MOD + 'Module successfully installed and launched');
      return cb(null, dt);
    });
  }
  else {
    /**
     * Production mode
     */
    var cli = {};

    if (semver.satisfies(process.version, '> 0.12') && shelljs.which('yarn')) {
      cli = {
        bin : 'yarn',
        cmd : 'add'
      }
    }
    else {
      cli = {
        bin : 'npm',
        cmd : 'install'
      }
    }

    Common.printOut(cst.PREFIX_MSG_MOD + 'Calling ' + chalk.bold.red('[' + cli.bin.toUpperCase() + ']') + ' to install ' + module_name + ' ...');

    var install_instance = spawn(cst.IS_WINDOWS ? cli.bin + '.cmd' : cli.bin, [cli.cmd, module_name, '--loglevel=error'], {
      stdio : 'inherit',
      env: process.env,
			shell : true,
      cwd : cst.PM2_ROOT_PATH
    });

    install_instance.on('close', finalize);

    install_instance.on('error', function (err) {
      console.error(err.stack || err);
    });

    function finalize(code) {
      if (code != 0) {
        return cb(new Error("Installation failed"));
      }

      Common.printOut(cst.PREFIX_MSG_MOD + 'Module downloaded');

      var canonic_module_name = Utility.getCanonicModuleName(module_name);

      proc_path = p.join(cst.PM2_ROOT_PATH, 'node_modules', canonic_module_name);

      cmd = p.join(proc_path, cst.DEFAULT_MODULE_JSON);

      /**
       * Append default configuration to module configuration
       */
      try {
        var conf = JSON.parse(fs.readFileSync(path.join(proc_path, 'package.json')).toString()).config;
        if (conf) {
          Object.keys(conf).forEach(function(key) {
            Configuration.setSyncIfNotExist(canonic_module_name + ':' + key, conf[key]);
          });
        }
      } catch(e) {
        Common.printError(e);
      }

      Common.extend(opts, {
        cmd : cmd,
        development_mode : development_mode,
        proc_path : proc_path
      });

      Configuration.set(MODULE_CONF_PREFIX + ':' + canonic_module_name, {
        uid : opts.uid || null,
        gid : opts.gid || null
      }, function(err, data) {

        startModule(CLI, opts, function(err, dt) {
          if (err) return cb(err);

          if (process.env.PM2_PROGRAMMATIC === 'true')
            return cb(null, dt);

          CLI.conf(canonic_module_name, function() {
            Common.printOut(cst.PREFIX_MSG_MOD + 'Module successfully installed and launched');
            Common.printOut(cst.PREFIX_MSG_MOD + 'Edit configuration via: `pm2 conf`');
            return cb(null, dt);
          });
        });
      });
    }
  }
}

function uninstallModule(CLI, module_name, cb) {
  var proc_path = p.join(cst.PM2_ROOT_PATH, 'node_modules', module_name);

  Configuration.unsetSync(MODULE_CONF_PREFIX + ':' + module_name);

  CLI.deleteModule(module_name, function(err, data) {
    if (err) {
      Common.printError(err);

      if (module_name != '.') {
        console.log(proc_path);
        shelljs.rm('-r', proc_path);
      }

      return cb(err);
    }

    if (module_name != '.') {
      shelljs.rm('-r', proc_path);
    }

    return cb(null, data);
  });
}

function installLangModule(module_name, cb) {
  var node_module_path = path.resolve(path.join(__dirname, '../../../'));

  var cli = semver.satisfies(process.version, '> 0.12') && shelljs.which('yarn') ? 'yarn' : 'npm'
  Common.printOut(cst.PREFIX_MSG_MOD + 'Calling ' + chalk.bold.red('[' + cli.toUpperCase() + ']') + ' to install ' + module_name + ' ...');

  var install_instance = spawn(cst.IS_WINDOWS ? 'npm.cmd' : 'npm', ['install', module_name, '--loglevel=error'], {
    stdio : 'inherit',
    env: process.env,
		shell : true,
    cwd : node_module_path
  });

  install_instance.on('close', function done() {
    cb(null);
  });

  install_instance.on('error', function (err) {
    console.error(err.stack || err);
  });
};

/**
 * List modules on the old way
 */
var listModules = function() {
  var module_folder = p.join(cst.PM2_ROOT_PATH, 'node_modules');
  var ret = {};

  shelljs.config.silent = true;
  var modules = shelljs.ls(module_folder);
  shelljs.config.silent = false;

  modules.forEach(function(module_name) {
    if (module_name.indexOf('pm2-') > -1)
      ret[module_name] = {};
  });

  return ret;
};

/**
 * List modules based on internal database
 */
var listModulesV2 = Modularizer.listModules = function() {
  var config = Configuration.getSync(MODULE_CONF_PREFIX);

  if (!config) {
    var modules_already_installed = listModules();

    Object.keys(modules_already_installed).forEach(function(module_name) {
      Configuration.setSync(MODULE_CONF_PREFIX + ':' + module_name, {});
    });
    return modules_already_installed;
  }

  return config;
};

Modularizer.getAdditionalConf = function(app_name) {
  if (!app_name) throw new Error('No app_name defined');

  var module_conf = Configuration.getAllSync();

  var additional_env = {};

  if (!module_conf[app_name]) {
    additional_env = {};
    additional_env[app_name] = {};
  }
  else {
    additional_env = Common.clone(module_conf[app_name]);
    additional_env[app_name] = JSON.stringify(module_conf[app_name]);
  }
  return additional_env;
};

Modularizer.launchAll = function(CLI, cb) {
  var module_folder = p.join(cst.PM2_ROOT_PATH, 'node_modules');

  var modules = listModulesV2();

  async.eachLimit(Object.keys(modules), 1, function(module, next) {
    var pmod = p.join(module_folder, module, cst.DEFAULT_MODULE_JSON);

    Common.printOut(cst.PREFIX_MSG_MOD + 'Starting module ' + module);

    var opts = {};

    if (modules[module] != true) {
      Common.extend(opts, modules[module]);
    }

    Common.extend(opts, {
      cmd : pmod,
      development_mode : false,
      proc_path : p.join(module_folder, module)
    });

    startModule(CLI, opts, function(err, dt) {
      if (err) console.error(err);
      return next();
    });

  }, function() {
    return cb ? cb(null) : false;
  });
};

Modularizer.install = function(CLI, module_name, opts, cb) {
  Common.printOut(cst.PREFIX_MSG_MOD + 'Installing module ' + module_name);

  var canonic_module_name = Utility.getCanonicModuleName(module_name);

  if (module_name == 'v8-profiler' || module_name == 'profiler') {
    installLangModule('v8-profiler', function(e) {
      Common.printOut(cst.PREFIX_MSG + chalk.bold.green('V8 profiling enabled'));
      return cb();
    });
    return false;
  }

  if (module_name == 'typescript') {
    // Special dependency install
    installLangModule('typescript', function(e) {
      installLangModule('ts-node@latest', function(e) {
        Common.printOut(cst.PREFIX_MSG + chalk.bold.green('Typescript support enabled'));
        return cb();
      });
    });
    return false;
  }

  if (module_name == 'livescript') {
    installLangModule('livescript', function(e) {
      Common.printOut(cst.PREFIX_MSG + chalk.bold.green('Livescript support enabled'));
      return cb();
    });
    return false;
  }

  if (module_name == 'coffeescript') {
    installLangModule('coffee-script', function(e) {
      Common.printOut(cst.PREFIX_MSG + chalk.bold.green('Coffeescript support enabled'));
      return cb();
    });
    return false;
  }

  if (moduleExist(canonic_module_name) === true) {
    /**
     * Update
     */
    Common.printOut(cst.PREFIX_MSG_MOD + 'Module already installed. Updating.');

    uninstallModule(CLI, canonic_module_name, function(err) {
      return installModule(CLI, module_name, opts, cb);
    });

    return false;
  }

  /**
   * Install
   */
  installModule(CLI, module_name, opts, cb);
};

/**
 * Uninstall module
 */
Modularizer.uninstall = function(CLI, module_name, cb) {
  Common.printOut(cst.PREFIX_MSG_MOD + 'Uninstalling module ' + module_name);

  //if (moduleExist(module_name) === false && module_name != '.') {
  //Common.printError(cst.PREFIX_MSG_MOD_ERR + 'Module unknown.');
  //return cb({msg:'Module unknown'});
  //}
  if (module_name == 'all') {
    var modules = listModulesV2();
    async.forEachLimit(Object.keys(modules), 1, function(module, next) {
      uninstallModule(CLI, module, next);
    }, cb);
    return false;
  }

  var canonic_module_name = Utility.getCanonicModuleName(module_name);
  uninstallModule(CLI, canonic_module_name, cb);
};

/**
 * Publish a module
 */
Modularizer.publish = function(cb) {
  var rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
  });

  var semver = require('semver');

  var package_file = p.join(process.cwd(), 'package.json');

  var package_json = require(package_file);

  package_json.version = semver.inc(package_json.version, 'minor');
  Common.printOut(cst.PREFIX_MSG_MOD + 'Incrementing module to: %s@%s',
                  package_json.name,
                  package_json.version);

  rl.question("Write & Publish? [Y/N]", function(answer) {
    if (answer != "Y")
      return cb();

    fs.writeFile(package_file, JSON.stringify(package_json, null, 2), function(err, data) {
      if (err) return cb(err);

      Common.printOut(cst.PREFIX_MSG_MOD + 'Publishing module - %s@%s',
                      package_json.name,
                      package_json.version);

      shelljs.exec('npm publish', function(code) {
        Common.printOut(cst.PREFIX_MSG_MOD + 'Module - %s@%s successfully published',
                        package_json.name,
                        package_json.version);

        Common.printOut(cst.PREFIX_MSG_MOD + 'Pushing module on Git');
        shelljs.exec('git add . ; git commit -m "' + package_json.version + '"; git push origin master', function(code) {

          Common.printOut(cst.PREFIX_MSG_MOD + 'Installable with pm2 install %s', package_json.name);
          return cb(null, package_json);
        });
      });
    });

  });
};

Modularizer.generateSample = function(app_name, cb) {
  var rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
  });

  function samplize(module_name) {
    var cmd1 = 'git clone https://github.com/pm2-hive/sample-module.git ' + module_name + '; cd ' + module_name + '; rm -rf .git';
    var cmd2 = 'cd ' + module_name + ' ; sed -i "s:sample-module:'+ module_name  +':g" package.json';
    var cmd3 = 'cd ' + module_name + ' ; npm install';

    Common.printOut(cst.PREFIX_MSG_MOD + 'Getting sample app');
    shelljs.exec(cmd1, function(err) {
      if (err) Common.printError(cst.PREFIX_MSG_MOD_ERR + err.message);
      shelljs.exec(cmd2, function(err) {
        console.log('');
        shelljs.exec(cmd3, function(err) {
          console.log('');
          Common.printOut(cst.PREFIX_MSG_MOD + 'Module sample created in folder: ', path.join(process.cwd(), module_name));
          console.log('');
          Common.printOut('Start module in development mode:');
          Common.printOut('$ cd ' + module_name + '/');
          Common.printOut('$ pm2 install . ');
          console.log('');

          Common.printOut('Module Log: ');
          Common.printOut('$ pm2 logs ' + module_name);
          console.log('');
          Common.printOut('Uninstall module: ');
          Common.printOut('$ pm2 uninstall ' + module_name);
          console.log('');
          Common.printOut('Force restart: ');
          Common.printOut('$ pm2 restart ' + module_name);
          return cb ?  cb() : false;
        });
      });
    });
  }

  if (app_name) return samplize(app_name);

  rl.question(cst.PREFIX_MSG_MOD + "Module name: ", function(module_name) {
    samplize(module_name);
  });

};

function isValidModule(conf) {
  var valid = true;

  if (!conf.apps) {
    Common.printError(cst.PREFIX_MSG_MOD_ERR + 'apps attribute indicating the script to launch is not defined in the package.json');
    return false;
  }

  if (Array.isArray(conf.apps)) {
    conf.apps.forEach(function(app) {
      if (!app.script)
        valid = false;
    });
  }
  else {
    if (!conf.apps.script)
      valid = false;
  }

  return valid;
};

function moduleExist(module_name) {
  var modules = getModuleInstalled();

  if (module_name.indexOf('/') > -1)
    module_name = module_name.split('/')[1];

  return modules.indexOf(module_name) > -1 ? true : false;
};

function getModuleInstalled() {
  shelljs.config.silent = true;
  var module_folder = p.join(cst.PM2_ROOT_PATH, 'node_modules');
  var modules       = shelljs.ls('-A', module_folder);
  shelljs.config.silent = false;
  return modules;
}


#!/usr/bin/env bash

SRC=$(cd $(dirname "$0"); pwd)
source "${SRC}/include.sh"

cd $file_path/interpreter

########### coffee

$pm2 start echo.coffee
sleep 1
should 'process should be errored without coffee installed' "status: 'errored'" 1

########### Install

$pm2 install coffeescript

########### coffee fork test
$pm2 delete all

$pm2 start echo.coffee

sleep 1.5

should 'process should not have been restarted' 'restart_time: 0' 1
should 'process should be online' "status: 'online'" 1

########### coffee cluster test
$pm2 delete all

$pm2 start echo.coffee -i 1

sleep 1.5

should 'process should not have been restarted' 'restart_time: 0' 1
should 'process should be online' "status: 'online'" 1

########## LIVESCRIPT

$pm2 delete all
$pm2 start echo.ls
sleep 1
should 'process should be errored without coffee installed' "status: 'errored'" 1

########### Install

$pm2 install livescript

########### livescript fork test
$pm2 delete all

>livescript.log

$pm2 start echo.ls -o livescript.log --merge-logs

sleep 1.5
grep "Hello Livescript!" livescript.log
spec "Should work on Livescript files in fork mode"

########### livescript cluster test
$pm2 delete all

>livescript.log

$pm2 start echo.ls -i 1 -o livescript.log --merge-logs

sleep 1.5
grep "Hello Livescript!" livescript.log
spec "Should work on Livescript files in cluster mode"

########### TYPESCRIPT

$pm2 delete all
$pm2 start echo.ts
sleep 1
should 'process should be errored without coffee installed' "status: 'errored'" 1

########### Install

# $pm2 install typescript

########### typescript fork test
# $pm2 delete all

# >typescript.log

# $pm2 start echo.ts -o typescript.log --merge-logs

# sleep 1.5

# grep "Hello Typescript!" typescript.log
# spec "Should work on Typescript files in fork mode"

########### typescript cluster test
# $pm2 delete all

# >typescript.log

# $pm2 start echo.ts -i 1 -o typescript.log --merge-logs

# sleep 1.5
# grep "Hello Typescript!" typescript.log
# spec "Should work on Typescript files in cluster mode"


class Greeter {
  constructor(public greeting: string) { }
  greet() {
    return this.greeting;
  }
};

var greeter = new Greeter("Hello Typescript!");

console.log(greeter.greet());


#!/bin/bash

RESULT_FILE=result.monit

export RESULT_FILE=$RESULT_FILE

launch() {
    echo "========= `date`" >> $RESULT_FILE
    nohup ./monit-daemon.sh &> monit.log &
}

ppkill() {
    pkill -f monit-daemon.sh ; pkill -f sleep
}

case "$1" in
    start)
        launch
        ;;
    kill)
        ppkill
        ;;
    stop)
        ppkill
        ;;
    restart)
        ppkill
        launch
        ;;
    *)
        echo "Usage: {start|kill|stop|restart}"
        exit 1
        ;;
esac
exit $RETVAL


Common.printOut(cst.PREFIX_MSG + chalk.bold.green('Typescript support enabled'));