[
  {
    "path": ".editorconfig",
    "content": "# top-most EditorConfig file\nroot = true\n\n# Unix-style newlines with a newline ending every file\n[*]\nend_of_line = lf\ncharset = utf-8\ntrim_trailing_whitespace = true\ninsert_final_newline = true\nindent_style = space\nindent_size = 2\n\n# Makefile\n[Makefile]\nindent_style = tab\n"
  },
  {
    "path": ".gitattributes",
    "content": "* text=auto\n*.sh eol=lf\nbin/** eol=lf\ntest/fixtures/** eol=lf\n"
  },
  {
    "path": ".github/CONTRIBUTING.md",
    "content": "# Contributing to PM2\n\n## Pull-Requests\n\n1. Fork pm2\n2. Create a different branch to do your fixes/improvements if it's core-related.\n3. Please add unit tests! There are lots of tests take examples from there!\n4. Try to be as clear as possible in your commits\n5. Pull request on the **development branch** from your fork\n\nWe 'd like to keep our master branch as clean as possible, please avoid PRs on master, thanks!\n\n## Fire an issue\n\nWhen you got an issue by using pm2, you will fire an issue on [github](https://github.com/Unitech/pm2). We'll be glad to help or to fix it but the more data you give the most fast it would be resolved.\nPlease try following these rules it will make the task easier for you and for us:\n\n#### 1. Search through issues if it hasn't been resolved yet\n#### 2. Make sure that you provide following informations:\n  - pm2 version `pm2 --version`\n  - nodejs version `node --version`\n  - operating system\n  - `head -n 50 ~/.pm2/pm2.log` output\n\n#### 3. Provide details about your issue:\n  - What are the steps that brought me to the issue?\n  - How may I reproduce this? (this isn't easy in some cases)\n  - Are you using a cluster module? Are you trying to catch SIGTERM signals? With `code` if possible.\n\n#### 4. Think global\nIf your issue is too specific we might not be able to help and stackoverflow might be a better place to seak for an answer\n\n#### 5. Be clear and format issues with [markdown](http://daringfireball.net/projects/markdown/)\nNote that we might understand english, german and french but english is prefered.\n\n#### 6. Use debugging functions:\n\n```DEBUG=pm2:* PM2_DEBUG=true ./bin/pm2 --no-daemon start my-buggy-thing.js```\n\nIf your issue is flagged as `need data` be sure that there won't be any upgrade unless we can have enough data to reproduce.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE.md",
    "content": "## What's going wrong?\n\n## How could we reproduce this issue?\n\n## Supporting information\n\n```\n# Run the following commands\n$ pm2 report\n```\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "content": "<!--\nPlease always submit pull requests on the development branch.\n-->\n| Q             | A\n| ------------- | ---\n| Bug fix?      | yes/no\n| New feature?  | yes/no\n| BC breaks?    | no\n| Deprecations? | no\n| Tests pass?   | yes\n| Fixed tickets | #1234, #5678\n| License       | MIT\n| Doc PR        | https://github.com/pm2-hive/pm2-hive.github.io/pulls\n<!--\n*Please update this template with something that matches your PR*\n-->"
  },
  {
    "path": ".github/stale.yml",
    "content": "# Number of days of inactivity before an issue becomes stale\ndaysUntilStale: 30\n# Number of days of inactivity before a stale issue is closed\ndaysUntilClose: 14\n# Issues with these labels will never be considered stale\nexemptLabels:\n  - Inspecting\n  - security\n  - \"P3: Medium\"\n  - \"T: Bug\"\n  - \"S: Open for PR\"\n  - \"T: Enhancement\"\n  - \"T: Feature\"\n  - \"S: In Progress\"\n  - \"S: Pending Release\"\n\n# Label to use when marking an issue as stale\nstaleLabel: stale\n# Comment to post when marking an issue as stale. Set to `false` to disable\nmarkComment: >\n  This issue has been automatically marked as stale because it has not had\n  recent activity. It will be closed if no further activity occurs. Thank you\n  for your contributions.\n# Comment to post when closing a stale issue. Set to `false` to disable\ncloseComment: false\nlimitPerRun: 30\nonly: issues\n"
  },
  {
    "path": ".github/workflows/node.js.yml",
    "content": "name: Node.js CI\n\non: [push, pull_request]\n\njobs:\n\n  node-tests:\n    runs-on: ubuntu-latest\n    timeout-minutes: 30\n\n    strategy:\n      matrix:\n        node-version: [16.x, 24.x]\n\n    steps:\n    - uses: actions/checkout@v4\n    - name: Use Node.js ${{ matrix.node-version }}\n      uses: actions/setup-node@v4\n      with:\n        node-version: ${{ matrix.node-version }}\n        cache: 'npm'\n    - name: Install Python\n      run: sudo apt install python3\n    - name: Install PHP CLI\n      run: sudo apt install php-cli\n    - name: Install Node.js dependencies\n      run: npm install\n    - name: Run end-to-end tests\n      run: npm run test:e2e\n    - name: Run unit tests\n      run: npm run test:unit\n\n  bun-tests:\n    runs-on: ubuntu-latest\n    timeout-minutes: 30\n\n    steps:\n    - name: Checkout code\n      uses: actions/checkout@v4\n    - name: Remove Node.js installed by setup-node action (if any)\n      run: |\n        if command -v node &> /dev/null; then\n          sudo rm -rf \"$(which node)\"\n        fi\n        if command -v npm &> /dev/null; then\n          sudo rm -rf \"$(which npm)\"\n        fi\n\n    - name: Setup Bun\n      uses: oven-sh/setup-bun@v1\n    - name: Install dependencies using Bun\n      run: bun install\n    - name: Run end-to-end tests with Bun\n      run: bun run test:e2e\n    - name: Run unit tests with Bun\n      run: bun run test:unit\n"
  },
  {
    "path": ".gitignore",
    "content": "/node_modules\n*.log\n*.pid\ntest/child\n*.iml\n.idea/**\n*.heapsnapshot\n*.cpuprofile\n.cache-require-paths.json\ndist/\n*.deb\n*.rpm\n.DS_Store\n*.swp\n*.swo\ncurrentTagChangelog.md\njoblog-X\ntest/fixtures/path-check*.txt\nyarn.lock\n*.tar.gz\ne2e_time\nunit_time\n*.heapprofile\na.out\n"
  },
  {
    "path": ".mocharc.js",
    "content": "\nmodule.exports = {\n  'allow-uncaught' : false,\n  'async-only': false,\n  bail: true,\n  color: true,\n  delay: false,\n  diff: true,\n  exit: true,\n  timeout: 10000,\n  'trace-warnings': true,\n  ui: 'bdd',\n  retries: 2\n}\n"
  },
  {
    "path": ".npmignore",
    "content": "test\napps\ndoc\n/pres\n*.log\n*.pid\nexamples\n*.pyc\n.github\n.cache-require-paths.json\n*.heapsnapshot\npackager\nartifacts\n.editorconfig\n.bithoundrc\ndist\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "\n## 6.0.14\n\n- Fixed version of @pm2/pm2-version-check #6055\n- CVE-2025-64718 Update js-yaml\n- replace fs.R_OK with fs.constants.T_OK #6012 #6019\n\n## 6.0.13\n\n- Fix blessed package import\n\n## 6.0.12\n\n- #6037 Drop npm-shrinkwrap in favor of fixed dependencies versions\n- #5577 fix pm2 monit crash\n\n## 6.0.11\n\n- #6034 replace package-lock.json by npm-shrinkwrap.json\n- #5915 fix allowing to update namespaced pm2 NPM module (@org/module-name)\n\n## 6.0.10\n\n- revert #5971 #6031\n\n## 6.0.9\n\n- updates all typescript definitions\n- upgrade github ci workflows\n- upgrade mocha dep and adapt tests\n- bump packages\n- fix:Potential ReDoS Vulnerability or Inefficient Regular Expression in Project: Need for Assessment and Mitigation #5971\n\n## 6.0.8\n\n- fix: package-lock update\n\n## 6.0.7\n\n- fix: ansis-node10 https://github.com/Unitech/pm2/commit/99d9224e940d119a1ad5b241b4fc4e0db7c830ed @webdiscus\n\n## 6.0.6\n\n- refactor: replace chalk with smaller alternative by @webdiscus\n\n## 6.0.5\n\n- Bun support - Fixes #5893 #5774 #5682 #5675 #5777\n- Disable git parsing by default #5909 #2182 #5801 #5051 #5696\n- Add WEBP content type for pm2 serve #5900 @tbo47\n- Enable PM2 module update from tarball #5906 @AYOKINYA\n- Fix treekil on FreeBSD #5896 @skeyby\n- fix allowing to update namespaced pm2 NPM module (@org/module-name) #5915 @endelendel\n\n## 5.4.3\n\n- Update sub packages\n\n## 5.4.2\n\n- Update sub packages\n\n## 5.4.1\n\n- @pm2/io DeprecationWarning: The util._extend API is deprecated https://github.com/keymetrics/pm2-io-apm/issues/301 @egoroof\n\n## 5.4.0\n\n- #5782 add autostart true||false feature by @ultimate-tester\n- fix UUID deprecation\n- updates modules\n\n## 5.3.1\n\n- #5686 Switch from Travis CI to Github Actions\n- #5680 Fixed reserved keyword for ES6 Strict Mode when Bundling @juaneth\n- #5683 update badges\n- #5684 auto switch light and dark mode logos\n- #5678 Bugfix/deploy ecosystem filename extension / esm module default ecosystem config name @TeleMediaCC\n- #5660 Fix matching logic for logs from namespace when lines = 0 @bawjensen\n- fix \"vulnerabilities\" in axios module\n\n## 5.3.0\n\n- fix: replace non-working condition that blocks flush from clearing the logs #5533 @Sailboat265\n- fix: ESM script loader #5524 @BlueWater86\n\n## 5.2.2\n\n- fix: correct pm2 ls display when there is a (very) long process id (@dko-slapdash)\n- typo: corrections\n\n## 5.2.1\n\n- fix cluster error avoiding process restart (#5396)\n- ensure increment_var value is a number (#5435)\n- update dependencies\n- add node latest to travis testing\n\n## 5.2.0\n\n- replace node-cron by croner (#5183 #5035)\n- upgrade mocha deps\n- fix pm2 report when daemon not running\n- remove semver check for legacy node.js versions\n- update node version in setup.deb.sh by using lts (#5201) + openrc\n- replace legacy util._extend by Object.assign (#5239)\n- add missing start options types (#5242)\n- recursive detection of package.json (#5267)\n- make tarball module uninstall cross-platform (#5269)\n- Fix unnecessary \"ENOENT\" console.error when serving a spa (#5272)\n- fix: used env variable instead of hardcode datetime format (#5277)\n- copyright update (#5278)\n- fix: remove constants import from VersionCheck (not needed) (#5279)\n- Reduce async import (#5280)\n\n## 5.1.2\n\n- easily disable cron-restart strategy via `$ pm2 restart --cron-restart 0`\n- allow to update cron-restart on restart\n\n## 5.1.1\n\n- remove fast-printf and replace with sprintfjs\n\n## 5.1.0\n\n- add back Node 10.x support\n- make pm2-sysmonit module optional\n\n## 5.0.3\n\n- skip system monitoring on Windows\n\n## 5.0.1/5.0.2\n\n- fix npm install --no-optional pm2\n\n## 5.0.0\n\n### System Monitoring\n\nA new local system monitoring feature has been added, allowing to monitor numerous vital server metrics.\n\nMost important metrics will be displayed when doing a pm2 ls:\n\n```bash\n┌─────┬─────────────────┬─────────────┬─────────┬─────────┬──────────┬────────┬──────┬───────────┬──────────┬──────────┬──────────┬──────────┐\n│ id  │ name            │ namespace   │ version │ mode    │ pid      │ uptime │ ↺    │ status    │ cpu      │ mem      │ user     │ watching │\n├─────┼─────────────────┼─────────────┼─────────┼─────────┼──────────┼────────┼──────┼───────────┼──────────┼──────────┼──────────┼──────────┤\n│ 4   │ app             │ default     │ 1.0.0   │ fork    │ 164618   │ 2s     │ 1670 │ online    │ 0%       │ 41.8mb   │ unitech  │ disabled │\n└─────┴─────────────────┴─────────────┴─────────┴─────────┴──────────┴────────┴──────┴───────────┴──────────┴──────────┴──────────┴──────────┘\nhost metrics | cpu: 1.6% 42.9º | mem free: 52.0% | wlp0s20f3: ⇓ 0mb/s ⇑ 0mb/s | disk: ⇓ 0.199mb/s ⇑ 0mb/s /dev/nvme0n1p3 88.25% |\n```\n\nAll server metrics will be available to pm2.io:\n\n```\n┌─────────────────────────────┬──────────────────────────────────┐\n│ PM2 CPU Usage               │ 0.0 %                            │\n│ PM2 Memory Usage            │ 67.4 mb                          │\n│ PM2 Agent CPU Usage         │ 0 %                              │\n│ PM2 Agent Memory Usage      │ 0 mb                             │\n│ CPU Usage                   │ 1.5 %                            │\n│ CPUs Usage                  │ 0|0|0|0|2|0|0|1|0|0|0|1|0|1|2|8  │\n│ CPU Temperature             │ 42.9 °C                          │\n│ RAM Total                   │ 15.34 gb                         │\n│ RAM Free                    │ 1.18 gb                          │\n│ RAM Active                  │ 7.35 gb                          │\n│ RAM Available               │ 7.99 gb                          │\n│ RAM Usage                   │ 47.9 %                           │\n│ FD Opened                   │ 15072                            │\n│ Disk Writes                 │ 0 mb/s                           │\n│ Disk Reads                  │ 0.24 mb/s                        │\n│ Disk Usage                  │ 88.25 %                          │\n│ Disk Size                   │ 465.60 gb                        │\n│ Total TX                    │ 0.005 mb/s                       │\n│ Total RX                    │ 0.004 mb/s                       │\n│ fs:use:/dev/nvme0n1p3       │ 88.25 %                          │\n│ fs:size:/dev/nvme0n1p3      │ 465.60 gb                        │\n│ net:tx_5:wlp0s20f3          │ 0.005 mb/s                       │\n│ net:rx_5:wlp0s20f3          │ 0.004 mb/s                       │\n│ net:rx_errors_60:wlp0s20f3  │ 0 /min                           │\n│ net:tx_errors_60:wlp0s20f3  │ 0 /min                           │\n│ net:rx_dropped_60:wlp0s20f3 │ 0 /min                           │\n│ net:tx_dropped_60:wlp0s20f3 │ 0 /min                           │\n│ graphics:mem:total          │ 3878 mb                          │\n│ graphics:mem:used           │ 1260 mb                          │\n│ graphics:temp               │ 46 °C                            │\n└─────────────────────────────┴──────────────────────────────────┘\n```\n\n#### Disabling system monitoring\n\n```\n# Disable system monitoring\npm2 set pm2:sysmonit false\n# Enable system monitoring\npm2 set pm2:sysmonit true\n```\n\n### PM2.io bandwidth reduction\n\nData quantity sent from PM2 to PM2.io has been reduced by 80%, thanks for a json patch differential system. Much more data can now be exposed (metrics, actions) to PM2.io\n\n### Other fixes\n\n- feat: added args and full script path to monitoring data\n- fix: regular local ip check\n- fix: pm2 agent watchdog has been consolidated and fixes freezing issues\n- fix: modules bumped\n\n## 4.5.6\n\n- all submodules version bumped\n- supress all \"security\" warnings\n\n## 4.5.5\n\n- bump debug\n- remove systeminformation\n\n## 4.5.4\n\n- drop non used package\n\n## 4.5.3\n\n- bump vizion from 0.2.13 to 2.2.1 (Snyk CVE)\n- bump chokidar to 3.5.1\n\n## 4.5.2\n\n- bump @pm2/js-api\n\n## 4.5.1\n\n- fix: cron in cluster mode was not restarting process after one pass - fixes #4834 #4733 #4307 #4834\n- fix: restore --sort <param> option on `pm2 ls` - fix #4536\n- fix: fix tests with npm7\n- fix: restore warning message about process list changed and not dumped\n- chore: alias `--cron` with `--cron-restart`\n- chore: test PM2 againt Node.js 15.x\n- chore: upgrade systeminformation and debug module to latest - PR #4892 by @AdamMajer\n- chore: drop Node 9.x from travis testing\n- chore: remove ps-list module\n\n## 4.5.0\n\n- fix: creating log folder in sync mode. #4846 - PR #4844 by @QS20199\n- fix: PM2 unable to run ESM packages: ERR_UNSUPPORTED_ESM_URL_SCHEME #4839 - PR #4841 by @ox-harris\n- fix: Use opts.namespace if it was passed in. Previously, it was ignored. #4778 - PR #4791 by @sbleon\n- fix: Prevent reloadLogs command from hanging. #4781 - PR #4782 by @mbrooks and Franck Danard\n- fix: backward compatibility fix for tarball modules - PR #4767 by @ykolbin\n- fix: Operation not permitted on call setgid #2957 - PR #4681 by @guard43ru\n- chore: upgrade dependencies\n\n## 4.4.1\n\n- feature drop: autodump fixes #4763\n- fix: fix starting pm2 script from inside an app #4761 (need use PM2_PROGRAMMATIC)\n\n## 4.4.0\n\n- feature: support Node v14.0\n\n## 4.3.1\n\n- fix: downgrade vizion\n\n## 4.3.0\n\n- feature: reduce by 1/3 pm2 package size (drop moment, lodash)\n- feature: pm2 start app.js --filter-env NODE_ENV #4596\n- feature: print logs of a particular namespace #4517 @bvsubhash\n- feature: trigger all the process using 'all' or trigger all processes in a particular namespace #4518 @bvsubhash\n- feature: support ecosystem.config.cjs #4662 @Timic3\n- fix: disable log (--error /dev/null --output /dev/null) on Windows #4560 @codpot\n- fix: pm2 install <tar_file> adaptation #4593 @adunkey\n- fix: add type for pm2.reload with optional options parameter #4615 @kevindoveton\n- fix: add `ignore_watch` to StartOptions types #4629 @jlvcm\n- fix: fix --cwd CLI option description #4639 @warpdesign\n- fix: do not require cron if not necessary in CLI\n- fix: upgrade mkdirp to 1.0.4 (sec vuln) #4638\n- chore: bump mocha to latest + drop mocha.opts + add .mocharc.yml\n- chore: pm2 init ecosyst file template changes\n- chore: pm2-deploy@1.0.2 (revert fix)\n- chore: pm2-io-apm@4.3.4 (instant trace + broadcast trace threshold + boolean metrics support)\n\n\n## 4.2.3\n\n- fix: Fix an import error on Node 9.x\n\n## 4.2.2\n\n- feat: Support ESM on Node 13 via .mjs file or `\"type\":\"module\"` in package.json #4540\n- fix: Fix an error for pm2 path on Windows. #4563\n\n## 4.2.1\n\n- fix: col size too small for certain app name with tracing enabled\n- chore: check for pm2 updates\n\n## 4.2.0\n\n- feature: `pm2 logs --highlight <str>` to highlight specified string when using `pm2 logs` #4013 by @bvsubhash\n- feature: `pm2 sysmonit` activate system wide monitoring && `pm2 sysinfos` display system informations\n- feature: new App Namespace feature via `namespace` attr or `pm2 start app.js --namespace <ns>` #3471 by @bvsubhash\n- feature: enforce message sending for Windows Graceful Shutdown via `pm2 start app.js --shutdown-with-message` to use message sending for specific process or via `PM2_KILL_USE_MESSAGE=true pm2 update` to default this behavior on PM2. #3561 #3691 #3555 #4469 #4470 #4474 by @aleksk and @8ai\n- feature: `pm2 ls` now display a hint when process list differ from dump file\n- fix: if id > 100 not shown in pm2 ls #4475\n- fix: stop and delete cron immediately on deletion of a process #4492 by @bvsubhash\n- fix: display correct username by @bvsubhash\n- chore: add test on node 13.x\n- chore: cleanup some unused files\n- chore: enforce node >= 8.10.0\n\n## 4.1.2\n\n- fix: temporarily disable system metrics retrieval\n\n## 4.1.1\n\n- fix: #4452 disable network collection metric\n\n## 4.1.0\n\n- fix: #4270 fix ANSI escape for `pm2 ls --watch`\n- fix: `pm2 start app.js -i 1` start app in cluster mode\n- fix: #4254 add HOST environment variable on pm2 serve\n- fix: #4267 Allow usernames in uid/gid/user again\n- fix: #4376 make process.send('ready') trigger sigint\n- fix: #4443 allow pm2-runtime to auto-exit even if modules are still running\n- fix: #4364 typos\n- fix: #4288 add 'max' type in typescript definition\n\n## 4.0.2\n\n- fix: #4450 do not open sysinfo window on Windows\n\n## 4.0.1\n\n- chore: switch Systeminfo logging to debug\n\n## 4.0.0\n\n- feat: make pm2 fully standalone with node embedded\n- feat: startup, npm, node system adaptation for standalone installs\n- feat: system information worker - retrieve:\n           - network I/O + latency\n           - disk I/O + space\n           - cpu usage + temperature\n           - memory usage\n           - intelligent display of information (e.g. display disks > 80% cpu usage)\n- feat: listing of docker container on host machine with independent pm2 list\n- feat: upgrade of Chokidar to 3.x - Massive CPU & RAM consumption improvements for watch feature\n- r&d: manage container like pm2 processes\n- feat: intelligent display of high loaded processes in an independent pm2 list\n- feat: #4224 --ignore-watch now accepts globs (@vaskevich)\n- feat: pm2 save --force allow to force save of empty process list\n- fix: pm2 monit dashboard without leaks\n- fix: pm2 register fixed\n- refactor/fix: pm2 listing systems refactoring\n- chore: remove old legacy code for < 8 Node.js versions\n- chore: make the repo lighter\n- chore: better display when pm2-runtime is linked to on-premise\n- chore: pm2 CLI refactoring #4391\n\n## 3.5.0\n\n- feat: #4220 #2941 #4196 improve pm2 serve for SPA - autoredirect requests to index.html if --spa\n- feat: on pm2 show <app>, display metric unit and divergent environment variables\n- feat: #4157 tweak systemd script to auto restart PM2 in case of crash failure\n- fix: #4212 on pm2 show, avoid crash when versioning comment is not present\n- fix: #4171 fix pm2 list when small screen\n- fix: #4197 fix pm2 unstartup for macOS\n- fix: #2764 in pm2 monit, only display log of selected application\n- fix: #2793 pm2 monit, rolling log buffer, avoid crash and performance issues\n- fix: #4060 do not emit online when application is errored\n- chore: remove nssocket in dependencies\n\n## 3.4.1\n\n- fix: allow pm2 register / pm2 monitor\n- fix: restore trace indicator\n\n## 3.4.0\n\n- use @pm2/io version 4\n- disable @pm2/io for node.js v4 and v5\n\n/!\\ Warning, built-in custom metrics are not supported anymore on Node 4 and 5\n\nNew builtin metrics when starting a Node.js application:\n- Heap Size\n- Heap Usage\n- Used Heap Size\n- Active Requests\n- Active handles\n- Event loop latency\n- Event loop latency p95\n- HTTP queries per minutes\n- HTTP Mean Latency\n- HTTP P95 Latency\n\n## 3.3.1 (18/02/19)\n\n- add pm2 profile:cpu [timeout]\n- add pm2 profile:mem [timeout]\n\n## 3.3.0 (14/02/19)\n\n- Upgrade pmx to ^3\n\n## 3.2.9 (17/01/19)\n\n- #4128 fix: force detached process\n\n## 3.2.6-8 (11/01/19)\n\n- rollback: node bin path handling adaptation\n\n## 3.2.5 (09/01/19)\n\n- feat: enhance pm2 report\n- feat: support snap Ubuntu system\n- fix: pm2 register/monitor command\n- fix: consolidate spawn function on unhealthy systems to avoid pm2 crash\n- fix: error message if extra lang interpreter are not installed when runing tests\n- fix: (pm2 deploy) command line bug when passing env variables to post-deploy hook\n- fix: (pm2 deploy) always deploy to default branch problem\n- fix: (pm2 deploy) pm2 deploy <env> exec now accept multiple commands\n- fix: print full env + skip extra internal fields when using programmatic pm2\n\n## 3.2.4 (19/12/18)\n\n### Feat\n\n- display cron configuration when doing `pm2 desc <id>`\n- refactor test suite (benchmark/simplification)\n\n### Fix\n\n- pm2 flush <app> flush only one app\n- resolve uid properly on pm2 ls / pm2 desc\n- keep wait_ready option on process reload\n- keep stringification of environment variable behavior\n- return an error when using pm2 api on starting json configuration if one app is errored\n\n## 3.2.3 (4/12/18)\n\n### Fix\n\n- medium rare bug: fix issue when acting on process file (#3987 + #3192)\n- concurrent action to 1 if acting on only 2 processes\n- fix cluster syntax\n- add more test on port release\n\n## 3.2.2 (5/10/18)\n\n### Fix\n\n- minor bug: fix bug when passing -i 'max' or -i 0\n\n## 3.2.1 (3/10/18)\n\n### Fix\n\n- minor bug: get internal pm2 config after creation on new pm2 boots\n\n## 3.2.0 (3/10/18)\n\n### Features\n\n- package.json version field retrieval and display in pm2 ls, pm2 show, pm2 monit\n- pm2 internal configuration system via `pm2 set pm2:key value`, attached to pm2.user_conf\n- add the .user field (CLI + Config) to set the user to start the application with\n- add the .time field (CLI + Config) to enable default logs date prefix\n- max_memory_restart now triggers a reload\n- pm2 env <pm_id> command to display the environment the application is running with\n- exponential backoff restart delay via `--exp-backoff-restart-delay <ms>` with reset mechanism\n- new timing library on PM2 daemon (increase log througput, reduce CPU usage and memory usage)\n- better user management system with username resolution to uid\n- websocket default switch for pm2 plus\n- new module management system (`pm2 package <folder>`, `pm2 publish <folder>`, `pm2 install <tarball>`)\n\n### Fix\n\n- @pm2/io 2.4 (restart > 10.0)\n- restart behavior tested\n- fix module version parsing\n- module system refactoring (TAR + NPM)\n- fix watch_delay in config file\n\n## 3.1.3 (20/09/18)\n\n### Features\n- allow non-node application to run multiple instances without auto switch to cluster mode\n- allow to call `pm2 logs` even without application (#3820)\n- switch `pm2 link` and `pm2 plus` protocol to websocket by default instead of axon\n- enhance the `pm2 init` template that generates ecosystem files by adding some extra fields\n\n### Fix\n- remove deprecation message for node 0.10\n- pm2 login/register/monitor now hit the new oauth pm2 plus system\n\n## 3.1.2 (10/09/18)\n\n- version bump on @pm2/io\n\n## 3.1.1 ( Mon Sep 10 2018 16:18:25 GMT+0200 (CEST) )\n\n\n## Hot Fixes\n  - #3901 fix error when installing module\n  ([7b43fea5](https://github.com/Unitech/pm2/commit/7b43fea55d7c2853a3032b3bddd12201cd6a29e9))\n\n\n## 3.1.0 ( Mon Sep 10 2018 10:25:13 GMT+0200 (CEST) )\n\n\n## Bug Fixes\n  - tmp fix io@beta + rename metric\n  ([04ab7ac4](https://github.com/Unitech/pm2/commit/04ab7ac4e1312c5a5332f37cbb81b0d98686936d))\n  - remove ending \\n on git version comment\n  ([9a36bfeb](https://github.com/Unitech/pm2/commit/9a36bfeb7e9f5ab1719ca3858510da08bb0cad6b))\n  - #3883 fix typings for max_memory_restart and add wait_ready\n  ([b35ea237](https://github.com/Unitech/pm2/commit/b35ea237e3b448088112b2f3a771a9c5286417a7))\n  - restore monitored indicator\n  ([34966432](https://github.com/Unitech/pm2/commit/349664329eb56232321694be9e08f16a3cda6fbd))\n  - remove install of modules on pm2 plus command\n  ([6a8bb269](https://github.com/Unitech/pm2/commit/6a8bb26952a7dcf109d28af7224b89faf0977a71))\n  - invert kill/link\n  ([3c37b528](https://github.com/Unitech/pm2/commit/3c37b5283bf0dea130fd375a5563974bd84543a9))\n  - #3877 #3831\n  ([16f4f2bc](https://github.com/Unitech/pm2/commit/16f4f2bc6589e8f0666f46d37c3f7f7739de7261))\n  - #3865 ensure pm2 never run simultaneous gracefullExit, prevent dump file corruption\n  ([79679db1](https://github.com/Unitech/pm2/commit/79679db1b321bbcc7296dbc41d005500cf61d273))\n  - #3786 fix issue when triggering an action that does not exist\n  ([1ff7fd3d](https://github.com/Unitech/pm2/commit/1ff7fd3d49ccaf3f65540774426b62fdc811e4f1))\n  - fixed unstartup when launchd\n  ([3d0461c3](https://github.com/Unitech/pm2/commit/3d0461c3e0a2362aef009e6f158b6f16b3d6510c))\n  - access gl_retry as class property\n  ([bbcb2b6b](https://github.com/Unitech/pm2/commit/bbcb2b6b5c5fa0ef872b64a648461c266350423a))\n  - #3831 switch registerToKM() to register()\n  ([8df2451e](https://github.com/Unitech/pm2/commit/8df2451e05bf5494b11f0546965718efe1f351b9))\n\n\n\n\n## Features\n  - add id column in stacked mode (80 char mode)\n  ([83033d4c](https://github.com/Unitech/pm2/commit/83033d4cdeb899bc4c1d1fe7a8c6391e64e9d0d0))\n\n\n\n\n## Refactor\n  - only enable deep monitoring if explicitly stated\n  ([f67e14f0](https://github.com/Unitech/pm2/commit/f67e14f0bd6d65bff6ef8f7e27e3f0aa93c60e40))\n  - #3786 clean code\n  ([6cbca8bc](https://github.com/Unitech/pm2/commit/6cbca8bccc0126f1557bf8326c81facc62100704))\n  - removes unused imports.\n  ([b8b48e83](https://github.com/Unitech/pm2/commit/b8b48e83f7f041508e39815e22501509259d4f26))\n  - only import the necessary methods from async.\n  ([6466ee44](https://github.com/Unitech/pm2/commit/6466ee44c1b85858f9b7e56b01aa6f2a08bde508))\n  - removes unused async imports.\n  ([679b14ff](https://github.com/Unitech/pm2/commit/679b14ff4b24519b5479c9e5f4ce0d9c32e39e55))\n\n\n\n\n## Chore\n  - upgrade to 3.1.0\n  ([0285d12d](https://github.com/Unitech/pm2/commit/0285d12df335667e9e0311a7abe175796bb517f4))\n  - update apm version\n  ([cc27de4a](https://github.com/Unitech/pm2/commit/cc27de4a8b400f1c20ba2e4b12dadcef1dd34fae))\n  - README update\n  ([c505dcc1](https://github.com/Unitech/pm2/commit/c505dcc1685380728b23f8757aa80fa4387d7fd3))\n  - remove unused console.log\n  ([61e32a43](https://github.com/Unitech/pm2/commit/61e32a4305490cc64c0a40cd83e2ad48c133b272))\n  - upgrade vizion to 2.0.2\n  ([c231e286](https://github.com/Unitech/pm2/commit/c231e28604aa4628d8f8ba10ea1f9f82e73269e6))\n  - #3415 try to update vizion to 2.0.1\n  ([9b80d8c1](https://github.com/Unitech/pm2/commit/9b80d8c1b69c07d21e63441c266b7acafffe0673))\n  - #3415 try to update vizion to 2.0.0\n  ([2c3df093](https://github.com/Unitech/pm2/commit/2c3df09378a92bac9de2d3b3b83103e02bd1bb82))\n  - update readme with 3.0.3 commits\n  ([476542fb](https://github.com/Unitech/pm2/commit/476542fbad038b951b6cfe6d6903d7b6bc8540a5))\n\n\n\n\n## Branchs merged\n  - Merge branch 'master' into development\n  ([95321c6d](https://github.com/Unitech/pm2/commit/95321c6dd2602e9ef71028731fd7a2e7b40a0d3c))\n  - Merge branch 'master' into development\n  ([c3c0e423](https://github.com/Unitech/pm2/commit/c3c0e423f9beeab25f53c0267d5f8a9e79d5c2e3))\n  - Merge branch 'master' into development\n  ([8e6481bc](https://github.com/Unitech/pm2/commit/8e6481bc9a6d23283895bf9cd3c7831c49a811ae))\n  - Merge branch 'development' into development\n  ([83294afe](https://github.com/Unitech/pm2/commit/83294afee7cf0204208e9cc7f4cf687469556492))\n  - Merge branch 'development' into flag--ext\n  ([79ab9242](https://github.com/Unitech/pm2/commit/79ab92425fef22cdf679fa77840d86a6e7cfc755))\n  - Merge branch 'development' into post_install\n  ([d5604300](https://github.com/Unitech/pm2/commit/d5604300685ace1c7dbd18776fd3df79da96f638))\n\n\n\n\n## Pull requests merged\n  - Merge pull request #3885 from Unitech/typings\n  ([19a35e9b](https://github.com/Unitech/pm2/commit/19a35e9b23716df8f7d1301acf7b0f0b601f93dd))\n  - Merge pull request #3878 from cuspymd/fix-command-help\n  ([2d3d2044](https://github.com/Unitech/pm2/commit/2d3d204427ce02617aa134ca0831a844de1a697d))\n  - Merge pull request #3876 from Unitech/lost_apps_sigterm\n  ([4fa247a3](https://github.com/Unitech/pm2/commit/4fa247a3e370607cf4198743de41dfa0a94bfbb5))\n  - Merge pull request #3874 from Unitech/trigger_no_action\n  ([e868f003](https://github.com/Unitech/pm2/commit/e868f003e3063a57236cb8d0ead33af808e0df70))\n  - Merge pull request #3872 from Unitech/column_id_stacked\n  ([55b6ccc3](https://github.com/Unitech/pm2/commit/55b6ccc32ae02e574ec1f80a36b4531761b94777))\n  - Merge pull request #3723 from livankrekh/development\n  ([98f49dc3](https://github.com/Unitech/pm2/commit/98f49dc393efd1fed03a1ef8a5752c0e490dd4b8))\n  - Merge pull request #3821 from imarakho/post_install\n  ([4217b150](https://github.com/Unitech/pm2/commit/4217b1505419904252d0ae7640a51128a2459d98))\n  - Merge pull request #3823 from imarakho/flag--ext\n  ([cc68dc1f](https://github.com/Unitech/pm2/commit/cc68dc1f9faf010af0648992193230af609413c5))\n  - Merge pull request #3822 from imarakho/flush_parameter\n  ([bbcc85a4](https://github.com/Unitech/pm2/commit/bbcc85a41683f5fa573bf504894f8e817c89784a))\n  - Merge pull request #3807 from medanat/minimize-async-lib-footprint\n  ([7e92855f](https://github.com/Unitech/pm2/commit/7e92855ff5c394b5452db526d21262e343b89ef8))\n  - Merge pull request #3829 from soyuka/patch-pidusage\n  ([a668f576](https://github.com/Unitech/pm2/commit/a668f5762190061dd05de5c5d888b53f35fa386e))\n\n\n\n\n\n\n\n## 3.0.3 ( Tue Aug 07 2018 23:35:05 GMT+0200 (CEST) )\n\n\n## Bug Fixes\n  - pm2 plus + register\n  ([277ec6ba](https://github.com/Unitech/pm2/commit/277ec6ba8d1cdda7f8fdf11eb9d9d33c2c095d65))\n\n\n\n\n## 3.0.2 ( Tue Aug 07 2018 23:35:05 GMT+0200 (CEST) )\n\n\n## Bug Fixes\n  - allow tracing activation\n  ([f297ef1e](https://github.com/Unitech/pm2/commit/f297ef1ebbec292aedcfa48c27e3f31b8f206633))\n\n\n\n\n## Branchs merged\n  - Merge branch 'development'\n  ([80c94dd3](https://github.com/Unitech/pm2/commit/80c94dd3261544f627612ce4b541356e4adbc51f))\n\n\n\n\n## 3.0.1 ( Mon Jul 23 2018 14:13:35 GMT+0200 (CEST) )\n\n## Bug Fixes\n  - allow to set a name via pm2 link\n  ([ebffb609](https://github.com/Unitech/pm2/commit/ebffb609cf4da195c72ee67d8341c63b78f0654e))\n  - disable network monitoring as long as ampq not supported\n  ([ae1547bf](https://github.com/Unitech/pm2/commit/ae1547bfa9505b2d13e30df39ce614eee29463b0))\n  - display error message from pm2-deploy\n  ([9171b810](https://github.com/Unitech/pm2/commit/9171b81024641c3e104f3eeb2e2c6eb852dbe7f4))\n  - protect geteuid/getegid from being called on windows #3793\n  ([0495bd8e](https://github.com/Unitech/pm2/commit/0495bd8e4ffaeb1db729b35fa569696145d79c5f))\n  - put message module at the right level\n  ([56f5e047](https://github.com/Unitech/pm2/commit/56f5e04787da29e8b582bf4fa8325f72404a2fbe))\n  - do not ignore child pres folder\n  ([10ee9987](https://github.com/Unitech/pm2/commit/10ee99876d75679723e1e8522da07413a618e48c))\n  - let->var\n  ([89e2a125](https://github.com/Unitech/pm2/commit/89e2a125c22aee27014c279c86d1d9e0a0df0235))\n  - method renaming\n  ([f3faa3d8](https://github.com/Unitech/pm2/commit/f3faa3d846d1e895232743dd619f5ecb15fdf7ad))\n  - path\n  ([4f980550](https://github.com/Unitech/pm2/commit/4f9805508d2c1c575aabc4abbab25728f1c6a28a))\n  - #3791 mitigate pidusage errores\n  ([88551b8c](https://github.com/Unitech/pm2/commit/88551b8cfe8bf8dd330d582e71b808faadfaf161))\n  - pm2 plus\n  ([9bc34e56](https://github.com/Unitech/pm2/commit/9bc34e56b7ad66cbc6efbd26d4017f1e1813a720))\n  - #3764\n  ([3a582b42](https://github.com/Unitech/pm2/commit/3a582b42f9cca57779b99964c95a2cd0516efa11))\n  - drop coffee-script (installed via pm2 install coffeescript)\n  ([76ceb2fd](https://github.com/Unitech/pm2/commit/76ceb2fd52a2e5acbf03deacc3fa8a120a197023))\n  - restore no_interaction for pm2-dev\n  ([902e5a5a](https://github.com/Unitech/pm2/commit/902e5a5a1225d2072ab6337aa067caf9c6a7cca4))\n  - option -w doesn't work\n  ([165a05c8](https://github.com/Unitech/pm2/commit/165a05c854f9b3dd1418b988c954d333f81ba88f))\n  - retab shell script to use space for indent consistency\n  ([e3b4327d](https://github.com/Unitech/pm2/commit/e3b4327d9a6120c5ad589734ca926d3b49a8b706))\n  - set Makefile indent to tab instead of common space\n  ([4db0ae01](https://github.com/Unitech/pm2/commit/4db0ae011c161cbfca9e250da40deff9fdc36069))\n  - set yaml file indent to 2 spaces instead of 3\n  ([e4ecb0b2](https://github.com/Unitech/pm2/commit/e4ecb0b29dbcc4c6ca2d67b6bdc7da4c0a5d17a5))\n  - remove trailing spaces\n  ([5c115983](https://github.com/Unitech/pm2/commit/5c1159832680231bff5da79f1c91caf32ce3b5e0))\n  - fixes #3735\n  ([0548cb82](https://github.com/Unitech/pm2/commit/0548cb82aa1193a5725ca22e1babfc38db2e3b77))\n\n\n\n\n## Hot Fixes\n  - fix #3767, do not consider as a command if space and slash are found\n  ([d15a12ce](https://github.com/Unitech/pm2/commit/d15a12ceae8b0c9c27625180ae002178b0bfe5d0))\n  - fix #3767, do not consider as a command if space and slash are found\n  ([f8ec1503](https://github.com/Unitech/pm2/commit/f8ec1503c3e92bc0dec10d395ac682b116e2914e))\n\n\n\n\n## Features\n  - add inspector for node 10 and heap snapshot\n  ([dc61bca6](https://github.com/Unitech/pm2/commit/dc61bca66828c16cf6fd04a6f749f127da697cec))\n  - pm2 plus xx yy now generates a name with hostname-UID\n  ([fcf75e2c](https://github.com/Unitech/pm2/commit/fcf75e2cc321791273f6afe86c07fd147c6e8414))\n  - #3757 --only='app1,app2'\n  ([bea98330](https://github.com/Unitech/pm2/commit/bea983306c4736d3a2b1090f2708b7b29c44ed03))\n  - pm2 plus cli\n  ([1da6edde](https://github.com/Unitech/pm2/commit/1da6edde80e3029d99084992ec1a4ada7b2cc279))\n  - reload all apps after connection to pm2 plus\n  ([35a1ed2a](https://github.com/Unitech/pm2/commit/35a1ed2a1328a859a7797ec8e22024d171599d86))\n  - ask to install module after connection with KM\n  ([68e87b39](https://github.com/Unitech/pm2/commit/68e87b39ae2b57e9fbb0b0abde68112c839f05ee))\n  - with pm2 plus command ask to install modules\n  ([28c61716](https://github.com/Unitech/pm2/commit/28c61716ee5e8f2402205e4b06ed7ee0a942a3cc))\n\n\n\n\n## Test\n  - test with development packages\n  ([d361c840](https://github.com/Unitech/pm2/commit/d361c8405db47969bd68c7b1058a54f38e8e0e52))\n\n\n\n\n## Chore\n  - clean old snapshot method\n  ([d064750b](https://github.com/Unitech/pm2/commit/d064750be0d437945efdcd6a5ce4e56547b1bce6))\n  - update version to 3.0.1\n  ([efbcb021](https://github.com/Unitech/pm2/commit/efbcb02180ae38dd930e43282113dbcb24288eab))\n  - bump to 3.0.1\n  ([fb8357e3](https://github.com/Unitech/pm2/commit/fb8357e32f9f015e5b6e7ed8ef150f59de382c6d))\n  - new ascii logo + refactor pm2 plus command\n  ([8692a1da](https://github.com/Unitech/pm2/commit/8692a1daf7b4b7dfb8a4d6ec3363ac0cc62203a8))\n  - change motd.update + alias register to pm2 plus\n  ([cdc4a767](https://github.com/Unitech/pm2/commit/cdc4a767d5f1ff5873d0466b471daa3006608604))\n  - btn\n  ([319fa0dc](https://github.com/Unitech/pm2/commit/319fa0dcbea331a88a9888c207368e52665309ce))\n  - README button\n  ([1c6fb68c](https://github.com/Unitech/pm2/commit/1c6fb68c758d76cf81e53c43c2423ecd742265e5))\n  - remove duplicate configs in .editorconfig\n  ([86ad52b8](https://github.com/Unitech/pm2/commit/86ad52b837e23a7ec92705d21a152394c244571f))\n\n\n\n\n## Branchs merged\n  - Merge branch 'development' into uid-gen\n  ([5324c878](https://github.com/Unitech/pm2/commit/5324c878fd0d37e068bc25c8e37f19f73bfebf30))\n  - Merge branch 'master' into development\n  ([7d04f638](https://github.com/Unitech/pm2/commit/7d04f63835845e92d32d6ad7ffab166a2954302f))\n\n\n\n\n## Pull requests merged\n  - Merge pull request #3811 from Unitech/memory_inspector\n  ([62018044](https://github.com/Unitech/pm2/commit/62018044d7a1ef7fd0b37fe3082da4bf05989de0))\n  - Merge pull request #3801 from vkotovv/grammar-fixes\n  ([9bb37a66](https://github.com/Unitech/pm2/commit/9bb37a662a91369caaa5a1a43751541e41970a51))\n  - Merge pull request #3799 from Unitech/refactor-agent\n  ([bcc4fea8](https://github.com/Unitech/pm2/commit/bcc4fea80885ce941e11b17936aab6582660fc7f))\n  - Merge pull request #3787 from Unitech/multi-only\n  ([ea5d74a8](https://github.com/Unitech/pm2/commit/ea5d74a87f6911b238634419665c716bc877be10))\n  - Merge pull request #3788 from Unitech/uid-gen\n  ([f70444f3](https://github.com/Unitech/pm2/commit/f70444f39b7cc8fe05faf57dac1b46fc15a2053c))\n  - Merge pull request #3784 from Unitech/pm2-plus-cli\n  ([e8c13c37](https://github.com/Unitech/pm2/commit/e8c13c374dfeabf42f75af50b838adb7ac4a50aa))\n  - Merge pull request #3780 from Unitech/plus_modules\n  ([466d2701](https://github.com/Unitech/pm2/commit/466d2701ca48d0c4b8466d6867135e43b22deeb5))\n  - Merge pull request #3768 from Unitech/spaces\n  ([0477354b](https://github.com/Unitech/pm2/commit/0477354b502aef612012e833bd47ce1940da1a0b))\n  - Merge pull request #3771 from chinesedfan/patch-2\n  ([8de987a6](https://github.com/Unitech/pm2/commit/8de987a604679774ec39e7d5a1a905556524c53d))\n  - Merge pull request #3762 from shaharmor/issue-3441\n  ([429e455d](https://github.com/Unitech/pm2/commit/429e455db96d2a56448a11b7602333324c9bf433))\n  - Merge pull request #3761 from PeterDaveHello/fix-sh-indent-style\n  ([24cddc25](https://github.com/Unitech/pm2/commit/24cddc257734beebb33ee5abac5a4107a5d86093))\n  - Merge pull request #3737 from morugu/add-node-env-output\n  ([6628f163](https://github.com/Unitech/pm2/commit/6628f1637497771bbc5c4f0ba0e9423c63660e0e))\n  - Merge pull request #3743 from vivex/master\n  ([06872c25](https://github.com/Unitech/pm2/commit/06872c2520f73bcabb6198a96c4dafb46706c9e9))\n  - Merge pull request #3748 from JimiC/support_nvm4win\n  ([2dac235b](https://github.com/Unitech/pm2/commit/2dac235bc8956d170fee2341517739d3781048d7))\n  - Merge pull request #3752 from PeterDaveHello/upstart.tpl\n  ([d4e66e3a](https://github.com/Unitech/pm2/commit/d4e66e3a9d954ab5c15d5bc35910cdfb71ba8321))\n  - Merge pull request #3753 from PeterDaveHello/fix-editorconfig\n  ([d1478680](https://github.com/Unitech/pm2/commit/d1478680325822c206afbcb197a9a732318f6d64))\n  - Merge pull request #3754 from PeterDaveHello/remove-trailing-space\n  ([b660f03e](https://github.com/Unitech/pm2/commit/b660f03eba71bb80a1a3d313be4525160727921f))\n\n\n\n\n\n\n## 3.0.0 ( Wed Jun 20 2018 11:06:21 GMT+0200 (CEST) )\n\n\n## Breaking changes\n  - merge_logs is now activated by default if not in cluster mode. Logs will not be suffixed by the pm_id if only one app is started\n  ([ae02adf6](https://github.com/Unitech/pm2/commit/ae02adf63f70ceb3bf101be968996ca68d9ce277))\n  - Drop support for node 0.12\n  - Drop gracefulReload command\n  - Remove Interactor from PM2 source code\n  - Replace pmx with [pm2-io-apm](https://github.com/keymetrics/pm2-io-apm)\n\n\n## Bug Fixes\n  - return the configuration and allow custom conf to override default values\n  ([37dc7de1](https://github.com/Unitech/pm2/commit/37dc7de11e930aa4fce6a485e892f11ee714acd6))\n  - add use strict for node 4 compatibility\n  ([ba2ee3b1](https://github.com/Unitech/pm2/commit/ba2ee3b1ea9aa5fa665e706b3d49a205eac44d53))\n  - #3605 fix parameters definition, don't use camelcase for properties\n  ([c8616276](https://github.com/Unitech/pm2/commit/c8616276e4e08b4d90a742e219372e775bb81098))\n  - #3695 change version check method in order to make it work with alpha/beta versions\n  ([052d6c55](https://github.com/Unitech/pm2/commit/052d6c55df0e941e1dd11430bbcbcaa34061a06e))\n  - deprecated warning on isbinaryfile\n  ([db09275f](https://github.com/Unitech/pm2/commit/db09275f8e353e257c89e12fed754236b15cee74))\n  - #3688 test adaptation + pm2 serve --port option\n  ([f0249684](https://github.com/Unitech/pm2/commit/f0249684bcbfdb75749a516f447c8e8d32020709))\n  - startup script issue 18.04 #3645\n  ([ff1a7f31](https://github.com/Unitech/pm2/commit/ff1a7f315bfee38eb9fd9cdd63efcc0d971585f8))\n  - that this - uncache node_modules\n  ([294038d7](https://github.com/Unitech/pm2/commit/294038d76272a915e3addc67d3694717a9f7d704))\n  - verify default conf variable via package.json on public module\n  ([157b106d](https://github.com/Unitech/pm2/commit/157b106df78af1d28d37bbea069b926de4dceca5))\n  - bug because of const\n  ([56f05a90](https://github.com/Unitech/pm2/commit/56f05a900b03fb0c8dd635aede666c7d2f213271))\n  - do not run two pm2 para cmds\n  ([3274132b](https://github.com/Unitech/pm2/commit/3274132b866ba5c93d5786e755acbada922f5f1e))\n  - version\n  ([3ec178e5](https://github.com/Unitech/pm2/commit/3ec178e577e79730aae02c913301cd905ea8ce52))\n  - re-enable agent tests\n  ([e6febcd7](https://github.com/Unitech/pm2/commit/e6febcd70dd0f1e68b74df8563d3046ee3b32b89))\n  - test/display summary\n  ([b075e6d0](https://github.com/Unitech/pm2/commit/b075e6d09b09ff371adf045dc5079bb8ef82f1cf))\n  - skip interactor tests\n  ([36c4d6bc](https://github.com/Unitech/pm2/commit/36c4d6bca7445b46afc1236dc8ab4b8bf921148b))\n  - remove unused tests\n  ([234c6314](https://github.com/Unitech/pm2/commit/234c63143e723a508796bc1d323c7241979bf4c2))\n  - add missing libraries in travis\n  ([88fbb845](https://github.com/Unitech/pm2/commit/88fbb84597cee7029ce33f5b7e20e45f5a815b4b))\n  - remove unused variable when trying to use tracing\n  ([3aeeba02](https://github.com/Unitech/pm2/commit/3aeeba02f628bf4f19e8d5b93657fd94a6ef0ec7))\n  - remove useless tests from .sh\n  ([e0be81c8](https://github.com/Unitech/pm2/commit/e0be81c86c7defb5e7a271edd5cc37f960c6aa69))\n  - conflict\n  ([e13f39c9](https://github.com/Unitech/pm2/commit/e13f39c90b6a5e803c59c5424332520564703f5c))\n  - fix bug with interpreter args\n  ([b26efa0d](https://github.com/Unitech/pm2/commit/b26efa0d4cd72cf04762df7b7d2eaddc4f4117d2))\n  - improve error message if action has failed\n  ([d9f44f17](https://github.com/Unitech/pm2/commit/d9f44f170f115c2d6dfb6a7fe71dc31bd7fb66fb))\n  - use polyfill module for copySync with node 4.x\n  ([bc07f43b](https://github.com/Unitech/pm2/commit/bc07f43b115066f6077606df8f59379777f2a917))\n  - improve error message if action has failed\n  ([dacc6542](https://github.com/Unitech/pm2/commit/dacc654207cbe494af0d12a3f9f27c3b16541802))\n  - solve empty list when no process and try to update pm2\n  ([89511846](https://github.com/Unitech/pm2/commit/8951184688c720ded5b4b46bd5b393c3793f9b03))\n  - #3485 fix issue when there is empty dump file\n  ([f2523f6a](https://github.com/Unitech/pm2/commit/f2523f6a6b9d8b61ba6ace7b89a0353bee76360b))\n  - #3456 use homedir() instead of process.env.HOME, make module installation work on windows\n  ([1e001732](https://github.com/Unitech/pm2/commit/1e0017325fc8cf658263fb4e02c7bf8912f422b3))\n\n\n\n\n## Features\n  - add support for openbsd rc.d init scripts\n  ([fdeb0c32](https://github.com/Unitech/pm2/commit/fdeb0c327afd91b113b214c4c4de187848f9f1cb))\n  - add kill_retry_time argument\n  ([b2cc0031](https://github.com/Unitech/pm2/commit/b2cc003114b44f1a9a31876ee4a2f4cb91e210b3))\n\n  - **bin/pm2**\n    - improve usage\n  ([2c310084](https://github.com/Unitech/pm2/commit/2c310084453dd7b1546957e59b1fc7ef964d425b))\n\n\n\n\n## Refactor\n  - use @pm2/js-api for login/register on pm2.io via CLI\n  ([cb6521ac](https://github.com/Unitech/pm2/commit/cb6521ac32f4737c42fc97fef972960bfe16c829))\n  - keymetrics examples\n  ([109b331d](https://github.com/Unitech/pm2/commit/109b331ddf37e061d1890ef952f4cd167ce53f64))\n  - faster cli with less require\n  ([ee5e6a06](https://github.com/Unitech/pm2/commit/ee5e6a06cbf93f2d1fa7fa022d6bdcad55a39695))\n  - replace fs-extra with node calls\n  ([4576b4c9](https://github.com/Unitech/pm2/commit/4576b4c97bc685c9d774018d6b29c918abd7cb8d))\n  - centralize SECRET/PUBLIC/MACHINE_NAME + change some wordings\n  ([d0a2a30e](https://github.com/Unitech/pm2/commit/d0a2a30e4110496b178199fb33e026d6402dd00d))\n  - remove test deported to keymetrics-agent\n  ([299a52a2](https://github.com/Unitech/pm2/commit/299a52a253d70edcde23cbd7e0c201d492984df4))\n  - parallel test v1\n  ([08612de5](https://github.com/Unitech/pm2/commit/08612de5b7893a004ae33ed77fcb2ee3ff7b2251))\n  - e2e test rewrite\n  ([2b9ffd4e](https://github.com/Unitech/pm2/commit/2b9ffd4eb493f1ff32c979e3811f4f1fedfae97d))\n  - drop gracefullreload\n  ([bb57c76d](https://github.com/Unitech/pm2/commit/bb57c76d4191343925013d4353299092d80732c9))\n  - add node 4.x support\n  ([d322dd00](https://github.com/Unitech/pm2/commit/d322dd00de0f527224c027b4fec5e86f12fd69ed))\n  - create alias method instead of modify prototype\n  ([6d8f0dfa](https://github.com/Unitech/pm2/commit/6d8f0dfae8106deb2fee0a7ae15b6ca9802a066d))\n  - change safety var to const\n  ([047aa494](https://github.com/Unitech/pm2/commit/047aa494d5c4dd4342915766b54d673db0d5cdf1))\n  - drop some 0.x patch\n  ([0cab8880](https://github.com/Unitech/pm2/commit/0cab8880ffa362cf27ab7d7b6a64d6b478dce7cd))\n  - remove prototype from API and create method\n  ([9552bd61](https://github.com/Unitech/pm2/commit/9552bd61b72692beb620a91765ad440cdf6abefe))\n  - transform API into class\n  ([e3831f95](https://github.com/Unitech/pm2/commit/e3831f95c8d71f98e8840da37f7e883727eccd59))\n  - name tests well\n  ([c3ccc651](https://github.com/Unitech/pm2/commit/c3ccc651d09ed7291090f516637b75bda99ff71c))\n  - refactor e2e one line parallel\n  ([93802711](https://github.com/Unitech/pm2/commit/938027117cdb2f300ee772ab27f008cbe22a4b19))\n  - e2e rename\n  ([8a7db95a](https://github.com/Unitech/pm2/commit/8a7db95aabc8437f292af0316cec81ab80ec41f5))\n  - change params\n  ([282186f2](https://github.com/Unitech/pm2/commit/282186f24b19b010999f7c7c49750935ef19c190))\n  - parallelize bash test\n  ([d4b4375e](https://github.com/Unitech/pm2/commit/d4b4375e16fe7ac463b252702da662d3a21bf8b4))\n\n\n\n\n## Test\n  - adapt test to new api\n  ([7a275e27](https://github.com/Unitech/pm2/commit/7a275e279ea01b1239e9dd8b9cf8e088e407b96d))\n  - refactor before/after\n  ([b85ca3ca](https://github.com/Unitech/pm2/commit/b85ca3caa3c68e18f7ce6954cc85e90a9d33efef))\n  - 3 concurrent jobs\n  ([472aba34](https://github.com/Unitech/pm2/commit/472aba3499ff2d9d0eb834e819410026b1a44503))\n  - move test\n  ([9c973324](https://github.com/Unitech/pm2/commit/9c9733246dbe6afff1b488bc3ba3b6fea3877ea5))\n  - move test\n  ([952b7631](https://github.com/Unitech/pm2/commit/952b7631d19e1074ea73cc7a67bbaefe20950603))\n  - fix test with km_link\n  ([23fd8ecf](https://github.com/Unitech/pm2/commit/23fd8ecfea9b2bf61359f62a8e6e1a582c3b0d6e))\n\n\n\n\n## Chore\n  - shorten ecosystem file\n  ([992a0452](https://github.com/Unitech/pm2/commit/992a045227aed559e708ac4e6bb3f54beabe48e0))\n  - change motd wording\n  ([aa183ba1](https://github.com/Unitech/pm2/commit/aa183ba19d88777d82619aa40499c2661d67879e))\n  - merge master in development\n  ([0e4453d9](https://github.com/Unitech/pm2/commit/0e4453d9cc789aa08ee778ff400572337e90d2e3))\n  - keymetrics -> pm2\n  ([2c8170c2](https://github.com/Unitech/pm2/commit/2c8170c25e231eb8827bb0944b76c2f4b041d84e))\n  - upgrade all modules + keymetrics-agent -> pm2/agent + increase version enabling v8-compile-cache\n  ([53ca18c1](https://github.com/Unitech/pm2/commit/53ca18c12868ab177b60a4edff2ccaa8127e301f))\n  - pm2.io -> @pm2/io\n  ([ae098962](https://github.com/Unitech/pm2/commit/ae098962df35eee7f482dc0a514fd29a02a5f4ad))\n  - right names as pm2 maintainers\n  ([e8cd7131](https://github.com/Unitech/pm2/commit/e8cd7131a6b9c9d497a2079bcbfc03770a753a06))\n  - add changelog generation into contributing.md\n  ([d77bfbc3](https://github.com/Unitech/pm2/commit/d77bfbc3c8929851ee19ea604b2a6481d03771e3))\n  - cache node_modules\n  ([81627e94](https://github.com/Unitech/pm2/commit/81627e94c72efa1f4d726e20bbf67f0bbd5c116f))\n  - clone last 5 commits\n  ([dad38ed1](https://github.com/Unitech/pm2/commit/dad38ed1bae849147f66e44186cd71c4b9cb022d))\n  - delete old stagnating pmx inside test\n  ([36834c2c](https://github.com/Unitech/pm2/commit/36834c2c00d496e04c38abaca30202eb650015c4))\n  - pmx -> pm2.io\n  ([adcbebc3](https://github.com/Unitech/pm2/commit/adcbebc3f6419cd97c5ea99f3c3a6789585bda66))\n  - updgrade pmx-2\n  ([eeeb2988](https://github.com/Unitech/pm2/commit/eeeb2988f8886e405aea107db3b888fc1fc929f8))\n  - disable legacy test\n  ([13723bd9](https://github.com/Unitech/pm2/commit/13723bd938d0e6fb1cbf35f15eabe91c52d87b58))\n  - remove test for pmx alert system\n  ([c43414a6](https://github.com/Unitech/pm2/commit/c43414a63438d724b8099eb531ec72bab23b8ca2))\n  - sync from master\n  ([3424ee27](https://github.com/Unitech/pm2/commit/3424ee27870feaf62fdf4509cce9015f8b1a8a2e))\n  - add unique id for each process\n  ([85a5ee0f](https://github.com/Unitech/pm2/commit/85a5ee0f1fd16da9635fb4b16ddcd8d53aca8224))\n  - use npm install for CI as yarn has issue with npm\n  ([52902186](https://github.com/Unitech/pm2/commit/5290218626af815f6cae8173bc78d21881a4dda8))\n  - remove unused dependency\n  ([830fc15f](https://github.com/Unitech/pm2/commit/830fc15fad1aee95e65b2681482b03369f1f97d7))\n  - upgrade PM2 to 3.0\n  ([4bc2eb4c](https://github.com/Unitech/pm2/commit/4bc2eb4c9a8179b9ae38438e98ce7650a91b64db))\n  - remove unused console.log\n  ([33db5084](https://github.com/Unitech/pm2/commit/33db5084814ae7940c90b7f933f9514d28008b78))\n  - wording on error message\n  ([c251c8c9](https://github.com/Unitech/pm2/commit/c251c8c97e6f18aae584cac6b7f3c83cf4f2de9c))\n  - revert PR #3496\n  ([aae1d55e](https://github.com/Unitech/pm2/commit/aae1d55e410c4dcfbbca83eaabbdf1a65d55f3aa))\n  - fix issue with snapshot command + remove command forceGc\n  ([97fd1010](https://github.com/Unitech/pm2/commit/97fd1010d005e59f2411042fa95891f9717fa8b7))\n  - wording on error message\n  ([5f78ecbf](https://github.com/Unitech/pm2/commit/5f78ecbf90f9f46a7feb2a169968e86b0ecac91e))\n  - drop 0.12 test on travis\n  ([beb6e487](https://github.com/Unitech/pm2/commit/beb6e48787c39c66569141d0fd8d090736114d23))\n  - downgrade promptly\n  ([074a7a40](https://github.com/Unitech/pm2/commit/074a7a407a31b4d88442f5834d253d62f4e543b8))\n  - remove coffee and livescript dependencies\n  ([13d6565c](https://github.com/Unitech/pm2/commit/13d6565c72e3596d05f87bfc8be15d3ee45fb279))\n  - upgrade module version and engine version\n  ([84796956](https://github.com/Unitech/pm2/commit/84796956347ca638750fe89cb5545e2a90a0f2c2))\n\n\n\n\n## Branchs merged\n  - Merge branch 'development' into chore/dev-cache-node-modules\n  ([146c4e11](https://github.com/Unitech/pm2/commit/146c4e113c88e8ade17c7558c8e14cf523a3b2d6))\n  - Merge branch 'development' of https://github.com/Unitech/pm2 into new-agent\n  ([3514e7fa](https://github.com/Unitech/pm2/commit/3514e7fac624bb83b4cc22651ebc05385f9c284d))\n  - Merge branch 'development' into master\n  ([f5668331](https://github.com/Unitech/pm2/commit/f5668331dbe7346304258317a3b84450f421ed03))\n  - Merge branch 'development' into new-usage-cli\n  ([4ae27694](https://github.com/Unitech/pm2/commit/4ae27694e34c4bc6ed389566d71fc5ec48b69652))\n  - Merge branch 'Eywek-improv/agent' into new-agent\n  ([3e259dd1](https://github.com/Unitech/pm2/commit/3e259dd1d6bb96ea41897c49f3a84557c00c7dad))\n  - Merge branch 'ecosystem-documentation' of github.com:rmonnier/pm2 into ecosystem-documentation\n  ([98348955](https://github.com/Unitech/pm2/commit/98348955a6eb3a9cd524b991bd1dd6ed03d2c857))\n  - Merge branch 'development' into ecosystem-documentation\n  ([40157784](https://github.com/Unitech/pm2/commit/40157784a63bcb0e744d4ed56f6c687e28379fdd))\n  - Merge branch 'inspect_mode' of github.com:Unitech/pm2 into inspect_mode\n  ([7e1494c7](https://github.com/Unitech/pm2/commit/7e1494c7f7971aaf1f4d00d2ee691c3c41775001))\n  - Merge branch 'development' of github.com:Unitech/pm2 into development\n  ([48f81a8b](https://github.com/Unitech/pm2/commit/48f81a8b2f6f0db39edd86083fb369b74845c387))\n  - Merge branch 'development' into master\n  ([47e54109](https://github.com/Unitech/pm2/commit/47e5410987ab3d824a34c062d70c24ab686e57db))\n  - Merge branch 'development' into module_install_windows\n  ([7b82fb91](https://github.com/Unitech/pm2/commit/7b82fb916ed453c1c263bae43c962f6a5294d810))\n  - Merge branch 'development' into module_install_windows\n  ([80b0495f](https://github.com/Unitech/pm2/commit/80b0495f63d1224b850af4b14cdeb055e3fef50b))\n\n\n\n\n## Pull requests merged\n  - Merge pull request #3726 from soyuka/fix-list\n  ([0255c5a6](https://github.com/Unitech/pm2/commit/0255c5a6ab1b8a8f609d2183d998695b8c42838d))\n  - Merge pull request #3725 from soyuka/fix-list\n  ([a39eb4f8](https://github.com/Unitech/pm2/commit/a39eb4f806e87565f53758a19f0ee289b6489b67))\n  - Merge pull request #3718 from AaronM04/openbsd-init-script\n  ([85458261](https://github.com/Unitech/pm2/commit/85458261d2673c609cb252d64ad4dfbaa466d848))\n  - Merge pull request #3721 from Unitech/io_conf\n  ([70ec1f81](https://github.com/Unitech/pm2/commit/70ec1f81eae089f75e82723fde7b0b3926d0a9bc))\n  - Merge pull request #3716 from Unitech/io_conf\n  ([0bc000b9](https://github.com/Unitech/pm2/commit/0bc000b9aae7dd37b456bc2d4fbc9eb4a9f047ef))\n  - Merge pull request #3714 from Unitech/definition\n  ([d8cff0de](https://github.com/Unitech/pm2/commit/d8cff0dec5160a620d1512ff56726c073368d1a4))\n  - Merge pull request #3700 from Unitech/report_error\n  ([4b2cad40](https://github.com/Unitech/pm2/commit/4b2cad407b76994e978074a2a3825fe70656304d))\n  - Merge pull request #3670 from Unitech/changelog\n  ([4bcbcce1](https://github.com/Unitech/pm2/commit/4bcbcce16ced596f6ca2bab2b77d608a174a7c1a))\n  - Merge pull request #3662 from DanielRuf/chore/dev-cache-node-modules\n  ([540590ee](https://github.com/Unitech/pm2/commit/540590ee056b44eed3b688a7b0b16ca78ec82cd9))\n  - Merge pull request #3663 from DanielRuf/chore/dev-clone-last-5-commits\n  ([bdf95fc9](https://github.com/Unitech/pm2/commit/bdf95fc997f9ab2995b23668f25f11b6e98b5c47))\n  - Merge pull request #3584 from ngtmuzi/development\n  ([33984b64](https://github.com/Unitech/pm2/commit/33984b64a2969ca4a3a5913f0f7da0242b6c5ec1))\n  - Merge pull request #3500 from Unitech/test-parallel\n  ([da56c7af](https://github.com/Unitech/pm2/commit/da56c7aff18d3a38b3ad068b22cd75b290bac9d0))\n  - Merge pull request #3539 from KimSeongIl/master\n  ([1325704d](https://github.com/Unitech/pm2/commit/1325704d95d324e56b0ebc86aed8137e0d0aa450))\n  - Merge pull request #3556 from N-Nagorny/logs-smart-app-name-cutting\n  ([bfddf4fd](https://github.com/Unitech/pm2/commit/bfddf4fdef5ec293119d850cc2532ac5d6490ae3))\n  - Merge pull request #3553 from Unitech/fix_tracing_not_working\n  ([9d51fe08](https://github.com/Unitech/pm2/commit/9d51fe0819182339f3a6a4aee7ea603ea3f4dd76))\n  - Merge pull request #3549 from Eywek/new-agent\n  ([2f04027b](https://github.com/Unitech/pm2/commit/2f04027b536094d192b399677b3a113102f06b8e))\n  - Merge pull request #3548 from rmonnier/start-ecosystem-default\n  ([55412f26](https://github.com/Unitech/pm2/commit/55412f263250395de0085144932cfe06b8c7180d))\n  - Merge pull request #3546 from soyuka/improve-monitor-perf\n  ([e4e29233](https://github.com/Unitech/pm2/commit/e4e29233f99db36462a6e8f48eb8ebd3d2fd9fa5))\n  - Merge pull request #3534 from rmonnier/new-usage-cli\n  ([5dfba8a4](https://github.com/Unitech/pm2/commit/5dfba8a4491f0bb83f2879915f0c4b164be2552c))\n  - Merge pull request #3542 from rmonnier/default-start-ecosystem\n  ([c65595f4](https://github.com/Unitech/pm2/commit/c65595f4a70659e1e0d753e6c28a1fcedf45a91a))\n  - Merge pull request #3545 from rmonnier/default-ecosystem\n  ([b3718656](https://github.com/Unitech/pm2/commit/b3718656f630aa54880343d9742534a2a508daec))\n  - Merge pull request #3543 from rmonnier/ecosystem-documentation\n  ([a60580a1](https://github.com/Unitech/pm2/commit/a60580a12b4a0066c8df6620317fbc8bf599b0b6))\n  - Merge pull request #3541 from soyuka/development\n  ([67e7a015](https://github.com/Unitech/pm2/commit/67e7a015cabaa7b08206a3b1bf9c0399af88f76b))\n  - Merge pull request #3511 from Unitech/inspect_mode\n  ([75fb87f8](https://github.com/Unitech/pm2/commit/75fb87f8a1c46a6db8e974b421e857175e69b535))\n  - Merge pull request #3517 from Unitech/polyfill_fs_copy_node4\n  ([524f5494](https://github.com/Unitech/pm2/commit/524f54948de5080632d43bb512038d7bd7271619))\n  - Merge pull request #3516 from Unitech/drop_unused_feature\n  ([9436f11a](https://github.com/Unitech/pm2/commit/9436f11aeecfc07e77aa9d6b108df4478b43402e))\n  - Merge pull request #3510 from Unitech/dump_refacto\n  ([674e4469](https://github.com/Unitech/pm2/commit/674e4469554e6a765bb3d57a3c083e6ab53b20cc))\n  - Merge pull request #3501 from Unitech/refactor_api\n  ([9f2c4ca4](https://github.com/Unitech/pm2/commit/9f2c4ca4c9eadf6c7730e3889c72e908cd2d8f5d))\n  - Merge pull request #3496 from rmonnier/master\n  ([829cc303](https://github.com/Unitech/pm2/commit/829cc3032b2d61e20f7a2e7d1d819c0ddc0845e8))\n  - Merge pull request #3484 from Unitech/pull_by_name\n  ([24d29404](https://github.com/Unitech/pm2/commit/24d294049008a0d01b2bc407b9b2b880d5843fbd))\n  - Merge pull request #3482 from Unitech/mjs_support\n  ([ebe7b048](https://github.com/Unitech/pm2/commit/ebe7b0487218557858aaa98527360eca1776b140))\n  - Merge pull request #3495 from Unitech/module_install_windows\n  ([e9c625d3](https://github.com/Unitech/pm2/commit/e9c625d3088c71eef4237ecd866b806957c61815))\n  - Merge pull request #3507 from cheapsteak/patch-1\n  ([a49287d6](https://github.com/Unitech/pm2/commit/a49287d6a1d22b39270e2d05dee2a17c0ed55797))\n\n\n\n\n## 2.10.4 ( Thu May 17 2018 14:32:40 GMT+0200 (CEST) )\n\n\n## Bug Fixes\n  - #3645 throttle startup\n  ([d529f675](https://github.com/Unitech/pm2/commit/d529f675d0240777cba95442ba35205c370cdb43))\n\n\n\n\n## Chore\n  - update issue and PR templates to use comments to hide instructions in the frontend\n  ([9e0180ed](https://github.com/Unitech/pm2/commit/9e0180eddab071916144ad7008817bd6aef1c8ce))\n\n\n\n\n## Pull requests merged\n  - Merge pull request #3664 from DanielRuf/chore/update-issue-pr-templates\n  ([067446f2](https://github.com/Unitech/pm2/commit/067446f2133ba7f761b0ad3c9f3692b167affd8b))\n\n\n## v2.10.3 ( Fri Apr 27 2018 11:42:16 GMT+0200 (CEST) )\n\n\n### Chore\n  - upgrade for node 10\n  ([cf7630e](https://github.com/Unitech/pm2/commit/cf7630e259742bdff8257cff4dbed2732bf24f9c))\n\n## v2.10.2 ( Thu Mar 29 2018 13:06:11 GMT+0200 (CEST) )\n\n\n## Bug Fixes\n  - reinforce pm2-runtime auto exit strategy #3567 #3206\n  ([e09cdbab](https://github.com/Unitech/pm2/commit/e09cdbabd0b479acda3cb24154bbaa071aa35407))\n\n\n\n\n## Pull requests merged\n  - Merge pull request #3569 from Unitech/pm2-runtime-hot-fix\n  ([473a2d6d](https://github.com/Unitech/pm2/commit/473a2d6d3867c617e4a41571d1780618c5025b87))\n  - Merge pull request #3547 from Unitech/revert-3532-logs-smart-app-name-cutting\n  ([438e3030](https://github.com/Unitech/pm2/commit/438e303013e82ecc199cb68d018144cde8a0b2e6))\n  - Merge pull request #3532 from N-Nagorny/logs-smart-app-name-cutting\n  ([067c18e6](https://github.com/Unitech/pm2/commit/067c18e601aca4fac10101a7c23cc4c3525ad776))\n\n\n\n## v2.10.1 ( Mon Feb 26 2018 11:38:18 GMT+0100 (CET) )\n\n\n## Bug Fixes\n  - restore --raw option #3476\n  ([340011ca](https://github.com/Unitech/pm2/commit/340011cace2b90c2a1ead8d86baba517f5570e15))\n\n\n## v2.10.0 ( Mon Feb 19 2018 14:51:19 GMT+0100 (CET) )\n\n\n### Bug Fixes\n  - add livescript in default modules\n  ([a315eeb6](https://github.com/Unitech/pm2/commit/a315eeb65f04b22643a903f0cb1c0f416615ad8b))\n  - replace dash with underscore\n  ([203df768](https://github.com/Unitech/pm2/commit/203df7688ca348967c00bc45289ae70fd2c4aaaa))\n  - make sure not pm2 is running\n  ([bd798fd7](https://github.com/Unitech/pm2/commit/bd798fd748665e935db4bb91f9d1d66952d9842a))\n  - auto-exit edge case fix + pm2 no daemon mode + log in raw by default + less logs\n  ([704ae518](https://github.com/Unitech/pm2/commit/704ae518f5d7df0a631349e518d81cef51249a58))\n  - impact v8 flag in fork mode also\n  ([41bf6ef7](https://github.com/Unitech/pm2/commit/41bf6ef7d3633180b4c1e90f36eb206d82fab2b1))\n  - fixup! #2182 Get rid of annoying popups in Windows 10\n  ([3a85b59d](https://github.com/Unitech/pm2/commit/3a85b59de4a76796ad0880368d8d085a7ba55d36))\n\n\n\n\n### Hot Fixes\n  - \\#3420 ([673acf36](https://github.com/Unitech/pm2/commit/673acf36b4ca1fd65c5135a92d56081f76237a8b))\n\n\n\n\n### Features\n  - add dependencies section into ecosystem.json file.\n  ([828a30d0](https://github.com/Unitech/pm2/commit/828a30d0ccc88b3f6e2b66d517ccf5f2394bd08b))\n  - --deep-monitoring available from pm2-runtime\n  ([99e62e3b](https://github.com/Unitech/pm2/commit/99e62e3bb808f071d6e4850c234b34f7de65b1c2))\n  - add deep_metrics to deep_monitoring flag\n  ([4d1bea5e](https://github.com/Unitech/pm2/commit/4d1bea5e0bbaab1f16f75d012bca25702cdff88e))\n  - add flag to enable deep-monitoring\n  ([c5418688](https://github.com/Unitech/pm2/commit/c541868837a1c4421394de5dd1029d2619b5ac82))\n  - allow pm2 to install a set of module as one single command and add deep-monitoring.\n  ([9dddc80d](https://github.com/Unitech/pm2/commit/9dddc80db5e496def44d4d36716b7de54e5171cf))\n  - pm2 pid <app_name> command\n  ([6687d499](https://github.com/Unitech/pm2/commit/6687d499415151bd62489fed5331f414576ec354))\n  - allow pm2 to install and enable event-loop-inspector data collecting\n  ([e6b0c474](https://github.com/Unitech/pm2/commit/e6b0c47443d3e6a839bf29057ef0a80ef135c47e))\n  - ignore signal when running in --no-daemon\n  ([b9c01c99](https://github.com/Unitech/pm2/commit/b9c01c99d54aba98ab790b8888500ac0f0af05c9))\n  - upgrade pmx to git development branch\n  ([21be05a0](https://github.com/Unitech/pm2/commit/21be05a07bd93eacaddedde3b647c16468937473))\n  - allow pm2 to enable v8 data collecting from pmx\n  ([aa180fa8](https://github.com/Unitech/pm2/commit/aa180fa8ab47f0c687d7c21854d005ad0ebf8475))\n  - allow pm2 to install gc-stats\n  ([15634168](https://github.com/Unitech/pm2/commit/15634168582e4c7b3c5f47a3f58a0fcf8b732a76))\n  - feat add changelog generation support\n  ([14f53fc0](https://github.com/Unitech/pm2/commit/14f53fc0c28be4084778785aeace3763ed0d827f))\n\n  - **pm2**\n    - add pm2 init option to generate an ecosystem file\n  ([5d56fac7](https://github.com/Unitech/pm2/commit/5d56fac7cc12590af29ee46c68ba32a82a2b813b))\n    - add pm2 init option to generate an ecosystem file\n  ([a38fd199](https://github.com/Unitech/pm2/commit/a38fd199b90d27a2405f8cabab0e4f6e45c69b08))\n\n\n\n\n### Documentation\n  - add documentation on new pm2 install command\n  ([c90c453f](https://github.com/Unitech/pm2/commit/c90c453f85b07adb346bc55c2b685d689a2e96f7))\n  - add sendDataToProcessId into typescript definitions\n  ([4a2e8d2d](https://github.com/Unitech/pm2/commit/4a2e8d2d2c4b38fe0ff2377dfe32fce9a43c8044))\n\n\n\n\n### Refactor\n  - delete all \"if\" condition when installing new module, create an object with all modules and a generic installation process\n  ([1b92a9c4](https://github.com/Unitech/pm2/commit/1b92a9c4000734367e68d8dbd60d0901009f4c56))\n  - deep pm2-runtime refactor #3408 #3257 #3266\n  ([c13b2364](https://github.com/Unitech/pm2/commit/c13b23648269529a1f998d816be10f895665861e))\n  - no more interactive spinner for connection to KM + change pm2 log format + remove some logs\n  ([d1916f40](https://github.com/Unitech/pm2/commit/d1916f40962b2cc8a1866172eab7d5d89db093be))\n\n\n\n\n### Chore\n  - pmx to 1.6.3-rc2\n  ([41815e0b](https://github.com/Unitech/pm2/commit/41815e0ba0298979f936b3d4badb196f8d9783d8))\n  - switch pmx to development\n  ([748019d1](https://github.com/Unitech/pm2/commit/748019d1ef0cf760b5e8de9d5b6af6fee300db02))\n  - 2.10.0-beta\n  ([0d2b7172](https://github.com/Unitech/pm2/commit/0d2b7172a093d0638deabb5f23383cc9eec5dda9))\n  - upgrade pmx to 1.6.3-next\n  ([5a1b4343](https://github.com/Unitech/pm2/commit/5a1b4343cc1e1f5018e21451a111340351706213))\n  - upgrade pmx dep\n  ([4bbeec3d](https://github.com/Unitech/pm2/commit/4bbeec3d170ba63af0c0ae0e2d07beec2ab49772))\n  - switch to published pmx(@next)\n  ([859d18fb](https://github.com/Unitech/pm2/commit/859d18fbc79e2a2760fe90e9c17e71209f8177ce))\n  - remove --exit from mocha.opts\n  ([36bf03e1](https://github.com/Unitech/pm2/commit/36bf03e1eed69a27e518151e2f7aa958b15db2fb))\n  - remove unused files\n  ([65d233e5](https://github.com/Unitech/pm2/commit/65d233e5b5290f65796b7cf3daa20706e0f3bee6))\n\n\n\n\n### Branchs merged\n  - Merge branch 'development' of ssh://github.com/deltasource/pm2 into hotfix/scoped-package-support\n  ([94ea9d9e](https://github.com/Unitech/pm2/commit/94ea9d9eeff40faca8aa9f7edfc81aa29c08e740))\n  - Merge branch 'master' into development\n  ([46606903](https://github.com/Unitech/pm2/commit/46606903f25d0f4d0eee226da863e20e4b396dc9))\n  - Merge branch 'development' of github.com:Unitech/pm2 into v8_option\n  ([757562f7](https://github.com/Unitech/pm2/commit/757562f755b09124bbd006209ae38a096d692529))\n  - Merge branch 'development' of github.com:Unitech/pm2 into gc-stats\n  ([3ed1a747](https://github.com/Unitech/pm2/commit/3ed1a7471aec7d79f7d604447ac7445720bdaced))\n  - Merge branch 'master' into development\n  ([ee7651e4](https://github.com/Unitech/pm2/commit/ee7651e47e944c3c829933494c6cc765deb4bb29))\n\n\n\n\n### Pull requests merged\n  - Merge pull request #3466 from natcl/development\n  ([c6d7ace8](https://github.com/Unitech/pm2/commit/c6d7ace802e667def75bc68344effa4856830fb4))\n  - Merge pull request #3464 from andyfleming/patch-1\n  ([dd9ebb60](https://github.com/Unitech/pm2/commit/dd9ebb6051708ee5a13cc68dbcb8238e41860bb9))\n  - Merge pull request #3459 from rmonnier/master\n  ([46948a98](https://github.com/Unitech/pm2/commit/46948a98e90c7864f7b8100db5c519fe9d37f11a))\n  - Merge pull request #3458 from Unitech/pm2_install_command\n  ([f3b35726](https://github.com/Unitech/pm2/commit/f3b35726895bd82b92813f308b787d68e9df1fa4))\n  - Merge pull request #3453 from deltasource/hotfix/scoped-package-support\n  ([974f9bf0](https://github.com/Unitech/pm2/commit/974f9bf0dc7a7aa7ff6860f8640da3593b802296))\n  - Merge pull request #3448 from Unitech/deep_monitoring_flag\n  ([331bc741](https://github.com/Unitech/pm2/commit/331bc741d7285094738a91cd816bc9755cc76605))\n  - Merge pull request #3447 from Unitech/deep-monitoring\n  ([719d328e](https://github.com/Unitech/pm2/commit/719d328e8d14871b34fd33df54fd80f4f8e7825f))\n  - Merge pull request #3443 from Unitech/event-loop-inspector\n  ([77a35274](https://github.com/Unitech/pm2/commit/77a3527407f3d090c7a5fa0bedaf943a7536b5eb))\n  - Merge pull request #3442 from Unitech/event-loop-inspector\n  ([dad98e6e](https://github.com/Unitech/pm2/commit/dad98e6e0738983717fee155ff0f6519955ffc1b))\n  - Merge pull request #3424 from Unitech/sendDataToProcessId_def\n  ([95e85eef](https://github.com/Unitech/pm2/commit/95e85eef84510dddfb0c6b13f0ada38a7dd66cae))\n  - Merge pull request #3438 from Unitech/v8_option\n  ([e46b15dc](https://github.com/Unitech/pm2/commit/e46b15dc32c18e8b24f66da0c79cc06f91cf11b5))\n  - Merge pull request #3437 from Unitech/gc-stats\n  ([1a6771aa](https://github.com/Unitech/pm2/commit/1a6771aa361bb5718bafd6e33e616725f9c0d328))\n  - Merge pull request #3400 from toddwong/windowsHide2\n  ([f65e8794](https://github.com/Unitech/pm2/commit/f65e8794df6e67f4ff60dfbec7c05a37721cb6f9))\n  - Merge pull request #3421 from Unitech/generate_changelog\n  ([b0690618](https://github.com/Unitech/pm2/commit/b0690618d940c11e28eeb5115c060bf363c7b62b))\n  - Merge pull request #3419 from Rohja/fix-build-number-deb-rpm\n  ([b4343de2](https://github.com/Unitech/pm2/commit/b4343de2703fce03f3cf48cc303b12bc6b69b743))\n\n\n\n\n## 2.9.2\n\n- #3364 30% faster CLI via v8-compile-cache\n\n- add process._getActiveRequests() and process._getActiveHandles() custom metrics\n- #3402 #3360 fix bad username\n- #3413 check dependencies before launching tests\n- #3295 add sorting feature for process list (pm2 ls --sort <field_name:order>)\n- #3404 if no gid specified - set gid to uid\n- #3287 add typing for env\n- #3374 separate stdout and stderr for pm2-docker/pm2-runtime\n- #3366 improve building of rpm and deb packages\n- #3375 sendLineToStdin/sendDataToProcessId fix\n- #3365 fix report command for windows\n- #3367 Display an error if the process is not found when running 'pm2 logs <process-name>'\n- #3256 TypeError: Cannot read property 'destroy' of undefined\n- User: append SUDO_USER if no uid has been set and SUDO_USER present\n- User: check permission of agent\n- KM: send outliers\n- KM: infinite retry for km connection\n\n## 2.9.1\n\n- #3356 hot fix on startup system\n\n## 2.9.0\n\n- #3278 --silent -s now does not print welcome message\n- #3345 #2871 #3233 pm2 -v will not spawn daemon anymore\n- #3341 update moment dependency\n- #3314 pm2 install <MODULE> --safe will now monitor new installation of module and will\n  fallback to previous version if the module is failing (restart, fail on npm install)\n- #3314 module folder structure refactoring to keep independent dependencies for each modules\n- #3324 remove yarn installation of modules\n- #3273 pm2 --mini-list now print the right pid file\n- #3206 add flag to auto turn off auto exit with pm2-docker\n- #3036 Fix applying env PM2_CONCURRENT_ACTIONS correctly\n- #3346 do not chmod systemd script (was failing systemd script on orange pi)\n- #3347 Add --wait-ip option to override systemd initialization to wait for internet full connectivity\n- #3348 alias pm2-docker to pm2-runtime\n- #3350 Override HOME and USER when setting --uid to start module or application\n- #3351 alias pm2 ps to pm2 ls (docker style)\n\n## 2.8.0\n\n- #2070 Fix sendDataToProcessId not working (@h091237557)\n- #2182 Add windowHide options in cluster mode (@soyuka)\n- #3206 By default in docker, pm2 will auto exit when no process are online (@dguo)\n- #3225 fix --lines accepting invalid values (@vmarchaud)\n- #3036 fix when PM2_CONCURRENT_ACTIONS was overriden everytime on node > 4 (@danez)\n- Add node 9 tests on CI (@Unitech)\n- Add pm2 unlink command (eq to pm2 link delete) (@Unitech)\n- Fix interactor to support custom endpoints (@vmarchaud)\n- Allow custom PM2_HOME for docker (@lucidNTR)\n- Support MJS module (@vpotseluyko)\n- Allow custom service name for startup (@danez)\n- Update PMX to 1.5 (@unitech)\n\n## 2.7.2\n\n- #3200 Associate .tsx files with ts-node (@dguo)\n- #3202 Add first draft of typescript definitions (@jportela)\n- Allow to install http url via pm2 install (@unitech)\n- #3204 Given --uid add all its gids automatically (@jmeit)\n- #3184 bugfix: try/catch around userInfo to avoid crash (@vmarchaud)\n- #3181 force upgrade to latest pm2-deploy\n\n## 2.7.1\n\n- #3117 Add required node env on cluster mode start instance (2m0nd)\n- make profiler compatible with Node.js 8\n\n## 2.7.0\n\n- #3150 fix watchdog on agent\n- #3001 dump-backup feature\n- #3134 edge case error handling\n- #3096 fix module installation\n- #3085 honor every pm2 args on restart\n- #3046 better error message if PM2 is misconfigured\n- #3058 pm2-docker now does not write logs by default\n- #3045 continue to broadcast on the bus system even if logs are disabled\n- [Docker] Auto Exit when no application is running\n- [Keymetrics] pm2 unmonitor fix\n- [Beta Container Support] beta pm2 start app.js --container\n- [Chore] upgrade modules\n- [Chore] enhance package.json\n\n## 2.6.1\n\n- #3037 bug fix cb\n\n## 2.6.0\n\n### Changes\n\n- #2998 pm2 report command for automated system inspection\n- #2997 --disable-logs option to suppress error\n- #2290 allow to declare apps under \"pm2\" attribute (eq \"apps\"). Nicer in package.json\n- #2994 allow to specify typescript version to be installed\n- #2501 low memory environment pm2 setting via PM2_OPTIMIZE_MEMORY (beta)\n- #2968 pm2 attach <pm_id> to attach to process stdin / stdout\n- pm2-runtime -> drop in replacement for the node.js binary\n- #2951 pm2 reload command locker via timestamped lock file\n- #2977 pm2 reloadLogs protected\n- #2958 Allow to delete attribute via --attribute null\n- #2980 PM2_SILENT=true pm2 startup\n- #2690 --parallel <number> command allows to change the nb of concurrent actions (reload/restart)\n- expose cwd on CLI via --cwd\n- multiple pm2-docker enhacements\n- Alias pm2.link and pm2.unlink to pm2.interact and pm2._pre_interact\n- Allow to customize kill signal via PM2_KILL_SIGNAL\n- Support git+http in module installation\n- force reverse interaction reconnection on internet discovery\n- `--instances -1` when having a 1 cpu is no-longer spawning no processes #2953\n- refactor the context retrieving from error\n- add a TTL for file cache entry\n- #2956 Fix listen_timeout in combination with wait_ready\n- #2996 respect signal order on pm2 reload (delegate ready function to reload fn)\n\n### Breaking\n\n- Drop pm2-daemon CLI (replaced by pm2-runtime)\n\n## 2.5\n\n- `pm2 register|login` to create new account / login on Keymetrics + auto link\n- `pm2 open` to open dashboard on browser\n- `pm2 monitor|unmonitor <pm_id|name|all>` for selective monitoring\n- #2818 alias pm2-docker to pm2-daemon\n- #2809 correctly resolve git/npm repo when running pm2 install\n- #2861 better auto exit check for docker\n- #2870 avoid null error when preparing app config\n- #2872 avoid showing useless warning\n- #438 allow to override daemon config paths via env (example: `PM2_PID_FILE_PATH` to override pid file of the daemon)\n- #2849 better gentoo template for pm2 startup\n- #2868 allow tailing log with `--raw` flag\n- #452 Add `PM2_WEB_STRIP_ENV_VARS` to remove environnement vars from `pm2 web` endpoint\n- #2890 Fix wait-ready for cluster mode\n- #2906 randomize machine name with default pm2 link\n- #2888 allow to use regex for pm2 logs\n- #2045 allow to rename NODE_APP_INSTANCE env variable\n- #2809 add `increment_var` options to ask for a environnement variable to be incremented for each application started\n- more informations when failing to deploy on custom ecosystem file\n- fix tests for node 8\n- fix missing callback when overriding console.log\n- allow to rename daemon process name via `PM2_DAEMON_NAME`\n- few typo in the readme\n\n### Breaking change\n\n- the NODE_APP_INSTANCE var behavior has been changed :\n    - 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)\n    - 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)\n\n## 2.4.5/6\n\n- #2818 alias pm2-docker to pm2-runtime\n- #2815 polyfill for path.isAbsolute for node v0.11\n\n### Breaking change\n\n- rundev command has been dropped because of too low adoption\n\n## 2.4.4\n\n- #2806 fix reconnection to keymetrics\n\n## 2.4.3\n\n- #2759 disable default require of vxx in pmx\n- #2651 always spawn pm2 daemon with `node` binary\n- #2745 new issue template\n- #2761 Make JSON log stream timestamp in consistent format\n- #2770 Fix trigger API never calling callback\n- #2796 Fix absolute path on windows\n- [KM] profiler installation via `pm2 install v8-profiler` or `pm2 install profiler`\n- [KM] Agent rescue system\n\n## 2.4.2\n\n- [KM] Disable pm2-server-monit auto install\n\n## 2.4.1\n\n- #2720 multi user startup script\n- #2266 start and tail logs via `pm2 start app.js --attach`\n- #2699 add back previous termcaps interface via `pm2 imonit`\n- #2681 fix log folder create\n- #2724 make sure process is stopped even if there is a restart_delay\n- #2706 install pm2 modules via yarn if available\n- #2719 show 15 logs line bu default\n- #2703 allow custom timestamp with pm2-docker\n- #2698 fix unicode on pm2 monit\n- #2715 handle treekill edge case bug\n- Optimize CPU usage of pm2 monit command\n- [KM] URL web access dashboard\n- [KM] Auto install pm2-server-monit on keymetrics linking\n- [KM] Error reporting: add context (-B3 -A3 code lines)\n- [KM] Transaction Tracer: reset routes on app restart / wait some time before sending\n\n## 2.4.0\n\n- #2631 new pm2 monit command (blessed dashboard!)\n- #2670 allow to expose a folder over http via `pm2 serve <path> <port>`\n- #2617 fix startup script generation on macosx (launchd)\n- #2650 new option to append env name to app name (used to allow the same app to be launched in different environment w/o name conflict)\n- #2671 allow to pass a delay to pm2-docker (`pm2-docker process.json --delay 10`)\n- `pm2 ecosystem simple` to generate a simple ecosystem file\n- aliasing: `pm2-dev <script>` <=> `pm2-dev start <script>`\n- fix git parsing when using cwd\n- #2663 allow to directly output json when logging (via log_type for JSON and --log-type via CLI)\n- #2675 fix path when installing language module like typescript\n- #2674 increase restart timeout for systemd startup\n- #2564 allow to operate process (restart/reload/stop/delete) with regex\n\n## 2.3.0\n\n- Drop Node.js 0.10 support\n- (CLI) remove immutability of CLI parameters on restart (critical for ux)\n- Keymetrics VXX beta\n- Alias \"exec\" to \"script\"\n- `pm2 logs --nostream` allow to print last logs of application without attaching to logs bus #2620\n- Added startup script for gentoo v2.3 via PR #2625\n- optionalDependencies from http to https\n- remove agent pid on exit\n- #2646 check ps.stdout on treekil\n\n## 2.2.3\n\n- Various startup refactor fixes (#2598, #2587, #2590)\n\n## 2.2.2\n\n- #2574 Support Amazon systemv\n\n## 2.2.1 (rc: 2.2.0@next)\n\n- #2559 New startup system. Supported init system: systemd, upstart, launchd\n\n  $ pm2 startup   # Auto detect available init system + Setup init scripts\n  $ pm2 unstartup # Disable and Remove init scripts\n\n*SystemD, Upstart and Launchd scripts work like a charm*\n\n- #2515 New way to install PM2 on Debian based system:\n\n```\n$ wget -O - http://apt.pm2.io/ubuntu/apt.pm2.io.gpg.key | sudo apt-key add -\n$ echo \"deb http://apt.pm2.io/ubuntu xenial main\" | sudo tee /etc/apt/sources.list.d/pm2.list\n$ sudo apt-get update\n$ sudo apt-get install pm2\n```\n\n- #1090 pm2 resurrect does not respawn the same processes\n- #2544 Attach logs to exception\n- #2545 Right exit code via pm2 api\n- #2543 Fix module pid/mem monitoring\n- #2537 Remove duplicated code in Configuration subsystem\n- Responsive pm2 list (shortened list when < 90 columns)\n- If not TTY do not print ascii table\n- #2509 Trigger functions inside Node.js application from the PM2 CLI\n- Rename pm2.triggerCustomAction() by pm2.trigger(<app_id>, <action_name>, [params], [cb])\n\n## 2.1.6\n\n- #2509 Trigger functions inside Node.js application from the PM2 CLI\n- #2474 Resolve home path in configuration file\n- #2526 Expose .launchAll() method to API\n- #2351 inner pm2 actions - drop autorestart and node_args options\n- #2530 Make sure all processes are killed on system signal to PM2\n- #281 allow to combine PM2_SILENT + pm2 jlist to avoid extra data\n- Alias attributes error_file to err_file + err_log + err, alias out_file to out, out_log\n- Do not ask for pass for set/multiset from KM\n\n## 2.1.5\n\n- #2502 fix SIGTERM signal catch on pm2-docker\n- #2498 #2500 global log rotation\n\n## 2.1.4\n\n- #2486 add --web option to pm2-docker command to expose web process api\n- #2333 #2478 #1732 #1346 #1311 #1101 Fix GracefulShutdown SIGINT output + Better Stop process flow\n- #2353 --wait-ready will wait that the application sends 'ready' event process.send('ready')\n- #2425 allow to specify node.js version to be used or installed via interpreter 'node@VERSION'\n- #2471 Make app environment immutable on application restart/reload by default for CLI actions\n- #2451 Config file can be javascript files\n- #2484 fix pm2 kill on windows\n- #2101 pm2 ecosystem now generates a javascript configuration file\n- #2422 allow to pass none to exec_interpreter\n- Faster CLI load time, reduce load time by 1/4 (downgrade cli-table2 -> cli-table)\n- Do not use disconnect() anymore on cluster processes\n- Better Stop process flow: Upgrade TreeKill system + Wait for check\n- Fix deploy issue with Windows\n- Expose -i <instances> to pm2-docker\n- Drop npm-shrinkwrap\n- Upgrade chokidar (fix symlink), cron, fclone, shelljs\n- Add yarn.lock\n\n## 2.0.19\n\n- #2466 skip cluster workaround / fix cluster mode for Node.js v7\n- Enable Node v7 in travis\n\n## 2.0.16/17/18\n\n- #2400 Create log/pid default folder even if the root folder is already created\n- #2395 CRON feature now call PM2 for app to be killed (allow to use SIGINT)\n- #2413 #2405 #2406 do not exit on unhandledRejection auto catch\n- pidusage upgrade to 1.0.8 to avoid util exception on windows when wmic fail\n- Do no display error when pidusage try to monitor an unknow PID (modules)\n- pm2-docker binary does not need the start option\n\n## 2.0.15\n\n- process.on('unhandledRejection'): allow to catch promise error that have not been catched\n- upgrade fclone and pidusage (faster windows CPU/Mem monitoring)\n- allow to call pm2 CLI from bash script managed by pm2\n- #2394 fix pm2 id command\n- #2385 ts-node upgraded to latest\n- #2381 autocompletion fix\n\n## 2.0.12 Bradbury\n\n- Memory usage reduced by 40%\n- CPU usage in overall situations reduced by 60%\n- Refined pm2 logs command with --json, --format and --raw options\n- Faster process management with CONCURRENT_ACTIONs enabled\n- Faster installation (v1: ~30secs, v2: ~10secs)\n- Faster `pm2 update` with Keymetrics linking delayed at the end\n- Much better Module system with raw NPM feedback\n- Better Windows support\n- **pm2-docker** command with his official [Docker image](https://github.com/keymetrics/pm2-docker-alpine) + json output + auto exit\n- **pm2-dev -> pmd** command enhanced (better log output, post-exec cmd)\n- Watch and Reload instead of Watch and Restart\n- New PM2 API, backward compatible with previous PM2 versions\n\nThe new PM2 API is greatly tested and well designed:\n\n```javascript\nvar PM2 = require('pm2');\n\n// Or instanciate a custom PM2 instance\n\nvar pm2 = new PM2.custom({\n  pm2_home :    // Default is the legacy $USER/.pm2. Now you can override this value\n  cwd      :    // Move to CWD,\n  daemon_mode : // Should the process stay attached to this application,\n  independant : // Create new random instance available for current session\n  secret_key  : // Keymetrics secret key\n  public_key  : // Keymetrics public key\n  machine_name: // Keymetrics instance name\n});\n\n// Start an app\npm2.start('myapp.js');\n\n// Start an app with options\npm2.start({\n  script   : 'api.js',\n  instances: 4\n}, function(err, processes) {\n});\n\n// Stop all apps\npm2.stop('all');\n\n// Bus system to detect events\npm2.launchBus((err, bus) => {\n  bus.on('log:out', (message) => {\n    console.log(message);\n  });\n\n  bus.on('log:err', (message) => {\n    console.log(message);\n  });\n});\n\n// Connect to different keymetrics bucket\npm2.interact(opts, cb)\n\n// PM2 auto closes connection if no processing is done but manually:\n\npm2.disconnect(cb) // Close connection with current pm2 instance\npm2.destroy(cb)    // Close and delete all pm2 related files of this session\n```\n\n- Better CLI/API code structure\n- PM2 isolation for multi PM2 instance management\n\n### Bug fixes\n\n- #2093 #2092 #2059 #1906 #1758 #1696 replace optional git module with tgz one\n- #2077 fix calling pm2.restart inside pm2\n- #2261 GRACEFUL_LISTEN_TIMEOUT for app reload configurable via --listen-timeout\n- #2256 fix deploy command for yaml files\n- #2105 alias pm2 logs with pm2 log\n- Extra module display http://pm2.keymetrics.io/docs/advanced/pm2-module-system/#extra-display\n- Yamljs + Chokidar Security fixes\n- pm2 update / pm2 resurrect is now faster on Node > 4.0\n- keymetrics linking after pm2 update is done once all apps are started\n- pm2 list processes are now sorted by name instead id\n- #2248 livescript support added in development mode\n- The client/server file called Satan.js does not exist anymore. It has been replaced by the file combo ./lib/Client.js and ./lib/Daemon.js\n- PM2 --no-daemon is better now\n\n### Breaking change\n\n- Coffeescript must be installed via `pm2 install coffeescript`\n\n## 1.1.3\n\n- Node v6 compatibility\n\n## 1.1.2\n\n- [#2071 #2075] Fix pm2-dev command\n\n## 1.1.0: Galactica release\n\nThis release is about PM2's internals refactoring, homogenization in action commands (in terms of behavior and outputs).\nSome interesting features has been added, as YAML file support (for application declaration) and some syntaxic sugar.\nThe 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.\n\n**This version has been heavily tested in testing, production environments and deeply monitored in terms of CPU and Memory usage.**\n\n- [#133 #1568] Allow to rename a process via pm2 restart app --name \"new-name\"\n- [#2002 #1921 #1366] Fix CLI/JSON arguments update on restart (args, node_args, name, max-memory)\n- [#578] Add YAML support for application configuration file (in extent to JSON and JSON5 support)\n- [Keymetrics agent refactoring] TCP wait, memory consumption divided by two, reconnection refactoring, keep alive ping system\n- [Keymetrics agent refactoring] Fix random no response from pm2 link and pm2 unlink\n- [#2061] Kill ESRCH of processes in cluster mode with SIGINT catcher fixed\n- [#2012 #1650 #1743] CLI/JSON arguments update on reload\n- [#1613] Reload all reload ALL applications (stopped, errored...)\n- [#1961] Fix kill timeout info log\n- [#1987] Fix FreeBSD startup script\n- [#2011] Respect process.stdout/.stderr signature\n- [#1602] Fix zombie process when using babel-node as interpreter\n- [#1283] --skip-env option to not merge update with system env\n- Homogeneize actions commands outputs\n- Option --interpreter-args added (alias of node-args)\n- Allow to use exactly the same option in JSON declaration and CLI (e.g. interpreter) to avoid confusion\n- pm2 show, now shows more commands to manage processes\n- Refactor programmatic system\n\n## 1.0.2\n\n- [#1035 #1055] Deactivate automatic dump on startup scripts\n- [#1980] Add Javascript source map resolution when exceptions occurs [Documentation](http://pm2.keymetrics.io/docs/usage/source-map-support/)\n- [#1937] Allow to act on application having numerics as app name\n- [#1945] Fix post_update commands section when file contains Javascript\n- [#624] --only <app-name> to act only on specified app name in json app declaration\n- [0.6.1](https://github.com/keymetrics/pmx/releases/tag/0.6.1) PMX upgrade\n\n## 1.0.1\n\n- [#1895] pm2 id <app_name>: output array of ids for app_name @soyuka\n- [#1800] pm2 show <app_name>: now also display node.js version @soyuka\n\n## 1.0.0\n\n- [#1844][#1845][#1850] Load configuration in /etc/default/pm2 + add ulimit -n override\n- [#1810] Add --kill-timeout <number> option (delay before process receive a final SIGKILL)\n- [#1830] Add tests for PM2_KILL_TIMEOUT (SIGKILL delay) + default SIGINT to any kind of procs\n- [#1825] Process management commands (start/restart/stop/delete) can take multiple arguments\n- [#1822] Add new method pm2.sendDataToProcessId(type|data|id) to send data to processes\n- [#1819] Send SIGINT signal to process instead of SIGTERM\n- [#1819][#1794][#1765] Avoid writing on std err/out when process is disconnected\n\n- Add default attribute in schema.json to allow to configure default value when passing a JSON\n- JSON and CLI starts are now consistent in terms of option size, attribute number\n- pm2.restart(json_data, function(err, data) now returns an array of process instead of simple object (success:true))\n- Now pm2 restart process.json --env <X>, refresh environment variable on each restart depending of the X environment\n- prepareJSON method in PM2 code (God.js) removed\n- partition Common.prepareAppConf (duplicate with verifyConfs)\n- Change signature of Common.prepareAppConf\n- Centralize Interpreter resolution via Common.sink.resolveInterpreter(app) in Common.js\n\n- Better meta information when process restart/reload/stop (signal + exit code)\n- Upgrade pm2-axon, cron, should, mocha, coffee-script, chokidar, semver NPM packages\n- Show process configuration option when describing process\n- Add --no-automation flag\n- Fix when starting application with illegal names (#1764)\n- Fix management of app starting with numerics in the filename (#1769)\n- Fix versiong system (reset to default on resurrect/prepare)\n- Increase buffer size for versioning meta parsing\n\n## 0.15.10\n\n- Hot fix #1746\n\n## 0.15.9\n\n- Chokidar upgraded to 1.2\n- Fix startup script via new --hp option\n- Fix JSON refresh system\n\n## 0.15.1-8\n\n- JSON refresh available\n- New module system backward compatible and compatible with NPM 3.x\n- Possibility to install module from tgz (#1713)\n- ecosystem generated file via pm2 generate uptaded (not json5 prefix anymore, and updated comments)\n- always prefix logs #1695\n- blessed dependency removed\n- drop locking system\n- add callback to deploy (#1673)\n- typo fixes\n- pm2.update added\n- small db for pm2 modules added (solve npm 3.x issue)\n- pm2 multiset \"k1 v1 k2 v2 k3 v3\"\n- babel dependency removed\n- blessed dependency removed\n- chalk, safe-clone-deep, shelljs, semver upgraded\n- New command: pm2 module:update <module_name> -> Update a module\n- New command: pm2 module:publish  -> Publish module in current folder + Git push\n- New command: pm2 module:generate [module name] -> Generate a sample module\n- Feature: configuration system for raw Node.js applications\n- alias pm2 install with pm2 i\n- JSON declaration: You can now use process.env in application declaration file\n- watch has been refactored for windows and tests\n- allow installation of specific module version\n- wrap final process kill intro try catch (c4aecc8)\n- Appveyor to test PM2 under Windows added (+ fix some incorect file name)\n- Allow to escape key name when using pm2 conf system\n\n## 0.14.7\n\n- New flag `--no-pmx` : starts an app without injecting pmx\n- New feature : cron restart now works in fork mode as well\n- Disabled auto-gc on interactor\n- Allow PM2 to execute binaries in $PATH\n- pm2 link priv pub --recyle for elastic infrastructure\n- pm2 deploy now check default file ecosystem.js[on|on5], package.json\n\n## 0.14.6\n\n- Scoped PM2 actions\n- Password encryption via pm2 set pm2:passwd xxxx\n- Interactor Remote action refactor\n- .getSync method to get configuration variable synchronously\n- Add password protected PM2 methods (install, delete)\n- pm2 get|pm2 conf display all confs\n- Password protected PM2 flag\n- New flag : `--restart-delay <ms>` (or `restart_delay` in JSON declaration)\n- New command : `pm2 deepUpdate`\n- New command (beta) : `pm2 logrotate`\n- Enhancement : pm2 handles processes that can't be killed in a better way\n- Fix : some ignore_watch issues\n- Fix : some pm2 startup systemd issues\n\n## 0.14.5\n\n- Hot fix\n\n## 0.14.4\n\n- New command : `pm2 iprobe [app_name|app_id|'ALL']`\n- Feature: FreeBSD startup script\n- Fix: Remove forced GC\n- Fix: ##1444 --next-gen-js in fork mode\n- Fix: Windows path fix\n\n## 0.14.3 (Current Stable)\n\n- `pm2 flush` now flushes pm2.log as well\n- New flag : `--no-treekill` : when used PM2 won't kill children processes\n- New flags : `pm2 logs ['all'|'PM2'|app_name|app_id] [--err|--out] [--lines <n>] [--raw] [--timestamp [format]]`\n- Enhancement: Modules installable via Github: `pm2 install username/repository`\n- Feature: PMX has *scoped function* -> pm2 stores temporary output from custom functions\n- Fix: Interactor issue when doing an heapdump\n- Feature: PM2 CLI autocompletion\n\n## 0.14.2\n\n- Improved pm2-dev\n- Now when apps list is empty, the `id` counter is set to 0\n- Removed pres/keymetrics.js post-install script\n- Fix : `pm2 logs` allocation error\n- Fix : `pm2 prettylist|jlist` truncated output\n\n## 0.14.0 - CrystalClear (pre 1.0)\n\n- Removed: pm2.startJSON() method, now call pm2.start()\n- API Change: pm2 start <app_name|app_id> restart an application already launched\n- API Change: pm2 start <json> restart all json apps if already launched\n- pm2 start all - restart all applications\n- pm2 reload <json_file> possible\n- pm2 gracefulReload <json_file> possible\n- Smart start (pm2 start app.js ; pm2 stop app ; pm2 start app)\n- Reduced memory footprint\n- Reduced pipelined data\n- Reduced CPU usage\n- Faster command processing\n- Upgrade shelljs, semver, colors, chalk, coffee-script, async, json-stringify-safe, cron, debug, commander\n- Fix: launchBus() only connects and disconnects once\n\n- Refactored `pm2 logs` :\n  - Now you don't need to install tail on Windows\n  - 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)\n  - Logs are shown in chronological order at a file level (modified date)\n  - More verbosity : tailed logs are explicitely separated from the real-time logs\n  - Real-time logs now use the `bus` event emitter\n  - PM2 logs added to the `bus`\n  - `--lines <n>` and `--raw` flags available for `pm2 logs` command\n  - New flag : '--timestamp [format]' // default format is 'YYYY-MM-DD-HH:mm:ss'\n  - Now you can exclusively show PM2 logs by doing `pm2 logs PM2`\n\n## 0.12.16\n\n- Feature : File transmission added in Agent\n- Feature : Transmit Node.js/io.js version in Agent\n- Feature : Parameters can be passed to remote actions\n- Feature : Support JS in addition to JSON and JSON5 config files #1298\n- Enhanced: pm2 conf display all configuration values\n- Enhanced: pm2-dev\n- Enhanced: Better error messages when validating data passed via CLI\n- Enhanced: Smaller memory footprint for PM2 (~30%)\n- Fix #1285 : PID file was deleted after a reload/gracefulReload\n- Fix : ENOMEM made PM2 crash\n\n## 0.12.15\n\n- Fix #941 : Env variables overrided when an app is restarted\n- max_memory_restart now performs a graceful reload\n- `pm2 logs --raw` now shows 20 last lines of each log file\n- pm2-dev run app.js : start an app in dev mode (--no-daemon --watch and stream logs of all launched apps)\n- --no-daemon command now display logs of all processes (Docker)\n\n## 0.12.14\n\n- `ilogs` is no longer part of PM2\n- Improved interaction with Keymetrics\n- BabelJS is now integrated into PM2 (`--next-gen-js` flag)\n\n## 0.12.13\n\n- Enhanced  : PM2 doesn't leave processes behind when it crashes\n- Enhanced  : Call reload instead of restart when max-memory-limit reached\n- Enhanced  : Modules are compatible ES6 by default by adding --harmony flag\n- Enhanced  : Dump feature is now smarter\n- Fix #1206 : fix `pm2 logs` bug when merged_logs\n- Fix       : pm2 scale doesn't try to scale a fork_mode process\n\n## 0.12.12\n\n- `pm2 logs --raw` flag : show logs in raw format\n- New command: pm2 scale <app_name> <number> - scale up/down an application\n- Fix #1177 : no concurrent vizion.parse() for the same process event when it restarts\n- Added: Expose kill method programmatically\n- Added: Call disconnect without a function\n- Added: Programmatic call to .connect can now take no-daemon-option\n- Fixed: starting a JSON programmatically return a process list coming from God\n- Fixed: Reflect dump functions from CLI and God\n- Enhanced: New CLI API for configuring modules (pm2 conf module.option [value])\n- Added: Using Keymetrics harden PM2 by enabling a WatchDog that auto restart PM2 in case of crash\n- Added: Expose pm2 gc programmatically\n- Added: pm2 install <module_name> update the module\n- Enhanced: 4 new test suits for PM2 programmatics call\n- Enhanced: Documentation restructured\n\n## 0.12.11\n\n- `--no-autorestart` flag : starts an app without automatic restart feature\n(`\"autorestart\" : false` in JSON declaration)\n\n- `--no-vizion` flag : starts an app completely without vizion features\n(`\"vizion\" : false` in JSON declaration)\n\n- Fix #1146 : add module._initPaths() on ProcessContainer.js so it forces each\nnew process to take the current NODE_PATH env value in account\n\n- New: pm2.start() now handles json objects as param\n\n- Added: timestamps to KM agent logs\n\n- Fix: now properly closes all fds after logging has finished.\n\n- New command: pm2 gc (manually triggers garbage collection for PM2)\n\n- VersioningManagment: exec() timeout configurable via .json\n\n- Fix #1143 :\nIf we start let's say 4 instances of an app (cluster_mode),\nEach app will have a value in process.env.NODE_APP_INSTANCE which will be 0 for the first one,\n1, 2 and 3 for the next ones.\n\n- Fix #1154 :\nNegative arguments to '-i' are substracted to CPU cores number.\nE.g: 'pm2 start app.js -i -3' in a 8 cpus environment will start 5 instances (8 - 3).\n\n## 0.12.10\n\n- Fix : PM2 interactor doesn't send data about dead processes ('_old_') anymore.\n- Fix #1137 : Safe params for 'pm2 list' so cli-table won't fail\n- Refactored reverse interaction with keymetrics for better stability and more verbosity on Rollback/Pull/Upgrade operations\n\n## 0.12.9\n\n- Fix #1124 : PM2_PROGRAMMATIC flag wasn't handled properly\n- Fix #1121 : NODE_PATH before PATH so custom node versions come first\n- Fix #1119 : Safe params so cli-table won't fail\n- Fix #1099 : Bug when app name starts by digit (e.g '1-myApp')\n- Fix #1111 : More verbosity on writeFileSync errors\n- New env setting: PM2_KILL_TIMEOUT (ms) : time to wait before a process is considered dead\n- New env setting: PM2_CONCURRENT_ACTIONS : use it with care, value bigger than 1 is considered unstable\n- Refactored reload/gracefulReload for better stability\n\n## 0.12.8\n\n- Fix : `Channel closed error`\n- Fix : `Resource leak error`\n- Fix#1091 : when passing a wrong formated number to `-i` infinite loop\n- Fix #1068 #1096 : restart fails after reloadLogs()\n- New : When PM2 is being killed, all restarts are blocked to avoid conflict\n- New : PM2 dumps the process list before exiting if it is killed by signal\n- Refactored stop/restart for better stability\n\n## 0.12.7\n\n- pm2 logs : Now shows merged logs\n- Fix #929 #1043 : Bug pm2 stop/restart not working properly\n- Fix #1039 : Better algorithm for vision recursive parsing to avoid infinite loops\n- Automatize #858 #905: Directly init pm2 folder if not present when using it programmatically\n- Add Bus system from PM2 programmatic API\n\n## 0.12.6\n\n- Enhancement of startJson command (force_name and additional_env options)\n- Fix #990 : pm2 flush while pm2 logs was open bug\n- Fix #1002 : pm2 monit bug\n- Fix #1024 : enhancement\n- Fix #1011 : json-stringify-safe bug\n- Fix #1007 ##1028 #1013 #1009 : pm2 desc bug\n- Fix : pm2 interact delete when file doesn't exist bug\n\n## 0.12.5\n\n- Windows support\n\n## 0.12.4\n\n- Never start a process that already has a PID [#938]\n- 1. Make platform auto detecting. 2. Support darwin startup script. [#936]\n- Fix #857 #935, add scriptArgs back [d61d710]\n- Fix broken link upstart [f8ff296]\n- Fixed: multiple calls to vizion.parse() for the same process [0e798b1]\n- fix 2015 test easter egg - Happy New Year! [85d11d5]\n- fixes #906 [#911]\n- Add back automatic coffee interpreter #488 #901 [e9a69fe]\n- Upgrade cli-table, commander, colors, moment dependencies [0cc58ce][a4b7d8d]\n- Domain system to patch fix the exception thrown by the cluster module\n- Fix #830 #249 #954 when there is no HOME env to default to /etc/.pm2 [17d022c]\n\n## 0.12.3\n\n- fixed critical bug: `process.env` flattens all env-vars [#898]\n- npm maintainers format [#894]\n- fix `pm2 desc` crash bug [#892]\n- fix CLI typo [#888]\n- `port` config [#885]\n\n## 0.12.2\n\n- treeKill copyright and update [#848] [#849]\n- Allow environment variables per each ecosystem deploy [#847]\n- max-memory-restart option [#697] [#141]\n- JSON validation (cf ADVANCED_README.md) [#768] [#838]\n- CLI/JSON refactoring\n- watch fixes\n- execute binary softwares\n- node_args refactored (ESC support) [#838]\n- reload env graceful and peaceful [#838]\n- min_uptime added [#838]\n- startOrRestart conf.json does update environment variables [#805]\n- vizion only refresh ahead and unstaged flags [f1f829c]\n- worker restart cluster process if it's equal to 0 && online [c2e3581]\n- pm2 pull <name> [commit_id] [c2e3581] [4021902]\n- fix reloadLogs for fork mode [c0143cc][197781e]\n- waterfall logs stream [#822]\n- --log option to have a merged error and out output [#822]\n- God core refactors\n- test refactoring\n- update isBinaryFile [636fd99]\n- pid deletion has been resurected [f2ce631]\n- worker refactor [29fc72b]\n- fix no color [3feead2]\n- upgrade chokidar 0.12 with follow symlink [4ac0e74]\n- refactor Reload [cf94517][f1eb17]\n- avoid truncate with pm2 logs command [26aff8b]\n- God print log with timestamp via PM2_LOG_DATE_FORMAT [bf2bf8a][3eaed07]\n- better test suit\n- new treekill system [11fe5f4]\n\nBig thanks to @Tjatse !\n\n## 0.12.1\n\n- Harden Lock system\n- Fix Worker bug / Refactor Worker\n- Cleanly close interactor sockets on end\n- Add backward compatibility for older PM2 on kill action via system signal SIGQUIT\n- once listener for killDaemon\n\n## 0.12.0 - clear water ops\n\n- better ecosystem.json5 file with embedded comments\n- startOrRestart conf.json update environment variables #805 #812\n- pm2 start my/bin/file work out of the box\n- JSON5 support\n- PM2_HOME supported - PM2 files paths relocation (logs, pid) via PM2_HOME option\n- post_updates commands are searched in process.json/ecosystem.json/package.json\n- Worker system to verify up to date repositories\n- Rename process running with PM2 <version> - app_name\n- Process Lock system\n- Inner iteraction with PM2 possible #782\n- Better vizion system\n- backward / forward / pull command\n- Doc moved to doc\n- remove uidnumber module\n- pre install / post install scripts removed\n- Remote Lock System\n- More God tests\n- GRACEFUL_LISTEN_TIMEOUT constant configurable\n- Logs are closed in Fork mode when reloading\n- Fix not tty\n- Fix cluster structure nullification\n- Pre Windows Support\n- Send revision process on each process event\n- Upgrade Commander (better help display)\n- Upgrade chokidar to 0.10.x\n- Better interactor\n- Better revision parsing\n- Configuration file\n- Close fd in fork mode while reloading\n- Remove --run-as-user option\n- Better CLI interface for interactor\n- axm:monitor axm:dynamic\n- Temporaly merge pm2-interface with pm2\n- Cache cpu infos\n- Make revision transit in God.bus broadcast\n- Ignore useless events in God.bus broadcast\n\n## 0.11.0-1\n\n- Multi user support and privilege containment: UNIX sockets instead of TCP\n- Reload refactoring\n- Process on uncaughtexcption to flush process list\n- pm2 logs display state change of processes\n\n## 0.10.x\n\n- multi host for pm2 deploy\n- fork mode by default\n- fix watch on clusters\n- refactor watch\n- env option via programmatic interface\n- fix watch system\n- correct pm2 describe command\n- close file used via pm2 flush\n- add startOrReload\n- better closing events\n\n## 0.10.0 - PM2 Hellfire release\n\n- PM2 hearth code has been refactored and now it handles extreme scenario without any leak or bug\n- PM2 restart <json|id|name|all> refresh current environment variables #528\n- PM2 delete all more verbose\n- PM2 reset <all|id|name> reset restart numbers\n- Auto update script at PM2 installation\n- --watch enhanced to avoid zombie processes\n- 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)\n- PM2 respects strong unix standard process management\n- Remove timestamps by default with pm2 logs\n- Coffeescript not enabled by default anymore (enhance memory usage)\n- PM2 Programmatic interface enhanced\n- PM2 hearth refactor\n- PM2 describe show node-args\n- node_args for V8 options is now available via JSON declaration\n- Watch system avoid ghost processes\n- Memory leak fixes\n- Better performance on interface\n- Fix tests\n- Enable PM2_NODE_OPTIONS and node-args for fork mode\n- Dependencies updated\n- Faster monitoring system\n- AXM actions unification\n- Socket errors handled\n- Watchdog via Agent - restart automatically PM2 with previous processes in case of crash\n- PM2_NODE_OPTIONS deprecation (use --node-args instead)\n\n## 0.9.6 - 0.9.5 - 0.9.4\n\n- Bash test auto exit when failure\n- Bump fix log streaming\n- Bump fix to display old logs streaming by default\n- Bump fix\n\n## 0.9.3\n\n- Critical bug on fork mode fixed (stream close)\n- Advanced log display interface pm2-logs #589\n- Simple log timestamp via --log-date-format (with momentJS formating) #183\n- Possible to pass arguments via scriptArg with programmatic PM2 #591\n- Gentoo startup script generation #592\n- Fix run-as-user and run-as-group in fork mode #582\n- Documentation update\n\n## 0.9.2\n\n- max_restart enabled\n- sudo fix for init scripts\n- some startup refactoring\n- Possibility to specify the configuration folder for PM2 via process.env.PM2_HOME\n- Fix date format\n- N/A for undefined date\n- Evented interactions with PM2, available via pm2-interface\n- Deep Interactor refactoring\n- Force reload for upstart script\n\n## 0.9.0-0.9.1\n\n- CLI flattening\n- require('pm2') possible to interact with\n- deployment system\n- Remove builtin monitoring feature\n- Fix watch on delete #514\n- Gracefull reload now rightly handled #502\n- Allow path in watch option #501\n- Allow management of non-interpreted binaries #499\n- Documentation fixes\n\n## 0.8.12-0.8.15\n\n- Version bumping\n\n## 0.8.12\n\n- Fix CWD option #295\n\n## 0.8.10-0.8.11\n\n- Builtin monitoring feature with email (with pm2 subscribe)\n- Reload Logs for Fork\n- Deletion of possible circular dependencies error\n- pm2 updatePM2 command to update in-memory pm2\n- notification message if the in-memory pm2 is outdated\n- cwd option in json #405 #417 #295\n- README updates\n- ipc channel for fork mode\n- re enable process event loggin for interactor\n- avoid possible stream error\n- watch ignore option in JSON\n\n## 0.8.5-6\n\n- Update monitoring module\n\n## 0.8.4\n\n- Remove C++ binding for monitoring\n- Update axon and axon-rpc\n\n## 0.8.2\n\n- Adds option to switch to a different user/group before starting a managed process #329\n- watch doesnt watch node_module folder\n- default log files and pid files location can be overrided by PM2_LOG_DIR / PM2_PID_DIR\n\n\n## 0.8.1\n\n- Readme changes #400 #398\n- Fix describe command #403\n- reload/gracefulReload throw error if no process has been reloaded #340\n\n## 0.8.0\n\n- More verbosity to pm2.log\n- Fast Watch & Reload\n- New README.md\n- --merge-logs option to merge logs for a group of process\n- logs reload with SIGUSR2 or `pm2 reloadLogs`\n- return failure code when no process has been reloaded\n- Upgrade of outdated packages\n- Silent (-s) flag remove all possible pm2 output to CLI\n- New display for list, more compact\n- `pm2 describe <id>` to get more details about a process\n- Fixed 0.10.x issue when stop/kill\n- Helper shown when -h\n- Linter errors\n- Systemd support for Fedora / ArchLinux\n- #381 Add support for Amazon Linux startup script\n- Fixed rendering\n- Interaction possible with VitalSigns.io\n- Avoid exception when dump file is not present\n\n## 0.7.8\n\n- List processes with user right `service pm2-init.sh status`\n\n## 0.7.7\n\n- Bug fixes, stability fixes\n\n## 0.7.2\n\n- harmony can be enabled [Enabling harmony](#a66)\n- can pass any options to node via PM2_NODE_OPTIONS, configurable via ~/.pm2/custom_options.sh\n- pid file written in ~/.pm2/pm2.pid\n- startup script support for CentOS\n- --no-daemon option (Alex Kocharin)\n- json file now can be : started/stoped/restarted/deleted\n- coffeescript support for new versions (Hao-kang Den)\n- accept JSON via pipe from standard input (Ville Walveranta)\n- adjusting logical when process got an uncaughtException (Ethanz)\n\n### Update from 0.x -> 0.7.2\n\n- CentOS crontab option should not be used anymore and use the new init script with `pm2 startup centos`\n- If you use the configuration file or the harmonoy option, you should regenerate the init script\n\n## 0.7.1\n\n- Integrates hardened reload, graceful reload and strengthened process management\n\n## 0.7.0\n\n- Reload works at 100%\n- Logs are now separated by process id\n- Minimal listing with -m option\n- pid files are deleted once process exit\n- ping method to launch or knwo if pm2 is alive\n- more tests\n- coffeescript is supported in cluster mode\n- clean exit\n- clean process stopping\n- speed process management enhanced\n- async used instead of recuresive loops\n- 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\n\n## 0.6.8\n\n- Homogeneize JSON #186\n- Auto intepreter selection (you can do pm2 start app.php)\n\n## 0.5.6\n\n- Coffeescript support\n- Updating dependencies - axon - commander\n- Log feature enhanced - duplicates removed - name or id can be passed to pm2 logs xxx\n\n## 0.5.5\n\n- Ability to set a name to a launched script + tests\n    - with the --name option when launching file\n    - with the \"name\" parameter for JSON files\n- Ability to restart a script by name + tests\n- Upgrade node-usage to 0.3.8 - fix monitoring feedback for MacOSx\n- require.main now require the right file (activate it by modifying MODIFY_REQUIRE in constants.js)\n- CentOS startup script with pm2 startup centos\n- 0 downtime reload\n\n## 0.5.4\n\n- Remove unused variable in startup script\n- Add options min_uptime max_restarts when configuring an app with JSON\n- Remove pid file on process exit\n- Command stopAll -> stop all | restartAll -> restart all (backward compatible with older versions)\n\n## 0.5.0\n\n- Hardening tests\n- Cron mode to restart a script\n- Arguments fully supported\n- MacOSx monitoring possible\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing\n\n## Cloning PM2 development\n\n```bash\n$ git clone https://github.com/Unitech/pm2.git\n$ cd pm2\n$ git checkout development\n$ npm install\n```\n\nI recommend having a pm2 alias pointing to the development version to make it easier to use pm2 development:\n\n```\n$ cd pm2/\n$ echo \"alias pm2='`pwd`/bin/pm2'\" >> ~/.bashrc\n```\n\nYou are now able to use pm2 in dev mode:\n\n```\n$ pm2 update\n$ pm2 ls\n```\n\n## Project structure\n\n```\n.\n├── bin      // pm2, pmd, pm2-dev, pm2-docker are there\n├── examples // examples files\n├── lib      // source files\n├── pres     // presentation files\n├── test     // test files\n└── types    // TypeScript definition files\n```\n\n## Modifying the Daemon\n\nWhen you modify the Daemon (lib/Daemon.js, lib/God.js, lib/God/*, lib/Watcher.js), you must restart the pm2 Daemon by doing:\n\n```\n$ pm2 update\n```\n\n## Commit rules\n\n### Commit message\n\nA good commit message should describe what changed and why.\n\nIt should :\n  * contain a short description of the change (preferably 50 characters or less)\n  * be entirely in lowercase with the exception of proper nouns, acronyms, and the words that refer to code, like function/variable names\n  * be prefixed with one of the following word\n    * fix : bug fix\n    * hotfix : urgent bug fix\n    * feat : new or updated feature\n    * docs : documentation updates\n    * BREAKING : if commit is a breaking change\n    * refactor : code refactoring (no functional change)\n    * perf : performance improvement\n    * style : UX and display updates\n    * test : tests and CI updates\n    * chore : updates on build, tools, configuration ...\n    * Merge branch : when merging branch\n    * Merge pull request : when merging PR\n\n## Tests\n\nThere are two tests type. Programmatic and Behavioral.\nThe main test command is `npm test`\n\n### Programmatic\n\nProgrammatic tests are runned by doing\n\n```\n$ bash test/pm2_programmatic_tests.sh\n```\n\nThis test files are located in test/programmatic/*\n\n### Behavioral\n\nBehavioral tests are runned by doing:\n\n```\n$ bash test/e2e.sh\n```\n\nThis test files are located in test/e2e/*\n\n## File of interest\n\n- `$HOME/.pm2` contain all PM2 related files\n- `$HOME/.pm2/logs` contain all applications logs\n- `$HOME/.pm2/pids` contain all applications pids\n- `$HOME/.pm2/pm2.log` PM2 logs\n- `$HOME/.pm2/pm2.pid` PM2 pid\n- `$HOME/.pm2/rpc.sock` Socket file for remote commands\n- `$HOME/.pm2/pub.sock` Socket file for publishable events\n\n## Generate changelog\n\n### requirements\n\n```\nnpm install git-changelog -g\n```\n\n### usage\n\nEdit .changelogrc\nChange \"version_name\" to the next version to release (example 1.1.2).\nChange \"tag\" to the latest existing tag (example 1.1.1).\n\nRun the following command into pm2 directory\n```\ngit-changelog\n```\n\nIt will generate currentTagChangelog.md file.\nJust copy/paste the result into changelog.md\n"
  },
  {
    "path": "GNU-AGPL-3.0.txt",
    "content": "\n                    GNU AFFERO GENERAL PUBLIC LICENSE\n                       Version 3, 19 November 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The GNU Affero General Public License is a free, copyleft license for\nsoftware and other kinds of works, specifically designed to ensure\ncooperation with the community in the case of network server software.\n\n  The licenses for most software and other practical works are designed\nto take away your freedom to share and change the works.  By contrast,\nour General Public Licenses are intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains free\nsoftware for all its users.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\n  Developers that use our General Public Licenses protect your rights\nwith two steps: (1) assert copyright on the software, and (2) offer\nyou this License which gives you legal permission to copy, distribute\nand/or modify the software.\n\n  A secondary benefit of defending all users' freedom is that\nimprovements made in alternate versions of the program, if they\nreceive widespread use, become available for other developers to\nincorporate.  Many developers of free software are heartened and\nencouraged by the resulting cooperation.  However, in the case of\nsoftware used on network servers, this result may fail to come about.\nThe GNU General Public License permits making a modified version and\nletting the public access it on a server without ever releasing its\nsource code to the public.\n\n  The GNU Affero General Public License is designed specifically to\nensure that, in such cases, the modified source code becomes available\nto the community.  It requires the operator of a network server to\nprovide the source code of the modified version running there to the\nusers of that server.  Therefore, public use of a modified version, on\na publicly accessible server, gives the public access to the source\ncode of the modified version.\n\n  An older license, called the Affero General Public License and\npublished by Affero, was designed to accomplish similar goals.  This is\na different license, not a version of the Affero GPL, but Affero has\nreleased a new version of the Affero GPL which permits relicensing under\nthis license.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                       TERMS AND CONDITIONS\n\n  0. Definitions.\n\n  \"This License\" refers to version 3 of the GNU Affero General Public License.\n\n  \"Copyright\" also means copyright-like laws that apply to other kinds of\nworks, such as semiconductor masks.\n\n  \"The Program\" refers to any copyrightable work licensed under this\nLicense.  Each licensee is addressed as \"you\".  \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\n  To \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy.  The resulting work is called a \"modified version\" of the\nearlier work or a work \"based on\" the earlier work.\n\n  A \"covered work\" means either the unmodified Program or a work based\non the Program.\n\n  To \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy.  Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\n  To \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies.  Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.\n\n  An interactive user interface displays \"Appropriate Legal Notices\"\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License.  If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n  1. Source Code.\n\n  The \"source code\" for a work means the preferred form of the work\nfor making modifications to it.  \"Object code\" means any non-source\nform of a work.\n\n  A \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\n  The \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form.  A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\n  The \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities.  However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work.  For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\n  The Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.\n\n  The Corresponding Source for a work in source code form is that\nsame work.\n\n  2. Basic Permissions.\n\n  All rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met.  This License explicitly affirms your unlimited\npermission to run the unmodified Program.  The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work.  This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\n  You may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force.  You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright.  Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\n  Conveying under any other circumstances is permitted solely under\nthe conditions stated below.  Sublicensing is not allowed; section 10\nmakes it unnecessary.\n\n  3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\n  No covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\n  When you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work's\nusers, your or third parties' legal rights to forbid circumvention of\ntechnological measures.\n\n  4. Conveying Verbatim Copies.\n\n  You may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\n  You may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n  5. Conveying Modified Source Versions.\n\n  You may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:\n\n    a) The work must carry prominent notices stating that you modified\n    it, and giving a relevant date.\n\n    b) The work must carry prominent notices stating that it is\n    released under this License and any conditions added under section\n    7.  This requirement modifies the requirement in section 4 to\n    \"keep intact all notices\".\n\n    c) You must license the entire work, as a whole, under this\n    License to anyone who comes into possession of a copy.  This\n    License will therefore apply, along with any applicable section 7\n    additional terms, to the whole of the work, and all its parts,\n    regardless of how they are packaged.  This License gives no\n    permission to license the work in any other way, but it does not\n    invalidate such permission if you have separately received it.\n\n    d) If the work has interactive user interfaces, each must display\n    Appropriate Legal Notices; however, if the Program has interactive\n    interfaces that do not display Appropriate Legal Notices, your\n    work need not make them do so.\n\n  A compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit.  Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n  6. Conveying Non-Source Forms.\n\n  You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:\n\n    a) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by the\n    Corresponding Source fixed on a durable physical medium\n    customarily used for software interchange.\n\n    b) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by a\n    written offer, valid for at least three years and valid for as\n    long as you offer spare parts or customer support for that product\n    model, to give anyone who possesses the object code either (1) a\n    copy of the Corresponding Source for all the software in the\n    product that is covered by this License, on a durable physical\n    medium customarily used for software interchange, for a price no\n    more than your reasonable cost of physically performing this\n    conveying of source, or (2) access to copy the\n    Corresponding Source from a network server at no charge.\n\n    c) Convey individual copies of the object code with a copy of the\n    written offer to provide the Corresponding Source.  This\n    alternative is allowed only occasionally and noncommercially, and\n    only if you received the object code with such an offer, in accord\n    with subsection 6b.\n\n    d) Convey the object code by offering access from a designated\n    place (gratis or for a charge), and offer equivalent access to the\n    Corresponding Source in the same way through the same place at no\n    further charge.  You need not require recipients to copy the\n    Corresponding Source along with the object code.  If the place to\n    copy the object code is a network server, the Corresponding Source\n    may be on a different server (operated by you or a third party)\n    that supports equivalent copying facilities, provided you maintain\n    clear directions next to the object code saying where to find the\n    Corresponding Source.  Regardless of what server hosts the\n    Corresponding Source, you remain obligated to ensure that it is\n    available for as long as needed to satisfy these requirements.\n\n    e) Convey the object code using peer-to-peer transmission, provided\n    you inform other peers where the object code and Corresponding\n    Source of the work are being offered to the general public at no\n    charge under subsection 6d.\n\n  A separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\n  A \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling.  In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage.  For a particular\nproduct received by a particular user, \"normally used\" refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product.  A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.\n\n  \"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source.  The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.\n\n  If you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information.  But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\n  The requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed.  Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.\n\n  Corresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n  7. Additional Terms.\n\n  \"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law.  If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\n  When you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit.  (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.)  You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\n  Notwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:\n\n    a) Disclaiming warranty or limiting liability differently from the\n    terms of sections 15 and 16 of this License; or\n\n    b) Requiring preservation of specified reasonable legal notices or\n    author attributions in that material or in the Appropriate Legal\n    Notices displayed by works containing it; or\n\n    c) Prohibiting misrepresentation of the origin of that material, or\n    requiring that modified versions of such material be marked in\n    reasonable ways as different from the original version; or\n\n    d) Limiting the use for publicity purposes of names of licensors or\n    authors of the material; or\n\n    e) Declining to grant rights under trademark law for use of some\n    trade names, trademarks, or service marks; or\n\n    f) Requiring indemnification of licensors and authors of that\n    material by anyone who conveys the material (or modified versions of\n    it) with contractual assumptions of liability to the recipient, for\n    any liability that these contractual assumptions directly impose on\n    those licensors and authors.\n\n  All other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10.  If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term.  If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\n  If you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\n  Additional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.\n\n  8. Termination.\n\n  You may not propagate or modify a covered work except as expressly\nprovided under this License.  Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\n  However, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.\n\n  Moreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\n  Termination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License.  If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n  9. Acceptance Not Required for Having Copies.\n\n  You are not required to accept this License in order to receive or\nrun a copy of the Program.  Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance.  However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work.  These actions infringe copyright if you do\nnot accept this License.  Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n  10. Automatic Licensing of Downstream Recipients.\n\n  Each time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License.  You are not responsible\nfor enforcing compliance by third parties with this License.\n\n  An \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations.  If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\n  You may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License.  For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n  11. Patents.\n\n  A \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based.  The\nwork thus licensed is called the contributor's \"contributor version\".\n\n  A contributor's \"essential patent claims\" are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version.  For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\n  Each contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\n  In the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement).  To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\n  If you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients.  \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\n  If, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\n  A patent license is \"discriminatory\" if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License.  You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.\n\n  Nothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n  12. No Surrender of Others' Freedom.\n\n  If conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot convey a\ncovered work so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all.  For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.\n\n  13. Remote Network Interaction; Use with the GNU General Public License.\n\n  Notwithstanding any other provision of this License, if you modify the\nProgram, your modified version must prominently offer all users\ninteracting with it remotely through a computer network (if your version\nsupports such interaction) an opportunity to receive the Corresponding\nSource of your version by providing access to the Corresponding Source\nfrom a network server at no charge, through some standard or customary\nmeans of facilitating copying of software.  This Corresponding Source\nshall include the Corresponding Source for any work covered by version 3\nof the GNU General Public License that is incorporated pursuant to the\nfollowing paragraph.\n\n  Notwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU General Public License into a single\ncombined work, and to convey the resulting work.  The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the work with which it is combined will remain governed by version\n3 of the GNU General Public License.\n\n  14. Revised Versions of this License.\n\n  The Free Software Foundation may publish revised and/or new versions of\nthe GNU Affero General Public License from time to time.  Such new versions\nwill be similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\n  Each version is given a distinguishing version number.  If the\nProgram specifies that a certain numbered version of the GNU Affero General\nPublic License \"or any later version\" applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Free Software\nFoundation.  If the Program does not specify a version number of the\nGNU Affero General Public License, you may choose any version ever published\nby the Free Software Foundation.\n\n  If the Program specifies that a proxy can decide which future\nversions of the GNU Affero General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\n  Later license versions may give you additional or different\npermissions.  However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n  15. Disclaimer of Warranty.\n\n  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\nIS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n  16. Limitation of Liability.\n\n  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGES.\n\n  17. Interpretation of Sections 15 and 16.\n\n  If the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nstate the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    PM2 Process manager for Node.JS\n    Copyright (C) 2013-2016 Strzelewicz Alexandre\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU Affero General Public License as published by\n    the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU Affero General Public License for more details.\n\n    You should have received a copy of the GNU Affero General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nAlso add information on how to contact you by electronic and paper mail.\n\n  If your software can interact with users remotely through a computer\nnetwork, you should also make sure that it provides a way for users to\nget its source.  For example, if your program is a web application, its\ninterface could display a \"Source\" link that leads users to an archive\nof the code.  There are many ways you could offer source, and different\nsolutions will be better for different programs; see section 13 for the\nspecific requirements.\n\n  You should also get your employer (if you work as a programmer) or school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary.\nFor more information on this, and how to apply and follow the GNU AGPL, see\n<http://www.gnu.org/licenses/>.\n\n\n--- ALEXANDRE STRZELEWICZ\n"
  },
  {
    "path": "LICENSE",
    "content": "GNU-AGPL-3.0.txt\n"
  },
  {
    "path": "README.md",
    "content": "<div align=\"center\">\n <br/>\n\n![https://raw.githubusercontent.com/Unitech/pm2/master/pres/pm2-logo-2.png](https://raw.githubusercontent.com/Unitech/pm2/master/pres/pm2-logo-2.png)\n\n<b>P</b>(rocess) <b>M</b>(anager) <b>2</b><br/>\n  <i>Runtime Edition</i>\n<br/><br/>\n\n\n<a title=\"PM2 Downloads\" href=\"https://npm-stat.com/charts.html?package=pm2&from=2018-01-01&to=2023-08-01\">\n  <img src=\"https://img.shields.io/npm/dm/pm2\" alt=\"Downloads per Month\"/>\n</a>\n\n<a title=\"PM2 Downloads\" href=\"https://npm-stat.com/charts.html?package=pm2&from=2018-01-01&to=2023-08-01\">\n  <img src=\"https://img.shields.io/npm/dy/pm2\" alt=\"Downloads per Year\"/>\n</a>\n\n<a href=\"https://badge.fury.io/js/pm2\" title=\"NPM Version Badge\">\n   <img src=\"https://badge.fury.io/js/pm2.svg\" alt=\"npm version\">\n</a>\n\n<br/>\n<br/>\n<br/>\n</div>\n\n\nPM2 is a production process manager for Node.js/Bun applications with a built-in load balancer. It allows you to keep applications alive forever, to reload them without downtime and to facilitate common system admin tasks.\n\nStarting an application in production mode is as easy as:\n\n```bash\n$ pm2 start app.js\n```\n\nPM2 is constantly assailed by [more than 1800 tests](https://github.com/Unitech/pm2/actions/workflows/node.js.yml).\n\nOfficial website: [https://pm2.keymetrics.io/](https://pm2.keymetrics.io/)\n\nWorks on Linux (stable) & macOS (stable) & Windows (stable). All Node.js versions are supported starting Node.js 12.X and Bun since v1\n\n\n## Installing PM2\n\n### With NPM\n\n```bash\n$ npm install pm2 -g\n```\n\n### With Bun\n\n```bash\n$ bun install pm2 -g\n```\n**Please note that you might need to symlink node to bun if you only want to use bun via `sudo ln -s /home/$USER/.bun/bin/bun /usr/bin/node`**\n\n___\n\nYou can install Node.js easily with [NVM](https://github.com/nvm-sh/nvm#installing-and-updating) or [FNM](https://github.com/Schniz/fnm) or install Bun with `curl -fsSL https://bun.sh/install | bash`\n\n### Start an application\n\nYou can start any application (Node.js, Bun, and also Python, Ruby, binaries in $PATH...) like that:\n\n```bash\n$ pm2 start app.js\n```\n\nYour app is now daemonized, monitored and kept alive forever.\n\n### Managing Applications\n\nOnce applications are started you can manage them easily:\n\n![Process listing](https://github.com/Unitech/pm2/raw/master/pres/pm2-ls-v2.png)\n\nTo list all running applications:\n\n```bash\n$ pm2 list\n```\n\nManaging apps is straightforward:\n\n```bash\n$ pm2 stop     <app_name|namespace|id|'all'|json_conf>\n$ pm2 restart  <app_name|namespace|id|'all'|json_conf>\n$ pm2 delete   <app_name|namespace|id|'all'|json_conf>\n```\n\nTo have more details on a specific application:\n\n```bash\n$ pm2 describe <id|app_name>\n```\n\nTo monitor logs, custom metrics, application information:\n\n```bash\n$ pm2 monit\n```\n\n[More about Process Management](https://pm2.keymetrics.io/docs/usage/process-management/)\n\n### Cluster Mode: Node.js Load Balancing & Zero Downtime Reload\n\nThe Cluster mode is a special mode when starting a Node.js application, it starts multiple processes and load-balance HTTP/TCP/UDP queries between them. This increase overall performance (by a factor of x10 on 16 cores machines) and reliability (faster socket re-balancing in case of unhandled errors).\n\n![Framework supported](https://raw.githubusercontent.com/Unitech/PM2/master/pres/cluster.png)\n\nStarting a Node.js application in cluster mode that will leverage all CPUs available:\n\n```bash\n$ pm2 start api.js -i <processes>\n```\n\n`<processes>` can be `'max'`, `-1` (all cpu minus 1) or a specified number of instances to start.\n\n**Zero Downtime Reload**\n\nHot Reload allows to update an application without any downtime:\n\n```bash\n$ pm2 reload all\n```\n\n[More informations about how PM2 make clustering easy](https://pm2.keymetrics.io/docs/usage/cluster-mode/)\n\n### Container Support\n\nWith the drop-in replacement command for `node`, called `pm2-runtime`, run your Node.js application in a hardened production environment.\nUsing it is seamless:\n\n```\nRUN npm install pm2 -g\nCMD [ \"pm2-runtime\", \"npm\", \"--\", \"start\" ]\n```\n\n[Read More about the dedicated integration](https://pm2.keymetrics.io/docs/usage/docker-pm2-nodejs/)\n\n### Host monitoring speedbar\n\nPM2 allows to monitor your host/server vitals with a monitoring speedbar.\n\nTo enable host monitoring:\n\n```bash\n$ pm2 set pm2:sysmonit true\n$ pm2 update\n```\n\n![Framework supported](https://raw.githubusercontent.com/Unitech/PM2/master/pres/vitals.png)\n\n### Terminal Based Monitoring\n\n![Monit](https://github.com/Unitech/pm2/raw/master/pres/pm2-monit.png)\n\nMonitor all processes launched straight from the command line:\n\n```bash\n$ pm2 monit\n```\n\n### Log Management\n\nTo consult logs just type the command:\n\n```bash\n$ pm2 logs\n```\n\nStandard, Raw, JSON and formated output are available.\n\nExamples:\n\n```bash\n$ pm2 logs APP-NAME       # Display APP-NAME logs\n$ pm2 logs --json         # JSON output\n$ pm2 logs --format       # Formated output\n\n$ pm2 flush               # Flush all logs\n$ pm2 reloadLogs          # Reload all logs\n```\n\nTo enable log rotation install the following module\n\n```bash\n$ pm2 install pm2-logrotate\n```\n\n[More about log management](https://pm2.keymetrics.io/docs/usage/log-management/)\n\n### Startup Scripts Generation\n\nPM2 can generate and configure a Startup Script to keep PM2 and your processes alive at every server restart.\n\nInit Systems Supported: **systemd**, **upstart**, **launchd**, **rc.d**\n\n```bash\n# Generate Startup Script\n$ pm2 startup\n\n# Freeze your process list across server restart\n$ pm2 save\n\n# Remove Startup Script\n$ pm2 unstartup\n```\n\n[More about Startup Scripts Generation](https://pm2.keymetrics.io/docs/usage/startup/)\n\n### Updating PM2\n\n```bash\n# Install latest PM2 version\n$ npm install pm2@latest -g\n# Save process list, exit old PM2 & restore all processes\n$ pm2 update\n```\n\n*PM2 updates are seamless*\n\n## PM2+ Monitoring\n\nIf you manage your apps with PM2, PM2+ makes it easy to monitor and manage apps across servers.\n\n![https://app.pm2.io/](https://pm2.io/img/app-overview.png)\n\nFeel free to try it:\n\n[Discover the monitoring dashboard for PM2](https://app.pm2.io/)\n\nThanks in advance and we hope that you like PM2!\n\n## CHANGELOG\n\n[CHANGELOG](https://github.com/Unitech/PM2/blob/master/CHANGELOG.md)\n\n## Contributors\n\n[Contributors](http://pm2.keymetrics.io/hall-of-fame/)\n\n## License\n\nPM2 is made available under the terms of the GNU Affero General Public License 3.0 (AGPL 3.0).\nFor other licenses [contact us](mailto:contact@keymetrics.io).\n"
  },
  {
    "path": "bin/pm2",
    "content": "#!/usr/bin/env node\n\nrequire('../lib/binaries/CLI.js');\n"
  },
  {
    "path": "bin/pm2-dev",
    "content": "#!/usr/bin/env node\n\nrequire('../lib/binaries/DevCLI.js');\n"
  },
  {
    "path": "bin/pm2-docker",
    "content": "#!/usr/bin/env node\n\nrequire('../lib/binaries/Runtime4Docker.js');\n"
  },
  {
    "path": "bin/pm2-runtime",
    "content": "#!/usr/bin/env node\n\nrequire('../lib/binaries/Runtime4Docker.js');\n"
  },
  {
    "path": "bin/pm2-windows",
    "content": "#!/usr/bin/env node\n\nrequire('../lib/binaries/CLI.js');\n"
  },
  {
    "path": "bin/pm2.ps1",
    "content": "# pm2.ps1\n$pm2Path = Join-Path $PSScriptRoot \"../lib/binaries/CLI.js\"\nnode $pm2Path $args\n"
  },
  {
    "path": "constants.js",
    "content": "/**\n * Copyright 2013-2022 the PM2 project authors. All rights reserved.\n * Use of this source code is governed by a license that\n * can be found in the LICENSE file.\n */\n\nvar debug  = require('debug')('pm2:conf');\nvar p      = require('path');\nvar util   = require('util');\nvar chalk  = require('ansis');\n\n/**\n * Get PM2 path structure\n */\nvar path_structure = require('./paths.js')(process.env.OVER_HOME);\n\n/**\n * Constants variables used by PM2\n */\nvar csts = {\n  PREFIX_MSG              : chalk.green('[PM2] '),\n  PREFIX_MSG_INFO         : chalk.cyan('[PM2][INFO] '),\n  PREFIX_MSG_ERR          : chalk.red('[PM2][ERROR] '),\n  PREFIX_MSG_MOD          : chalk.bold.green('[PM2][Module] '),\n  PREFIX_MSG_MOD_ERR      : chalk.red('[PM2][Module][ERROR] '),\n  PREFIX_MSG_WARNING      : chalk.yellow('[PM2][WARN] '),\n  PREFIX_MSG_SUCCESS      : chalk.cyan('[PM2] '),\n\n  PM2_IO_MSG : chalk.cyan('[PM2 I/O]'),\n  PM2_IO_MSG_ERR : chalk.red('[PM2 I/O]'),\n\n  TEMPLATE_FOLDER         : p.join(__dirname, 'lib/templates'),\n\n  APP_CONF_DEFAULT_FILE   : 'ecosystem.config.js',\n  APP_CONF_TPL            : 'ecosystem.tpl',\n  APP_CONF_TPL_SIMPLE     : 'ecosystem-simple.tpl',\n  SAMPLE_CONF_FILE        : 'sample-conf.js',\n  LOGROTATE_SCRIPT        : 'logrotate.d/pm2',\n\n  DOCKERFILE_NODEJS       : 'Dockerfiles/Dockerfile-nodejs.tpl',\n  DOCKERFILE_JAVA         : 'Dockerfiles/Dockerfile-java.tpl',\n  DOCKERFILE_RUBY         : 'Dockerfiles/Dockerfile-ruby.tpl',\n\n  SUCCESS_EXIT            : 0,\n  ERROR_EXIT              : 1,\n  CODE_UNCAUGHTEXCEPTION  : 1,\n\n  IS_BUN                  : typeof Bun !== 'undefined',\n  IS_WINDOWS              : (process.platform === 'win32' || process.platform === 'win64' || /^(msys|cygwin)$/.test(process.env.OSTYPE)),\n  ONLINE_STATUS           : 'online',\n  STOPPED_STATUS          : 'stopped',\n  STOPPING_STATUS         : 'stopping',\n  WAITING_RESTART         : 'waiting restart',\n  LAUNCHING_STATUS        : 'launching',\n  ERRORED_STATUS          : 'errored',\n  ONE_LAUNCH_STATUS       : 'one-launch-status',\n\n  CLUSTER_MODE_ID         : 'cluster_mode',\n  FORK_MODE_ID            : 'fork_mode',\n\n  ENABLE_GIT_PARSING      : false,\n  LOW_MEMORY_ENVIRONMENT  : process.env.PM2_OPTIMIZE_MEMORY || false,\n\n  MACHINE_NAME            : process.env.INSTANCE_NAME || process.env.MACHINE_NAME || process.env.PM2_MACHINE_NAME,\n  SECRET_KEY              : process.env.KEYMETRICS_SECRET || process.env.PM2_SECRET_KEY || process.env.SECRET_KEY,\n  PUBLIC_KEY              : process.env.KEYMETRICS_PUBLIC || process.env.PM2_PUBLIC_KEY || process.env.PUBLIC_KEY,\n  KEYMETRICS_ROOT_URL     : process.env.KEYMETRICS_NODE || process.env.PM2_APM_ADDRESS || process.env.ROOT_URL || process.env.INFO_NODE || 'root.keymetrics.io',\n\n\n  PM2_BANNER       : '../lib/motd',\n  PM2_UPDATE       : '../lib/API/pm2-plus/pres/motd.update',\n  DEFAULT_MODULE_JSON     : 'package.json',\n\n  MODULE_BASEFOLDER: 'module',\n  MODULE_CONF_PREFIX: 'module-db-v2',\n  MODULE_CONF_PREFIX_TAR: 'tar-modules',\n\n  EXP_BACKOFF_RESET_TIMER : parseInt(process.env.EXP_BACKOFF_RESET_TIMER) || 30000,\n  REMOTE_PORT_TCP         : isNaN(parseInt(process.env.KEYMETRICS_PUSH_PORT)) ? 80 : parseInt(process.env.KEYMETRICS_PUSH_PORT),\n  REMOTE_PORT             : 41624,\n  REMOTE_HOST             : 's1.keymetrics.io',\n  SEND_INTERVAL           : 1000,\n  RELOAD_LOCK_TIMEOUT     : parseInt(process.env.PM2_RELOAD_LOCK_TIMEOUT) || 30000,\n  GRACEFUL_TIMEOUT        : parseInt(process.env.PM2_GRACEFUL_TIMEOUT) || 8000,\n  GRACEFUL_LISTEN_TIMEOUT : parseInt(process.env.PM2_GRACEFUL_LISTEN_TIMEOUT) || 3000,\n  LOGS_BUFFER_SIZE        : 8,\n  CONTEXT_ON_ERROR        : 2,\n  AGGREGATION_DURATION    : process.env.PM2_DEBUG || process.env.NODE_ENV === 'local_test' || process.env.NODE_ENV === 'development' ? 3000 : 5 * 60000,\n  TRACE_FLUSH_INTERVAL    : process.env.PM2_DEBUG || process.env.NODE_ENV === 'local_test' ? 1000 : 60000,\n\n  // Concurrent actions when doing start/restart/reload\n  CONCURRENT_ACTIONS      : (function() {\n    var concurrent_actions = parseInt(process.env.PM2_CONCURRENT_ACTIONS) || 2;\n    debug('Using %d parallelism (CONCURRENT_ACTIONS)', concurrent_actions);\n    return concurrent_actions;\n  })(),\n\n  DEBUG                   : process.env.PM2_DEBUG || false,\n  WEB_IPADDR              : process.env.PM2_API_IPADDR || '0.0.0.0',\n  WEB_PORT                : parseInt(process.env.PM2_API_PORT)  || 9615,\n  WEB_STRIP_ENV_VARS      : process.env.PM2_WEB_STRIP_ENV_VARS || false,\n  MODIFY_REQUIRE          : process.env.PM2_MODIFY_REQUIRE || false,\n\n  WORKER_INTERVAL         : process.env.PM2_WORKER_INTERVAL || 30000,\n  KILL_TIMEOUT            : process.env.PM2_KILL_TIMEOUT || 1600,\n  KILL_SIGNAL             : process.env.PM2_KILL_SIGNAL || 'SIGINT',\n  KILL_USE_MESSAGE        : process.env.PM2_KILL_USE_MESSAGE || false,\n\n  PM2_PROGRAMMATIC        : typeof(process.env.pm_id) !== 'undefined' || process.env.PM2_PROGRAMMATIC,\n  PM2_LOG_DATE_FORMAT     : process.env.PM2_LOG_DATE_FORMAT !== undefined ? process.env.PM2_LOG_DATE_FORMAT : 'YYYY-MM-DDTHH:mm:ss'\n\n};\n\nmodule.exports = Object.assign(csts, path_structure);\n"
  },
  {
    "path": "examples/api-pm2/README.md",
    "content": "\n# PM2 API\n\nHere is an example of the PM2 API:\n\n```\n$ node api.js\n```\n\nWill delete all apps, will start http.js, and restart http\n\nThen you will see that the listing shows http app, restarted one time:\n\n```\n$ pm2 list\n┌──────────┬────┬──────┬──────┬────────┬─────────┬────────┬─────┬───────────┬─────────┬──────────┐\n│ App name │ id │ mode │ pid  │ status │ restart │ uptime │ cpu │ mem       │ user    │ watching │\n├──────────┼────┼──────┼──────┼────────┼─────────┼────────┼─────┼───────────┼─────────┼──────────┤\n│ http     │ 0  │ fork │ 7668 │ online │ 1       │ 2s     │ 0%  │ 34.2 MB   │ unitech │ disabled │\n└──────────┴────┴──────┴──────┴────────┴─────────┴────────┴─────┴───────────┴─────────┴──────────┘\n Use `pm2 show <id|name>` to get more details about an app\n```\n"
  },
  {
    "path": "examples/api-pm2/api.js",
    "content": "\n\nvar pm2 = require('../..');\n\npm2.delete('all', function(err) {\n  if (err) {\n    console.error(err);\n    return pm2.disconnect();\n  }\n\n  pm2.start('http.js', function(err, app) {\n    if (err) {\n      console.error(err);\n      return pm2.disconnect();\n    }\n\n    console.log('Process HTTP has been started');\n\n    pm2.restart('http', function(err, app) {\n      if (err) {\n        console.error(err);\n        return pm2.disconnect();\n      }\n\n      console.log('Process Restarted');\n      return pm2.disconnect();\n    });\n  });\n});\n"
  },
  {
    "path": "examples/api-pm2/http.js",
    "content": "\nvar http = require('http');\n\nvar server = http.createServer(function(req, res) {\n  res.writeHead(200);\n  res.end('hey');\n}).listen(process.env.PORT || 8000, function() {\n  console.log('App listening on port %d', server.address().port);\n});\n"
  },
  {
    "path": "examples/c-compile/hello.c",
    "content": "#include <stdio.h>\nint main()\n{\n  // printf() displays the string inside quotation\n  printf(\"Hello, Wss   asdsad!\\n\");\n  return 0;\n}\n"
  },
  {
    "path": "examples/cluster-http/README.md",
    "content": "\nTo start http application in cluster mode:\n\n```bash\n$ pm2 start ecosystem.config.js\n# OR\n$ pm2 start http.js -i max\n```\n"
  },
  {
    "path": "examples/cluster-http/ecosystem.config.js",
    "content": "module.exports = {\n  apps : [{\n    name   : 'clustered_http',\n    script : './http.js',\n    instances : 'max',\n    exec_mode : 'cluster',\n    env : {\n      PORT : 8002\n    }\n  }, {\n    name : 'forked_app',\n    script : './http.js',\n    env : {\n      PORT : 8001\n    }\n  }]\n}\n"
  },
  {
    "path": "examples/cluster-http/http-no-exit.js",
    "content": "\nvar http = require('http');\n\nvar server = http.createServer(function(req, res) {\n  res.writeHead(200);\n  res.end('hey');\n}).listen(process.env.PORT || 8089, '0.0.0.0', function() {\n  console.log('App listening on port %d', server.address().port);\n});\n\nprocess.on('SIGINT', () => {\n})\n"
  },
  {
    "path": "examples/cluster-http/http.js",
    "content": "\nvar http = require('http');\n\nvar server = http.createServer(function(req, res) {\n  res.writeHead(200);\n  console.log(`New query`)\n  res.end('hey');\n}).listen(process.env.PORT || 8089, '0.0.0.0', function() {\n  console.log('App listening on port 8089');\n});\n"
  },
  {
    "path": "examples/cluster-tcp/README.md",
    "content": "\nTo start tcp application in cluster mode:\n\n```bash\n$ pm2 start ecosystem.config.js\n# OR\n$ pm2 start tcp.js -i max\n```\n"
  },
  {
    "path": "examples/cluster-tcp/http.js",
    "content": "\nvar http = require('http');\n\nvar server = http.createServer(function(req, res) {\n  res.writeHead(200);\n  res.end('hey');\n}).listen(process.env.PORT || 8000, function() {\n  console.log('App listening on port %d', server.address().port);\n});\n"
  },
  {
    "path": "examples/cluster-tcp/process.config.js",
    "content": "module.exports = {\n  apps : [{\n    name   : 'clustered_tcp',\n    script : './tcp.js',\n    instances : 'max',\n    exec_mode : 'cluster',\n    env : {\n      PORT : 8002\n    }\n  }, {\n    name : 'forked_tcp',\n    script : './tcp.js',\n    env : {\n      PORT : 8001\n    }\n  }]\n}\n"
  },
  {
    "path": "examples/cluster-tcp/tcp.js",
    "content": "var net = require('net');\n\nvar server = net.createServer(function (socket) {\n  socket.write('Welcome to the Telnet server of the process' + (process.env.NODE_APP_INSTANCE || 'must be run on pm2'));\n}).listen(process.env.PORT || 8888, function() {\n  console.log('Listening on port %s', server.address().port);\n});\n"
  },
  {
    "path": "examples/custom-nodejs-version/ecosystem.config.js",
    "content": "module.exports = {\n  /**\n   * Application configuration section\n   * http://pm2.keymetrics.io/docs/usage/application-declaration/\n   */\n  apps : [\n    // First application\n    {\n      name      : \"API\",\n      script    : \"http.js\",\n      interpreter : \"node@6.9.0\"\n    }\n  ]\n}\n"
  },
  {
    "path": "examples/custom-nodejs-version/http.js",
    "content": "\nvar http = require('http');\n\nconsole.log(process.version);\n\nvar server = http.createServer(function(req, res) {\n  res.writeHead(200);\n  res.end('hey');\n}).listen(process.env.PORT || 8000, function() {\n  console.log('App listening on port %d in env %s', process.env.PORT || 8000, process.env.NODE_ENV);\n\n  // 1# Notify application ready\n  setTimeout(function() {\n    process.send('ready');\n  }, 2000);\n\n});\n\n// // 2# Handle on Exit\nprocess.on('SIGINT', function() {\n  console.log('Cleanup on exit');\n\n  server.on('close', function() {\n    console.log('Connections closed');\n    process.exit(0);\n  });\n\n  server.close();\n});\n"
  },
  {
    "path": "examples/docker-pm2/Dockerfile",
    "content": "FROM keymetrics/pm2:latest-alpine\n\n# Bundle APP files\nCOPY ./app /app\nWORKDIR /app\n\n# Install app dependencies\nENV NPM_CONFIG_LOGLEVEL warn\nRUN npm install --production\n\nENV KEYMETRICS_SECRET xxxx\nENV KEYMETRICS_PUBLIC yyyy\n\nCMD [ \"pm2-runtime\", \"process.config.js\" ]\n"
  },
  {
    "path": "examples/docker-pm2/README.md",
    "content": "\nHere is an example on using pm2 inside container with the official image and pm2-runtime.\n\nTo build & run it:\n\n```bash\n# build image\n$ docker build -t docker-pm2-test .\n# list images\n$ docker images\n# run image\n$ docker run docker-pm2-test\n```\n\nThere is also KEYMETRICS integration via KEYMETRICS_SECRET and KEYMETRICS_PUBLIC keys\n"
  },
  {
    "path": "examples/docker-pm2/app/app.js",
    "content": "const express = require('express')\nconst app = express()\n\napp.get('/', (req, res) => res.send('Hello World!'))\n\napp.listen(3000, () => console.log('Example app listening on port 3000!'))\n"
  },
  {
    "path": "examples/docker-pm2/app/package.json",
    "content": "{\n  \"name\": \"app\",\n  \"version\": \"1.0.0\",\n  \"description\": \"\",\n  \"main\": \"index.js\",\n  \"scripts\": {\n    \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n  },\n  \"author\": \"\",\n  \"license\": \"ISC\",\n  \"dependencies\": {\n    \"express\": \"^4.16.2\"\n  }\n}\n"
  },
  {
    "path": "examples/docker-pm2/app/process.config.js",
    "content": "module.exports = {\n  apps : [{\n    name   : \"express-app\",\n    script : \"./app.js\"\n  }]\n}\n"
  },
  {
    "path": "examples/echo/log.js",
    "content": "\n\nsetInterval(function() {\n  console.log('out');\n}, 1000);\n\nsetInterval(function() {\n  console.error('err');\n}, 1000);\n"
  },
  {
    "path": "examples/echo/stdout.js",
    "content": "\nsetInterval(function() {\n  process.stdout.write('ooo')\n}, 100)\n"
  },
  {
    "path": "examples/ecosystem-file/README.md",
    "content": "\nHere we have 3 applications (apps folder) that we can start with process file.\nThese process file can be of different format, javascript, json or yaml:\n\n```\n.\n├── apps\n│   ├── connection_check.sh\n│   ├── http.js\n│   └── worker.js\n├── process.config.js\n├── process.json\n└── process.yml\n```\n\nTo start them:\n\n```bash\n$ pm2 start process.config.js\n$ pm2 delete all\n$ pm2 start process.json\n$ pm2 delete all\n$ pm2 start process.yml\n```\n"
  },
  {
    "path": "examples/ecosystem-file/apps/connection_check.sh",
    "content": "#!/bin/bash\n\nfoo() {\n    echo \"Hello Bash!\"\n}\n\nwhile true; do foo; sleep 2; done\n"
  },
  {
    "path": "examples/ecosystem-file/apps/http.js",
    "content": "\nvar http = require('http');\n\nvar server = http.createServer(function(req, res) {\n  res.writeHead(200);\n  res.end('hey');\n}).listen(process.env.PORT || 8000, function() {\n  console.log('App listening on port %d in env %s', process.env.PORT || 8000, process.env.NODE_ENV);\n});\n"
  },
  {
    "path": "examples/ecosystem-file/apps/worker.js",
    "content": "\nsetInterval(function() {\n  console.log('Worker has finished his job.');\n  console.error('Worker has finished his job.');\n}, 1000);\n"
  },
  {
    "path": "examples/ecosystem-file/process.config.js",
    "content": "module.exports = {\n  apps : [{\n    name               : 'HTTP-API',\n    script             : 'apps/http.js',\n    exec_mode          : 'cluster',\n    instances          : 'max',\n    max_memory_restart : '260M',\n\n    ignore_watch       : ['node_modules'],\n    env : {\n      NODE_ENV : 'normal'\n    },\n    env_production : {\n      NODE_ENV : 'production'\n    }\n  }, {\n    name               : 'Worker',\n    script             : 'apps/worker.js',\n    err_file : 'toto-err.log'\n  }, {\n    name               : 'Checks',\n    script             : 'apps/connection_check.sh'\n  }]\n}\n"
  },
  {
    "path": "examples/ecosystem-file/process.json",
    "content": "{\n  apps : [{\n    name               : 'HTTP-API',\n    script             : 'apps/http.js',\n    exec_mode          : 'cluster',\n    instances          : 'max',\n    max_memory_restart : '260M',\n\n    ignore_watch       : ['node_modules'],\n    watch              : true,\n    env : {\n      NODE_ENV : 'normal'\n    },\n    env_production : {\n      NODE_ENV : 'production'\n    }\n  }, {\n    name               : 'Worker',\n    script             : 'apps/worker.js'\n  }, {\n    name               : 'Checks',\n    script             : 'apps/connection_check.sh'\n  }]\n}\n"
  },
  {
    "path": "examples/ecosystem-file/process.yml",
    "content": "apps:\n  - name : 'HTTP-API'\n    script: 'apps/http.js'\n    instances: 'max'\n    max_memory_restart: '260M'\n    watch : true\n    ignore_watch : ['node_modules']\n    env:\n      NODE_ENV : 'development'\n    env_production:\n      NODE_ENV : 'production'\n  - name : 'Worker'\n    script: 'apps/worker.js'\n  - name : 'Checks'\n    script : 'apps/connection_check.sh'\n"
  },
  {
    "path": "examples/esm/addTwo.js",
    "content": "// addTwo.mjs\nfunction addTwo(num) {\n  return num + 2;\n}\n\nexport { addTwo };\n"
  },
  {
    "path": "examples/esm/app.js",
    "content": "\n// app.mjs\nimport { addTwo } from './addTwo.js';\n\n// Prints: 6\nconsole.log(addTwo(4));\n"
  },
  {
    "path": "examples/esm/package.json",
    "content": "{\n  \"name\": \"esm\",\n  \"version\": \"1.0.0\",\n  \"description\": \"\",\n  \"main\": \"addTwo.js\",\n  \"type\": \"module\",\n  \"scripts\": {\n    \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n  },\n  \"author\": \"\",\n  \"license\": \"ISC\"\n}\n"
  },
  {
    "path": "examples/exception/fast.js",
    "content": "setTimeout(() => {\n  throw new Error('err')\n}, 100)\n"
  },
  {
    "path": "examples/exception/slow.js",
    "content": "setTimeout(() => {\n  throw new Error('err')\n}, 1500)\n"
  },
  {
    "path": "examples/expose-custom-metrics/README.md",
    "content": "\nTo expose custom metrics from your code and monitor it from CLI:\n\n```bash\n$ pm2 start process.config.js\n```\n\nThen to monitor metrics:\n\n```bash\n$ pm2 monit\n```\n\nOr\n\n```bash\n$ pm2 show 0\n```\n"
  },
  {
    "path": "examples/expose-custom-metrics/process-metrics.js",
    "content": "\nvar Probe = require('pmx').probe();\n\nvar i = 0;\n\nvar metric = Probe.metric({\n  name    : 'Metric',\n  value   : function() {\n    return i;\n  }\n});\n\nsetInterval(function() {\n  i++;\n}, 100);\n\n\nvar meter = Probe.meter({\n  name    : 'Meter'\n});\n\nsetInterval(function() {\n  meter.mark()\n}, 200);\n\nvar histo = Probe.histogram({\n  name    : 'Histogram'\n});\n\nvar latency;\n\nsetInterval(function() {\n  latency = Math.round(Math.random() * 100);\n  histo.update(latency);\n}, 100);\n\nvar counter = Probe.counter({\n  name    : 'Counter'\n});\n\nsetInterval(function() {\n  counter.inc()\n}, 500);\n"
  },
  {
    "path": "examples/expose-custom-metrics/process.config.js",
    "content": "module.exports = {\n  apps : [{\n    script : 'process-metrics.js'\n  }]\n}\n"
  },
  {
    "path": "examples/expose-triggerable-functions/index.js",
    "content": "\nvar pmx = require('pmx');\n\npmx.action('ping', function(reply) {\n  return reply({ 'pong' : 'hehe' })\n});\n\npmx.action('param', function(data, reply) {\n  return reply({ data : data })\n});\n\nsetInterval(function() {\n}, 1000);\n"
  },
  {
    "path": "examples/expose-triggerable-functions/process.yml",
    "content": "- script : './index.js'\n  name : 'custom-actions'\n"
  },
  {
    "path": "examples/expose-triggerable-functions/trigger-param.js",
    "content": "var pm2 = require('../..');\n\npm2.trigger('0', 'param', { some : 'data' }, function(err, res) {\n  var rep_1 = res[0];\n  console.log(`Got result from ${rep_1.process.name}`);\n  console.log(rep_1.data);\n\n  pm2.disconnect();\n});\n"
  },
  {
    "path": "examples/expose-triggerable-functions/trigger-ping.js",
    "content": "var pm2 = require('../..');\n\npm2.trigger('0', 'ping', function(err, res) {\n  var rep_1 = res[0];\n  console.log(`Got result from ${rep_1.process.name}`);\n  console.log(rep_1.data);\n\n  pm2.disconnect();\n});\n"
  },
  {
    "path": "examples/import/circle.js",
    "content": "const PI = 3.14159265359;\n\nexport function area(radius) {\n  return (radius ** 2) * PI;\n}\n\nexport function circumference(radius) {\n  return 2 * radius * PI;\n}\n"
  },
  {
    "path": "examples/import/index.js",
    "content": "import { area, circumference } from './circle.js';\n\nconst r = 3;\n\nconsole.log(`Circle with radius ${r} has\n  area: ${area(r)};\n  circunference: ${circumference(r)}`);\n"
  },
  {
    "path": "examples/import/package.json",
    "content": "{\n  \"name\": \"import\",\n  \"version\": \"1.0.0\",\n  \"description\": \"\",\n  \"main\": \"index.js\",\n  \"type\":\"module\",\n  \"scripts\": {\n    \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n  },\n  \"author\": \"\",\n  \"license\": \"ISC\"\n}\n"
  },
  {
    "path": "examples/interact-via-stdin/README.md",
    "content": "\nTo interact with an app readin on stdin:\n\n\n```\n$ pm2 start stdin.js\n```\n\nThen to attach to it:\n\n```\n$ pm2 attach 0\n```\n"
  },
  {
    "path": "examples/interact-via-stdin/stdin.js",
    "content": "\nconst readline = require('readline');\n\nconst rl = readline.createInterface({\n  input: process.stdin,\n  output: process.stdout\n});\n\nrl.prompt();\n\nrl.on('line', (data) =>{\n  console.log('Received data');\n  console.log(data);\n});\n\nsetInterval(() => {\n  //console.log('Yes');\n}, 2000);\n"
  },
  {
    "path": "examples/misc-examples/001-test.js",
    "content": "\n\n\nsetInterval(function() {\n  console.log('log message from echo.js');\n}, 1500);\n\nsetTimeout(function() {\n  setInterval(function() {\n    console.error('err msg from echo.js');\n  }, 1500);\n}, 750);\n"
  },
  {
    "path": "examples/misc-examples/apps/all-pm2.json",
    "content": "[{\n  \"name\"      : \"echo\",\n  \"script\"    : \"./examples/echo.js\",\n  \"instances\" : \"1\"\n},{\n  \"name\"      : \"api\",\n  \"script\"    : \"./examples/child.js\",\n  \"instances\" : \"4\",\n  \"error_file\" : \"./examples/child-err.log\",\n  \"out_file\" : \"./examples/child-out.log\"\n}]\n"
  },
  {
    "path": "examples/misc-examples/apps/args.json",
    "content": "{\n  \"name\"      : \"echo\",\n  \"script\"    : \"./examples/args.js\",\n  \"instances\" : \"1\",\n  \"args\"      : \"['--toto=heya coco', '-d', '1']\",\n  \"cron_restart\" : \"* * * * * *\"\n}\n"
  },
  {
    "path": "examples/misc-examples/apps/auto-kill-echo.json",
    "content": "{\n  \"name\" : \"auto-kill\",\n  \"script\" : \"./examples/echokill.js\",\n  \"max\" : \"10\"\n}\n"
  },
  {
    "path": "examples/misc-examples/apps/cluster-pm2.json",
    "content": "{\n  \"script\" : \"./examples/child.js\",\n  \"error_file\" : \"errLog.log\",\n  \"out_file\" : \"outLog.log\",\n  \"pid_file\" : \"child\",\n  \"instances\" : \"4\",\n  \"min_uptime\" : \"10\",\n  \"max_restarts\" : \"4\"\n}\n"
  },
  {
    "path": "examples/misc-examples/apps/cron-pm2.json",
    "content": "{\n  \"name\"      : \"echo\",\n  \"script\"    : \"./examples/args.js\",\n  \"instances\" : \"1\",\n  \"args\"      : \"['--toto=heya coco', '-d', '1']\",\n  \"cron_restart\" : \"* * * * * *\"\n}\n"
  },
  {
    "path": "examples/misc-examples/apps/default-path-echo.json",
    "content": "{\n  \"name\" : \"echo-default\",\n  \"script\" : \"examples/echo.js\",\n  \"max\" : \"1\"\n}\n"
  },
  {
    "path": "examples/misc-examples/apps/echo-pm2.json",
    "content": "{\n  \"script\" : \"examples/echo.js\",\n  \"error_file\" : \"errEcho.log\",\n  \"out_file\" : \"outEcho.log\",\n  \"name\" : \"ok\",\n  \"pid_file\" : \"echo.pid\",\n  \"max\" : \"1\",\n  \"exec_mode\" : \"cluster_mode\",\n  \"port\" : \"9001\",\n  \"env_variable\" : \"TOTO\"\n}\n"
  },
  {
    "path": "examples/misc-examples/apps/env-pm2.json",
    "content": "{\n  \"script\" : \"examples/env.js\",\n  \"error_file\" : \"errEcho.log\",\n  \"out_file\" : \"outEcho.log\",\n  \"name\" : \"ok\",\n  \"pid_file\" : \"echo.pid\",\n  \"max\" : \"1\",\n  \"exec_mode\" : \"cluster_mode\",\n  \"port\" : \"9001\",\n  \"env_variable\" : \"TOTO\",\n  \"TEST_VARIABLE\" : \"YESSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSIR\"\n}\n"
  },
  {
    "path": "examples/misc-examples/apps/killfast.json",
    "content": "{\n  \"min_uptime\" : \"100\",\n  \"max_restarts\" : \"400\",\n  \"name\" : \"auto-kill\",\n  \"script\" : \"./examples/killfast.js\"\n}\n"
  },
  {
    "path": "examples/misc-examples/apps/multi-pm2.json",
    "content": "[{\n  \"name\"      : \"echo\",\n  \"script\"    : \"./examples/args.js\",\n  \"instances\" : \"1\",\n  \"args\"      : \"['--toto=heya coco', '-d', '1']\",\n  \"cron_restart\" : \"* * * * * *\"\n}]\n"
  },
  {
    "path": "examples/misc-examples/apps/no-name-echo.json",
    "content": "{\n  \"script\" : \"examples/echo.js\",\n  \"max\" : \"1\"\n}\n"
  },
  {
    "path": "examples/misc-examples/args.js",
    "content": "\nprocess.argv.forEach(function (val, index, array) {\n  console.log(index + ': ' + val);\n});\n\nconsole.log('Argv2 = ', process.argv[2]);\n\nsetInterval(function() {\n  console.log('HERE ARE MY ARGS !!! = ', process.argv);\n}, 800);\n"
  },
  {
    "path": "examples/misc-examples/auto-restart-all.js",
    "content": "\n\n\nvar pm2 = require('..');\n\nsetTimeout(function() {\n  pm2.connect(function() {\n    pm2.restart('all', function() {\n      pm2.disconnect(function() {\n\n      });\n    });\n  });\n}, 3000);\n"
  },
  {
    "path": "examples/misc-examples/auto-restart-threshold.js",
    "content": "var pmx = require('pmx');\n\nvar Probe = pmx.probe();\n\nvar i = 0;\n\nvar val = Probe.metric({\n  name : 'test',\n  value : function() {\n    return i;\n  },\n  alert : {\n    mode : 'threshold',\n    value : 20,\n    msg : 'more than 20',\n    func : function() {\n      console.log('exiting');\n      process.exit(1);\n    }\n  }\n});\n\n\n\nsetInterval(function() {\n  console.log(i);\n  i++;\n}, 200);\n"
  },
  {
    "path": "examples/misc-examples/auto-save.js",
    "content": ""
  },
  {
    "path": "examples/misc-examples/beforeExit.js",
    "content": "\nvar stopped = false;\n\nfunction work() {\n  console.log('working');\n  !stopped && setTimeout(work, 1000);\n}\n\nfunction stop() {\n  console.log('shutting down');\n  stopped = true;\n}\n\nprocess.once('SIGINT', stop);   // CTRL-C\n\nprocess.on('beforeExit', function() {\n  console.log('exited cleanly :)');\n  process.exit(0);\n});\n\nwork();\n"
  },
  {
    "path": "examples/misc-examples/child-echo.json",
    "content": "{\n  \"apps\" : [{\n    \"script\":\"examples/child.js\",\n    \"name\":\"API-web\",\n    \"instances\":3\n  },{\n    \"script\":\"examples/echo.js\",\n    \"instances\":2\n  }],\n  \"deploy\" : {\n    \"production\" : {\n      \"user\" : \"node\",\n      \"host\" :  \"212.83.163.168\",\n      \"repo\" : \"git@github.com:Unitech/eip-vitrine.git\",\n      \"path\" : \"/var/www/test-deploy\"\n    }\n  }\n}\n"
  },
  {
    "path": "examples/misc-examples/child-env.js",
    "content": "\nvar http = require('http');\n\nhttp.createServer(function(req, res) {\n  res.writeHead(200);\n  res.end(process.env.PORT_ENV);\n}).listen(process.env.PORT_ENV);\n"
  },
  {
    "path": "examples/misc-examples/child-pm2.json",
    "content": "{\n  \"script\":\"examples/child.js\",\n  \"name\":\"SERVERONE\",\n  \"instances\":10,\n  \"error_file\":\"errfile.log\",\n  \"out_file\":\"outfile.log\",\n  \"pid_file\":\"pidfile.pid\"\n}\n"
  },
  {
    "path": "examples/misc-examples/child.js",
    "content": "\nvar http = require('http');\n\nvar server = http.createServer(function(req, res) {\n  res.writeHead(200);\n  res.end('hey');\n}).listen(process.env.PORT || 8000, function() {\n  console.log('App listening on port %d in env %s', process.env.PORT || 8000, process.env.NODE_ENV);\n});\n"
  },
  {
    "path": "examples/misc-examples/child.js-pm2.json",
    "content": "{\n  \"script\":\"examples/child.js\",\n  \"name\":\"child.js\"\n}\n"
  },
  {
    "path": "examples/misc-examples/child2.js",
    "content": "\nvar axm = require('pmx');\naxm.http();\nvar http = require('http');\n\nhttp.createServer(function(req, res) {\n  res.writeHead(200);\n  res.end('hoy');\n}).listen(8000);\n"
  },
  {
    "path": "examples/misc-examples/client.js",
    "content": "\n\nvar Wrap = require('./wrap.js');\nvar axon = require('pm2-axon');\n\nvar Module = require('module');\nWrap.wrap(Module, '_load', function(load) {\n  return function(file) {\n    return load.apply(this, arguments);\n  }\n});\n\n\nvar server = axon.socket('sub');\n\nserver.bind(8080);\n\nserver.on('bind', function() {\n  console.log('Server ready');\n});\n\nserver.on('message', function(data) {\n  console.log(data);\n});\n\n// setTimeout(function() {\n//   console.log('Closing server');\n//   server.close(function() {\n//     console.log('Closed');\n//   });\n// }, 3000);\n\nfunction setupConnection() {\n  var client = axon.socket('pub');\n\n  client.on('connect', function() {\n    console.log('Client connected');\n  });\n\n  client.on('error', function(e) {\n    console.log('Client got error', e.message);\n  });\n\n  client.on('close', function(e) {\n    console.log('Client got a close');\n  });\n\n  client.on('reconnect attempt', function(e) {\n    console.log('Reconnecting');\n  });\n\n  client.connect(8080);\n\n  this.send = function() {\n    client.send({success:true});\n  };\n\n  this.destroy = function() {\n    client.close();\n    client.removeAllListeners();\n  };\n\n  this.reconnect = function() {\n\n  };\n  return this;\n}\n\nvar connection = setupConnection();\n\nsetInterval(function() {\n  connection.send();\n}, 1000);\n\nsetInterval(function() {\n  connection.destroy();\n  connection = setupConnection();\n}, 2000);\n"
  },
  {
    "path": "examples/misc-examples/cwd.js",
    "content": "\n\nconsole.log(process.env.PWD);\nconsole.log(process.cwd());\nconsole.log(__dirname);\n\nrequire('./echo.js');\n\nconsole.log(process.cwd());\n\n"
  },
  {
    "path": "examples/misc-examples/echo.coffee",
    "content": "#!/usr/bin/env coffee\n\nsetInterval (-> console.log 'ok'), 500"
  },
  {
    "path": "examples/misc-examples/echo.js",
    "content": "\n\nsetInterval(function() {\n  console.log('log message from echo.js');\n  console.warn({ json : true });\n}, 1500);\n\nsetTimeout(function() {\n  setInterval(function() {\n    console.error('err msg from echo.js');\n  }, 1500);\n}, 750);\n"
  },
  {
    "path": "examples/misc-examples/echokill.js",
    "content": "\n\nsetInterval(function() {\n  console.log('log message from echo auto kill');\n}, 800);\n\nsetTimeout(function() {\n  console.error('error message, killing my self');\n  process.exit(10);\n}, 3000);\n"
  },
  {
    "path": "examples/misc-examples/env.js",
    "content": "setInterval(function() {\n  console.log('env NODE_ENV = ', process.env.NODE_ENV);\n}, 1000);\n"
  },
  {
    "path": "examples/misc-examples/env_args.js",
    "content": "\nconsole.log(process.env.PASSED_VARIABLE);\n"
  },
  {
    "path": "examples/misc-examples/exec_watch.json",
    "content": "[{\n  \"script\":\"examples/child.js\",\n  \"name\":\"SERVERONE\",\n  \"instances\":10,\n  \"watch\" : true,\n  \"error_file\":\"errfile.log\",\n  \"out_file\":\"outfile.log\",\n  \"pid_file\":\"pidfile.pid\"\n}]\n"
  },
  {
    "path": "examples/misc-examples/exit.js",
    "content": "\n// process.on('exit', function() {\n//   console.log('About to exit.');\n// });\n\n// process.on('uncaughtException', function(err) {\n//   console.log('Caught exception: ' + err);\n// });\n\n// process.on('SIGINT', function() {\n//   console.log('Got SIGINT.  Press Control-D to exit.');\n//   process.exit(1);\n// });\n\nvar worker = require('cluster').worker;\n\nworker.on('disconnect', function() {\n  console.log('exiting');\n});\n\n\nsetInterval(function() {\n}, 1);\n\nsetInterval(function() {\n  console.log('ok');\n}, 2000);\nconsole.log('ok');\n"
  },
  {
    "path": "examples/misc-examples/expose_method.js",
    "content": "\n/*\n * Example of usage : https://github.com/Unitech/pm2/pull/214\n */\nprocess.on(\"message\", function (msg) {\n  console.log('got message', msg);\n  if ( \"type\" in msg && msg.type === \"god:heap\" ) {\n    var heap = process.memoryUsage().heapUsed;\n    process.send({type:\"process:heap\", heap:heap});\n  }\n});\n"
  },
  {
    "path": "examples/misc-examples/graceful-exit.js",
    "content": "\n/*\n * Example of graceful exit\n *\n * $ pm2 reload all\n */\n\nprocess.on('message', function(msg) {\n  if (msg == 'shutdown') {\n    console.log('Closing all connections...');\n    setTimeout(function() {\n      console.log('Finished closing connections');\n      process.exit(0);\n    }, 1500);\n  }\n});\n\nvar http = require('http');\n\nhttp.createServer(function(req, res) {\n  res.writeHead(200);\n  console.log('got');\n  res.end('hey');\n}).listen(8000, function() {\n  console.log('listening');\n});\n"
  },
  {
    "path": "examples/misc-examples/harmony.js",
    "content": "var assert = require('assert')\n  , s = new Set()\n  ;\n\ns.add('a');\n\nassert.ok(s.has('a'));\n\nsetInterval(function() {\n  console.log(s.has('a'));\n}, 1000);\n"
  },
  {
    "path": "examples/misc-examples/http.js",
    "content": "\nvar pmx = require('pmx');\nvar conf = pmx.init({\n  http: true\n});\n\nvar http = require('http');\n\nhttp.createServer(function(req, res) {\n  res.writeHead(200);\n  res.end('hey');\n}).listen(8000);\n\nvar Probe = pmx.probe();\n\nvar value_to_inspect = 0;\n\n/**\n * .metric, .counter, .meter, .histogram are also available (cf doc)\n */\nvar val = Probe.metric({\n  name : 'test-probe',\n  value : function() {\n    return value_to_inspect;\n  },\n  /**\n   * Here we set a default value threshold, to receive a notification\n   * These options can be overriden via Keymetrics or via pm2\n   * More: http://bit.ly/1O02aap\n   */\n  alert : {\n    mode     : 'threshold',\n    value    : 20,\n    msg      : 'test-probe alert!',\n    action   : function(val) {\n      // Besides the automatic alert sent via Keymetrics\n      // You can also configure your own logic to do something\n      console.log('Value has reached %d', val);\n    }\n  }\n});\n\nsetInterval(function() {\n  // Then we can see that this value increase over the time in Keymetrics\n  value_to_inspect++;\n}, 300);\n\nprocess.on('message', function(msg) {\n  if (msg == 'shutdown') {\n    console.log('Closing all connections...');\n    setTimeout(function() {\n      console.log('Finished closing connections');\n      process.exit(0);\n    }, 500);\n  }\n});\n"
  },
  {
    "path": "examples/misc-examples/infinite-recurse.js",
    "content": "function x() {\n  x();\n}\nx();\n"
  },
  {
    "path": "examples/misc-examples/inside.js",
    "content": "\nvar pm2 = require('..');\n\npm2.connect(function() {\n  setInterval(function() {\n    pm2.restart('echo', function() {\n      console.log(arguments);\n    });\n  }, 2000);\n});\n"
  },
  {
    "path": "examples/misc-examples/inside.json",
    "content": "\n[{\n  \"name\" : \"inside\",\n  \"script\":\"examples/inside.js\"\n},{\n  \"name\" : \"echo\",\n  \"script\":\"examples/echo.js\"\n}]\n"
  },
  {
    "path": "examples/misc-examples/interact.js",
    "content": "\nvar pm2 = require('..');\n\nvar MACHINE_NAME = 'hk1';\nvar PRIVATE_KEY  = 'z1ormi95vomgq66';\nvar PUBLIC_KEY   = 'oa0m7nuhdfibi16';\n\npm2.connect(true, function() {\n  pm2.start({\n    script : '../test/fixtures/child.js',\n    name : 'production-app'\n  }, function() {\n    pm2.interact(PRIVATE_KEY, PUBLIC_KEY, MACHINE_NAME, function() {\n    });\n  });\n});\n"
  },
  {
    "path": "examples/misc-examples/json.js",
    "content": "\nsetInterval(function() {\n  console.log({\n    hey : 'hay',\n    ho : {\n      si : 'si',\n      ca : ['boum']\n    }\n  });\n  var a = {a: 'a', b: 'b'};\n  console.log(a);\n}, 1000);\n"
  },
  {
    "path": "examples/misc-examples/kill-not-so-fast.js",
    "content": "\nconsole.log('start');\n\nsetTimeout(function() {\n  console.log('exit');\n  throw new Error('Exitasdsadasdsda unacepted !!');\n}, 300);\n"
  },
  {
    "path": "examples/misc-examples/kill-slow.js",
    "content": "\nsetTimeout(function() {\n  console.log('exit');\n  process.exit(1);\n}, 1000);\n"
  },
  {
    "path": "examples/misc-examples/killfast.js",
    "content": "\nprocess.exit(1);\n"
  },
  {
    "path": "examples/misc-examples/killslow.js",
    "content": "\n\nsetTimeout(function() {\n  throw new Error('ok');\n}, 1100);\n"
  },
  {
    "path": "examples/misc-examples/killtoofast.js",
    "content": "\nconsole.log('im a kamikazy');\n\nsetInterval(function() {\n  console.log('BOUM');\n  process.exit(1);\n}, 30);\n"
  },
  {
    "path": "examples/misc-examples/leak.js",
    "content": "\nvar leak = [];\n\nsetInterval(function() {\n  for (var i = 0; i < 10; i++) {\n    var str = i.toString() + \" on a stick, short and stout!\";\n    leak.push(str);\n  }\n}, 50);\n"
  },
  {
    "path": "examples/misc-examples/malformated.json",
    "content": "{\n  \"apps\" : [{\n    \"exec_interpreter\"   : \"node,\n    \"exec_mode\"          : \"cluster_mode\",\n    \"instances\"          : 0,\n    \"log_date_format\"    : \"YYYY-MM-DD HH:mm Z\",\n    \"max_memory_restart\" : \"160\",\n    \"merge_logs\"         : true,\n    \"name\"               : \"hapi_playground\",\n    \"script\"             : \"child.js\",\n    \"cwd\"                : \"examples\",\n    \"node_args\"          : \"--harmony\",\n    \"ignore_watch\"        : [\"[\\\\/\\\\\\\\]\\\\./\", \"log\"],\n    \"watch\"              : true\n  }]\n}\n"
  },
  {
    "path": "examples/misc-examples/modulechild.js",
    "content": "\n\nconsole.log(module.parent);\n"
  },
  {
    "path": "examples/misc-examples/moduleparent.js",
    "content": "\n\nvar a =require('./modulechild.js');\nconsole.log(module.children);\n"
  },
  {
    "path": "examples/misc-examples/null.js",
    "content": "var pmx = require('pmx');\nvar http  = require('http');\n\nhttp.createServer(function(req, res) {\n      res.end('Thanks');\n  }).listen(5445);\n\npmx.action('db:test', {comment: 'Simply test'}, function(reply) {\n      reply({test: \"WOWOWOWOWOW\", length: 12});\n  });\n\npmx.action('throw', {comment: 'Simply test'}, function(reply) {\n  throw { success : 'false', length: 12 };\n});\n"
  },
  {
    "path": "examples/misc-examples/package.json",
    "content": "{\n  \"name\": \"example-module\",\n  \"version\": \"0.3.21\",\n  \"description\": \"Keymetrics++ and PM2 adapter\",\n  \"main\": \"scoped-action.js\",\n  \"dependencies\": {\n  },\n  \"scripts\": {\n    \"test\": \"DEBUG='axm:*' mocha test/*.mocha.js\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/keymetrics/pmx.git\"\n  },\n  \"config\" : {\n    \"aconfig-var\" : true,\n    \"var2\" : false\n  },\n  \"author\": \"Keymetrics I/O\",\n  \"license\": \"MIT\"\n}\n"
  },
  {
    "path": "examples/misc-examples/process.json",
    "content": "{\n  \"apps\" : [{\n    \"exec_interpreter\"   : \"node\",\n    \"exec_mode\"          : \"cluster_mode\",\n    \"instances\"          : 0,\n    \"log_date_format\"    : \"YYYY-MM-DD HH:mm Z\",\n    \"max_memory_restart\" : \"160M\",\n    \"merge_logs\"         : true,\n    \"name\"               : \"hapi_playground\",\n    \"script\"             : \"child.js\",\n    \"cwd\"                : \"examples\",\n    \"node_args\"          : \"--harmony\",\n    \"ignore_watch\"        : [\"[\\\\/\\\\\\\\]\\\\./\", \"log\"],\n    \"watch\"              : true,\n    env : {\n      NODE_ENV : 'normal'\n    },\n    env_production : {\n      NODE_ENV : 'production'\n    }\n  }]\n}\n"
  },
  {
    "path": "examples/misc-examples/programmatic.js",
    "content": "\nvar pm2 = require('..');\n\npm2.connect(function() {\n\n  pm2.start('echo.js', function() {\n\n    setInterval(function() {\n      pm2.restart('echo', function() {\n      });\n    }, 2000);\n\n  });\n\n\n});\n\npm2.launchBus(function(err, bus) {\n  console.log('connected', bus);\n\n  bus.on('log:out', function(data) {\n    if (data.process.name == 'echo')\n      console.log(arguments);\n  });\n\n  bus.on('reconnect attempt', function() {\n    console.log('Bus reconnecting');\n  });\n\n  bus.on('close', function() {\n    console.log('Bus closed');\n  });\n\n});\n\n/**\n * Exiting\n */\n//pm2.disconnectBus(); // For Bus system\n//pm2.disconnect();    // For RPC connection\n"
  },
  {
    "path": "examples/misc-examples/require.js",
    "content": "\nvar util = require('util');\n\nconsole.log(util.inspect(require.main));\nsetInterval(function() {\n  console.log(util.inspect(require.main));\n}, 8000);\n\n\n"
  },
  {
    "path": "examples/misc-examples/sendmsg.js",
    "content": "\nsetInterval(function() {\n  process.send({\n    type : 'miami',\n    data : { msg : 'i can communicate with others'}\n  });\n}, 1000);\n"
  },
  {
    "path": "examples/misc-examples/sigint.js",
    "content": "\nprocess.on('SIGINT', function() {\n  // Do othing for tests\n});\n\nsetInterval(function() {\n  console.log('I\\'m alive');\n}, 2000);\n"
  },
  {
    "path": "examples/misc-examples/start-args.js",
    "content": "\n\nvar pm2 = require('..');\n\npm2.connect(function() {\n  pm2.start(__dirname + '/args.js', {\n    scriptArgs : ['-i', 'sisi', '-x', 'toto']\n  }, function(err, res) {\n    console.log(arguments);\n  });\n});\n"
  },
  {
    "path": "examples/misc-examples/stop-after5.js",
    "content": "\nsetTimeout(function() {\n  process.exit(0);\n}, 5000);\n"
  },
  {
    "path": "examples/misc-examples/string-crash.js",
    "content": "\nfunction crash() {\n  throw 'crashed';\n}\n\ncrash();\n"
  },
  {
    "path": "examples/misc-examples/throw.js",
    "content": "\nsetTimeout(function() {\n  throw new Error('New error thrown automatically');\n}, 1200);\n"
  },
  {
    "path": "examples/misc-examples/tree.js",
    "content": "\nvar spawn = require('child_process').spawn,\n    grep  = spawn('top', [], { stdio: 'inherit' });\n\n\nvar http = require('http');\n\n\nvar normal = require('child_process').fork('examples/child.js', ['normal']);\n\nhttp.createServer(function(req, res) {\n  res.writeHead(200);\n  res.end('hoy');\n}).listen(8010);\n"
  },
  {
    "path": "examples/misc-examples/udp.js",
    "content": "\n\n\nvar punt = require('punt');\nvar server = punt.bind('0.0.0.0:5000');\nvar a = punt.connect('0.0.0.0:5000');\nvar b = punt.connect('0.0.0.0:5000');\nvar c = punt.connect('0.0.0.0:5000');\n\nserver.on('message', function(msg){\n  console.log(msg);\n});\n\nsetInterval(function(){\n  a.send({ hello: 'world' });\n}, 150);\n\nsetInterval(function(){\n  b.send('hello world');\n}, 150);\n\nsetInterval(function(){\n  c.send(new Buffer('hello'));\n}, 150);\n\nprocess.emit('listening');\n"
  },
  {
    "path": "examples/misc-examples/wrap.js",
    "content": "\nvar debug = require('debug')('methods');\n\nvar Proxy = module.exports = {\n  wrap : function(object, methods, hook) {\n    var self = this;\n\n    if (!Array.isArray(methods)) methods = [methods];\n\n    for (var i = 0; i < methods.length; ++i) {\n      debug('Wrapping method:', methods[i]);\n      var original = object[methods[i]];\n      if (!original) return debug('Method %s unknown', methods[i]);\n      if (original.__axm_original) {\n        debug('Already wrapped', methods[i]);\n        if (methods[i] != '_load')\n          return;\n      }\n      var hooked = hook(original);\n\n      if (original.__axm_original) {\n        hooked.__axm_original = original.__axm_original;\n      }\n      else {\n        hooked.__axm_original = original;\n      }\n      object[methods[i]] = hooked;\n      //debug('Method proxified');\n    }\n  }\n};\n"
  },
  {
    "path": "examples/module-extra-meta/index.js",
    "content": "\nvar pmx = require('pmx');\n\n/**\n * set \"PM2_WAIT_FOR_INIT\" : TIME to tell PM2 to show human infos\n */\npmx.configureModule({\n  human_info : [\n    [ 'Description',  'Gridcontrol is now running, tasdkkals dk als dkl askdl\\nasd lsdakl kdsald asdsd\\nAnd hthis like that and bla blab\\nYESY!' ],\n    [ 'Port',  8000],\n    [ 'Grid name', 'Sisi la grid']\n  ]\n});\n\nsetInterval(() => {}, 1000);\n"
  },
  {
    "path": "examples/module-extra-meta/package.json",
    "content": "{\n  \"name\": \"init-module\",\n  \"version\": \"1.0.0\",\n  \"description\": \"\",\n  \"main\": \"index.js\",\n  \"scripts\": {\n    \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n  },\n  \"apps\" : [{\n    \"script\" : \"index.js\",\n    \"env\" : {\n      \"PM2_WAIT_FOR_INIT\" : 500\n    }\n  }],\n  \"author\": \"\",\n  \"license\": \"ISC\"\n}\n"
  },
  {
    "path": "examples/module-test/README.md",
    "content": "\nThis is a sample module that have been generate via `pm2 module:generate`:\n\n```\n>>> pm2 module:generate\n[PM2] Spawning PM2 daemon with pm2_home=/home/unitech/.pm2\n[PM2] PM2 Successfully daemonized\n[PM2][Module] Module name: module-test\n[PM2][Module] Getting sample app\nCloning into 'module-test'...\n\nnpm notice created a lockfile as package-lock.json. You should commit this file.\nadded 4 packages in 0.939s\n\n[PM2][Module] Module sample created in folder:  /home/unitech/keymetrics/pm2/examples/module-test\n\nStart module in development mode:\n$ cd module-test/\n$ pm2 install .\n\nModule Log:\n$ pm2 logs module-test\n\nUninstall module:\n$ pm2 uninstall module-test\n\nForce restart:\n$ pm2 restart module-test\n```\n\n## Configuration\n\nTo add configuration to the module:\n\n```\n$ pm2 set module-test:var1 value1\n```\n\nYou will then be able to access to this value via\n\n```bash\npmx.initModule({\n}, function(err, conf) {\n  // var1 = value1\n  console.log(conf.var1);\n});\n```\n"
  },
  {
    "path": "examples/module-test/app.js",
    "content": "\nvar pmx = require('pmx');\n\n/******************************\n *    ______ _______ ______\n *   |   __ \\   |   |__    |\n *   |    __/       |    __|\n *   |___|  |__|_|__|______|\n *\n *      PM2 Module Sample\n *\n ******************************/\n\n/**\n *    Module system documentation\n *       http://bit.ly/1hnpcgu\n *\n *   Start module in development mode\n *          $ cd to my-module\n *          $ pm2 install .\n *\n *  Official modules are published here\n *      https://github.com/pm2-hive\n */\n\n/**\n *           Module Entry Point\n *\n *  We first initialize the module by calling\n *         pmx.initModule({}, cb);\n *\n *\n * More options: http://bit.ly/1EpagZS\n *\n */\npmx.initModule({\n\n  // Options related to the display style on Keymetrics\n  widget : {\n\n    // Logo displayed\n    logo : 'https://app.keymetrics.io/img/logo/keymetrics-300.png',\n\n    // Module colors\n    // 0 = main element\n    // 1 = secondary\n    // 2 = main border\n    // 3 = secondary border\n    theme            : ['#141A1F', '#222222', '#3ff', '#3ff'],\n\n    // Section to show / hide\n    el : {\n      probes  : true,\n      actions : true\n    },\n\n    // Main block to show / hide\n    block : {\n      actions : false,\n      issues  : true,\n      meta    : true,\n\n      // Custom metrics to put in BIG\n      main_probes : ['test-probe']\n    }\n\n  }\n\n}, function(err, conf) {\n\n  console.log(conf);\n\n  /**\n   * Module specifics like connecting to a database and\n   * displaying some metrics\n   */\n\n  /**\n   *                      Custom Metrics\n   *\n   * Let's expose some metrics that will be displayed into Keymetrics\n   *   For more documentation about metrics: http://bit.ly/1PZrMFB\n   */\n  var Probe = pmx.probe();\n\n  var value_to_inspect = 0;\n\n  /**\n   * .metric, .counter, .meter, .histogram are also available (cf doc)\n   */\n  var val = Probe.metric({\n    name : 'test-probe',\n    value : function() {\n      return value_to_inspect;\n    },\n    /**\n     * Here we set a default value threshold, to receive a notification\n     * These options can be overriden via Keymetrics or via pm2\n     * More: http://bit.ly/1O02aap\n     */\n    alert : {\n      mode     : 'threshold',\n      value    : 20,\n      msg      : 'test-probe alert!',\n      action   : function(val) {\n        // Besides the automatic alert sent via Keymetrics\n        // You can also configure your own logic to do something\n        console.log('Value has reached %d', val);\n      }\n    }\n  });\n\n  setInterval(function() {\n    // Then we can see that this value increase over the time in Keymetrics\n    value_to_inspect++;\n  }, 300);\n\n\n  /**\n   *                Simple Actions\n   *\n   *   Now let's expose some triggerable functions\n   *  Once created you can trigger this from Keymetrics\n   *\n   */\n  pmx.action('env', function(reply) {\n    return reply({\n      env: process.env\n    });\n  });\n\n  /**\n   *                 Scoped Actions\n   *\n   *     This are for long running remote function\n   * This allow also to res.emit logs to see the progress\n   *\n   **/\n  var spawn = require('child_process').spawn;\n\n  pmx.scopedAction('lsof cmd', function(options, res) {\n    var child = spawn('lsof', []);\n\n    child.stdout.on('data', function(chunk) {\n      chunk.toString().split('\\n').forEach(function(line) {\n        /**\n         * Here we send logs attached to this command\n         */\n        res.send(line);\n      });\n    });\n\n    child.stdout.on('end', function(chunk) {\n      /**\n       * Then we emit end to finalize the function\n       */\n      res.end('end');\n    });\n\n  });\n\n\n});\n"
  },
  {
    "path": "examples/module-test/package.json",
    "content": "{\n  \"name\": \"module-test\",\n  \"version\": \"1.0.0\",\n  \"description\": \"PM2 Sample Module\",\n  \"main\": \"app.js\",\n  \"dependencies\": {\n    \"pmx\": \"beta\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/keymetrics/pmx.git\"\n  },\n  \"config\": {\n    \"conf_var_1\": true,\n    \"conf_var_2\": \"myvalue\"\n  },\n  \"apps\": [\n    {\n      \"merge_logs\": true,\n      \"max_memory_restart\": \"200M\",\n      \"script\": \"app.js\"\n    }\n  ],\n  \"author\": \"Keymetrics Inc.\",\n  \"license\": \"MIT\"\n}\n"
  },
  {
    "path": "examples/npm-start/http.js",
    "content": "\nvar http = require('http');\n\nvar server = http.createServer(function(req, res) {\n  res.writeHead(200);\n  res.end('hey');\n}).listen(process.env.PORT || 8000, function() {\n  console.log('App listening on port %d', server.address().port);\n});\n"
  },
  {
    "path": "examples/npm-start/package.json",
    "content": "{\n  \"name\": \"npm-start\",\n  \"version\": \"1.0.0\",\n  \"description\": \"\",\n  \"main\": \"index.js\",\n  \"scripts\": {\n    \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\",\n    \"start\": \"node http.js\"\n  },\n  \"author\": \"\",\n  \"license\": \"ISC\"\n}\n"
  },
  {
    "path": "examples/pmx-test-all/elements/cluster.js",
    "content": "\nvar http = require('http');\n\nhttp.createServer(function(req, res) {\n  res.writeHead(200);\n  setTimeout(function() {\n    res.end('transaction');\n  }, 10);\n}).listen(process.env.PORT || 9010);\n\nsetInterval(function() {\n  request(['/user', '/bla', '/user/lol/delete', '/POST/POST'][Math.floor((Math.random() * 4))]);\n}, 140);\n\nfunction makeid()\n{\n  var text = \"\";\n  var possible = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\";\n\n  for( var i=0; i < 5; i++ )\n    text += possible.charAt(Math.floor(Math.random() * possible.length));\n\n  return text;\n}\n\nfunction request(path) {\n  var options = {\n    hostname: '127.0.0.1'\n    ,port: 9010\n    ,path: path || '/users'\n    ,method: 'GET'\n    ,headers: { 'Content-Type': 'application/json' }\n  };\n\n  var req = http.request(options, function(res) {\n    res.setEncoding('utf8');\n    res.on('data', function (data) {\n      //console.log(data);\n    });\n  });\n  req.on('error', function(e) {\n    console.log('problem with request: ' + e.message);\n  });\n  req.end();\n\n}\n"
  },
  {
    "path": "examples/pmx-test-all/elements/counter.js",
    "content": "\n\nconst pmx = require('pmx');\nconst Probe = pmx.probe();\n\nvar metric = Probe.counter({\n  name    : 'Counter'\n});\n\nsetInterval(function() {\n  metric.inc()\n}, 500);\n"
  },
  {
    "path": "examples/pmx-test-all/elements/error.js",
    "content": "\nsetInterval(function() {\n  new Error('toto');\n}, 10);\n"
  },
  {
    "path": "examples/pmx-test-all/elements/event.js",
    "content": "\nconst pmx = require('pmx');\n\nsetInterval(function() {\n  pmx.emit('user:register', {\n    user : 'Alex registered',\n    email : 'thorustor@gmail.com'\n  });\n}, 200);\n"
  },
  {
    "path": "examples/pmx-test-all/elements/histogram.js",
    "content": "\n\nconst pmx = require('pmx');\nconst Probe = pmx.probe();\n\nvar metric = Probe.histogram({\n  name    : 'Histogram'\n});\n\nvar latency;\n\nsetInterval(function() {\n  latency = Math.round(Math.random() * 100);\n  metric.update(latency);\n}, 100);\n"
  },
  {
    "path": "examples/pmx-test-all/elements/http.js",
    "content": "\nvar http = require('http');\n\nhttp.createServer(function(req, res) {\n  res.writeHead(200);\n  setTimeout(function() {\n    res.end('transaction');\n  }, 10);\n}).listen(process.env.PORT || 9010);\n\nsetInterval(function() {\n  request(['/user', '/bla', '/user/lol/delete', '/POST/POST'][Math.floor((Math.random() * 4))]);\n}, 140);\n\nfunction makeid()\n{\n  var text = \"\";\n  var possible = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\";\n\n  for( var i=0; i < 5; i++ )\n    text += possible.charAt(Math.floor(Math.random() * possible.length));\n\n  return text;\n}\n\nfunction request(path) {\n  var options = {\n    hostname: '127.0.0.1'\n    ,port: 9010\n    ,path: path || '/users'\n    ,method: 'GET'\n    ,headers: { 'Content-Type': 'application/json' }\n  };\n\n  var req = http.request(options, function(res) {\n    res.setEncoding('utf8');\n    res.on('data', function (data) {\n      //console.log(data);\n    });\n  });\n  req.on('error', function(e) {\n    console.log('problem with request: ' + e.message);\n  });\n  req.end();\n\n}\n"
  },
  {
    "path": "examples/pmx-test-all/elements/log-cluster.js",
    "content": "setInterval(function() {\n  console.log('log');\n  console.error('log');\n}, 200);\n"
  },
  {
    "path": "examples/pmx-test-all/elements/log.js",
    "content": "\nsetInterval(function() {\n  console.log('log');\n  console.error('log');\n}, 200);\n"
  },
  {
    "path": "examples/pmx-test-all/elements/meter.js",
    "content": "\nconst pmx = require('pmx');\nconst Probe = pmx.probe();\n\nvar metric = Probe.meter({\n  name    : 'Meter'\n});\n\nsetInterval(function() {\n  metric.mark()\n}, 200);\n"
  },
  {
    "path": "examples/pmx-test-all/elements/metric.js",
    "content": "\nconst pmx = require('pmx');\nconst Probe = pmx.probe();\n\nvar data = 10;\n\nvar metric = Probe.metric({\n  name    : 'Realtime user',\n  value   : function() {\n    return data;\n  }\n});\n\nsetInterval(function() {\n  data = Math.random();\n}, 500);\n"
  },
  {
    "path": "examples/pmx-test-all/elements/notify.js",
    "content": "\nconst pmx = require('pmx');\n\nsetInterval(function() {\n  pmx.notify({ success : false });\n}, 200);\n"
  },
  {
    "path": "examples/pmx-test-all/elements/trace.js",
    "content": "\nvar http = require('http');\n\nhttp.createServer(function(req, res) {\n  res.writeHead(200);\n  setTimeout(function() {\n    res.end('transaction');\n  }, 10);\n}).listen(9016);\n\nsetInterval(function() {\n  request(['/user', '/bla', '/user/lol/delete', '/POST/POST'][Math.floor((Math.random() * 4))]);\n}, 140);\n\nfunction makeid()\n{\n  var text = \"\";\n  var possible = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\";\n\n  for( var i=0; i < 5; i++ )\n    text += possible.charAt(Math.floor(Math.random() * possible.length));\n\n  return text;\n}\n\nfunction request(path) {\n  var options = {\n    hostname: '127.0.0.1'\n    ,port: 9016\n    ,path: path || '/users'\n    ,method: 'GET'\n    ,headers: { 'Content-Type': 'application/json' }\n  };\n\n  var req = http.request(options, function(res) {\n    res.setEncoding('utf8');\n    res.on('data', function (data) {\n      //console.log(data);\n    });\n  });\n  req.on('error', function(e) {\n    console.log('problem with request: ' + e.message);\n  });\n  req.end();\n\n}\n"
  },
  {
    "path": "examples/pmx-test-all/process.config.js",
    "content": "module.exports = {\n  pm2 : [{\n    script : './elements/error.js'\n  }, {\n    script : './elements/metric.js'\n  }, {\n    script : './elements/counter.js'\n  }, {\n    script : './elements/meter.js'\n  }, {\n    script : './elements/histogram.js'\n  }, {\n    script : './elements/event.js'\n  }, {\n    script : './elements/notify.js'\n  }, {\n    script : './elements/log.js'\n  }, {\n    script : './elements/log-cluster.js',\n    instances : 2\n  }, {\n    script : './elements/http.js'\n  }, {\n    script : './elements/cluster.js',\n    instances : 4,\n    env : {\n      PORT : 9803\n    }\n  }, {\n    script : './elements/trace.js',\n    trace : true\n  }]\n}\n"
  },
  {
    "path": "examples/run-php-python-ruby-bash/bashscript.sh",
    "content": "while true; do\n    ls -l\n    sleep 5\ndone\n\n"
  },
  {
    "path": "examples/run-php-python-ruby-bash/echo.php",
    "content": "<?php\n\nwhile (1) {\n    echo 'lol i hate php !';\n    sleep(1);\n}\n\n ?>"
  },
  {
    "path": "examples/run-php-python-ruby-bash/echo.py",
    "content": "#!/usr/bin/python\nimport time\n\nwhile 1:\n    print(\"Start : %s\" % time.ctime())\n    print(\"second line\")\n    time.sleep(1)\n"
  },
  {
    "path": "examples/run-php-python-ruby-bash/echo.rb",
    "content": "\nwhile 1 do\n  puts \"lol\"\n  sleep 1\nend\n"
  },
  {
    "path": "examples/run-php-python-ruby-bash/leak-mem.py",
    "content": "#!/usr/bin/env python3\n\"\"\"\nmemory_leak.py\n\nStarts with sensible defaults if no CLI args are provided.\nUsage:\n  python3 memory_leak.py          # runs with defaults\n  python3 memory_leak.py -c 20M   # override chunk size, etc.\n\"\"\"\n\nimport argparse\nimport time\nimport platform\n\ntry:\n    import psutil\nexcept Exception:\n    psutil = None\n\n_leak_store = []\n\ndef parse_size(s: str) -> int:\n    \"\"\"Parse sizes like '10M', '512K', '1G' into bytes.\"\"\"\n    s = str(s).strip().upper()\n    if s.endswith(\"G\"):\n        return int(float(s[:-1]) * 1024**3)\n    if s.endswith(\"M\"):\n        return int(float(s[:-1]) * 1024**2)\n    if s.endswith(\"K\"):\n        return int(float(s[:-1]) * 1024)\n    return int(s)\n\ndef rss_bytes() -> int:\n    \"\"\"Return current process RSS in bytes (best-effort).\"\"\"\n    if psutil:\n        return psutil.Process().memory_info().rss\n    if platform.system() == \"Linux\":\n        try:\n            with open(\"/proc/self/statm\", \"r\") as f:\n                parts = f.read().split()\n                if len(parts) >= 2:\n                    pages = int(parts[1])\n                    import os\n                    return pages * os.sysconf(\"SC_PAGE_SIZE\")\n        except Exception:\n            pass\n    return 0\n\ndef human_readable(n: int) -> str:\n    for unit in [\"B\", \"K\", \"M\", \"G\", \"T\"]:\n        if n < 1024.0:\n            return f\"{n:.1f}{unit}\"\n        n /= 1024.0\n    return f\"{n:.1f}P\"\n\ndef run_leak(chunk_size: int, interval: float, max_bytes: int, max_iters: int, verbose: bool, report_interval: float):\n    total_allocated = 0\n    iters = 0\n    last_report = time.time()\n\n    try:\n        while True:\n            # Stop conditions\n            if max_bytes and total_allocated >= max_bytes:\n                if verbose:\n                    print(f\"[info] reached max-bytes {human_readable(max_bytes)}; stopping allocation.\")\n                break\n            if max_iters and iters >= max_iters:\n                if verbose:\n                    print(f\"[info] reached max-iters {max_iters}; stopping allocation.\")\n                break\n\n            try:\n                chunk = bytearray(b\"\\x41\") * chunk_size\n            except MemoryError:\n                print(\"[error] MemoryError during allocation — exiting allocation loop.\")\n                break\n\n            _leak_store.append(chunk)\n            total_allocated += chunk_size\n            iters += 1\n\n            now = time.time()\n            if verbose and (now - last_report >= report_interval):\n                rss = rss_bytes()\n                print(f\"[alloc #{iters}] chunk={human_readable(chunk_size)}, total_alloc={human_readable(total_allocated)}, RSS={human_readable(rss)}\")\n                last_report = now\n\n            if interval:\n                time.sleep(interval)\n\n    except KeyboardInterrupt:\n        print(\"\\n[stopped] KeyboardInterrupt received — stopping allocations.\")\n\n    rss = rss_bytes()\n    print(\"\\n=== Summary ===\")\n    print(f\"iterations: {iters}\")\n    print(f\"total allocated (kept references): {human_readable(total_allocated)}\")\n    print(f\"final RSS: {human_readable(rss)}\")\n    print(f\"stored objects in leak store: {len(_leak_store)}\")\n\ndef main():\n    parser = argparse.ArgumentParser(description=\"Simple memory leak generator for testing (runs with defaults if no args).\")\n    parser.add_argument(\"--chunk-size\", \"-c\", default=\"10M\", help=\"Size per allocation chunk (e.g. 10M). Default 10M.\")\n    parser.add_argument(\"--interval\", \"-i\", type=float, default=0.1, help=\"Seconds between allocations. Default 0.1s.\")\n    parser.add_argument(\"--max-bytes\", \"-m\", default=None, help=\"Stop after allocating this many bytes (e.g. 200M). Optional.\")\n    parser.add_argument(\"--max-iters\", type=int, default=0, help=\"Stop after N allocations (0 = unlimited).\")\n    parser.add_argument(\"--report-interval\", type=float, default=1.0, help=\"How often (s) to print allocation status when verbose.\")\n    parser.add_argument(\"--quiet\", action=\"store_true\", help=\"Do not print allocation messages.\")\n    args = parser.parse_args()\n\n    chunk_size = parse_size(args.chunk_size)\n    max_bytes = parse_size(args.max_bytes) if args.max_bytes else 0\n    max_iters = args.max_iters if args.max_iters > 0 else 0\n    interval = float(args.interval)\n    verbose = not args.quiet\n\n    print(\"memory_leak starting (defaults used when no args provided):\")\n    print(f\"  chunk_size = {human_readable(chunk_size)}\")\n    print(f\"  interval   = {interval} s\")\n    if max_bytes:\n        print(f\"  max_bytes  = {human_readable(max_bytes)}\")\n    if max_iters:\n        print(f\"  max_iters  = {max_iters}\")\n    print(\"  verbose    =\", verbose)\n    print(\"Press Ctrl-C to stop manually.\\n\")\n\n    run_leak(chunk_size=chunk_size, interval=interval, max_bytes=max_bytes, max_iters=max_iters, verbose=verbose, report_interval=args.report_interval)\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "examples/run-php-python-ruby-bash/process.yml",
    "content": "apps:\n  - script : './bashscript.sh'\n    name : 'bash-script'\n  - script : './echo.php'\n    name : 'php-script'\n  - script : './echo.rb'\n    name : 'ruby-script'\n  - script : './echo.py'\n    name : 'python-script'\n"
  },
  {
    "path": "examples/send-msg/pm2-app.js",
    "content": "\nprocess.on('message', function(packet) {\n  process.send({\n    type : 'process:msg',\n    data : {\n      success : true\n    }\n  });\n});\n"
  },
  {
    "path": "examples/send-msg/pm2-msg.js",
    "content": "\nconst pm2 = require('../..')\n\nconsole.log(pm2)\n\npm2.connect(function() {\n  pm2.sendDataToProcessId({\n    // id of procces from \"pm2 list\" command or from pm2.list(errback) method\n    id   : '1',\n\n    // process:msg will be send as 'message' on target process\n    type : 'process:msg',\n\n    // Data to be sent\n    data : {\n      some : 'data'\n    },\n\n    topic: true\n  }, function(err, res) {\n  })\n})\n\n// Listen to messages from application\npm2.launchBus(function(err, pm2_bus) {\n  pm2_bus.on('process:msg', function(packet) {\n    console.log(packet)\n  })\n})\n"
  },
  {
    "path": "examples/send-msg/t2.js",
    "content": "\nvar tx2 = require('tx2')\nvar http = require('http')\n\nvar meter = tx2.meter({\n  name      : 'req/sec',\n  samples   : 1,\n  timeframe : 60\n})\n\nhttp.createServer(function (req, res) {\n  meter.mark()\n  res.writeHead(200, {'Content-Type': 'text/plain'})\n  res.write('Hello World!')\n  res.end()\n}).listen(6001)\n"
  },
  {
    "path": "examples/signal-catching/graceful-http.js",
    "content": "\nvar http = require('http');\n\nprocess.on('SIGINT', function() {\n  console.log('Graceful closing...');\n  server.close(function() {\n    process.exit(0);\n  });\n});\n\nvar server = http.createServer(function(req, res) {\n  res.writeHead(200);\n  res.end('hey');\n}).listen(process.env.PORT || 8000, function() {\n  console.log('App listening on port %d in env %s', process.env.PORT || 8000, process.env.NODE_ENV);\n});\n"
  },
  {
    "path": "examples/sourcemap-auto-resolve/API.js",
    "content": "/**\n * Copyright 2013-2022 the PM2 project authors. All rights reserved.\n * Use of this source code is governed by a license that\n * can be found in the LICENSE file.\n */\n\nvar commander   = require('commander');\nvar fs          = require('fs');\nvar path        = require('path');\nvar eachLimit       = require('async/eachLimit');\nvar series       = require('async/series');\nvar debug       = require('debug')('pm2:cli');\nvar util        = require('util');\nvar chalk       = require('ansis');\nvar fclone      = require('fclone');\n\nvar IMMUTABLE_MSG = chalk.bold.blue('Use --update-env to update environment variables');\n\n/**\n * Main Function to be imported\n * can be aliased to PM2\n *\n * To use it when PM2 is installed as a module:\n *\n * var PM2 = require('pm2');\n *\n * var pm2 = PM2(<opts>);\n *\n *\n * @param {Object}  opts\n * @param {String}  [opts.cwd=<current>]         override pm2 cwd for starting scripts\n * @param {String}  [opts.pm2_home=[<paths.js>]] pm2 directory for log, pids, socket files\n * @param {Boolean} [opts.independent=false]     unique PM2 instance (random pm2_home)\n * @param {Boolean} [opts.daemon_mode=true]      should be called in the same process or not\n * @param {String}  [opts.public_key=null]       keymetrics bucket public key\n * @param {String}  [opts.secret_key=null]       keymetrics bucket secret key\n * @param {String}  [opts.machine_name=null]     keymetrics instance name\n */\nvar API = module.exports = function(opts) {\n  if (!opts) opts = {};\n  var that = this;\n\n  this.daemon_mode = typeof(opts.daemon_mode) == 'undefined' ? true : opts.daemon_mode;\n  this.pm2_home    = conf.PM2_ROOT_PATH;\n  this.public_key   = process.env.KEYMETRICS_SECRET || opts.public_key || null;\n  this.secret_key   = process.env.KEYMETRICS_PUBLIC || opts.secret_key || null;\n  this.machine_name = process.env.INSTANCE_NAME || opts.machine_name || null\n\n  /**\n   * CWD resolution\n   */\n  this.cwd         = process.cwd();\n  if (opts.cwd) {\n    this.cwd = path.resolve(opts.cwd);\n  }\n\n  /**\n   * PM2 HOME resolution\n   */\n  if (opts.pm2_home && opts.independent == true)\n    throw new Error('You cannot set a pm2_home and independent instance in same time');\n\n  if (opts.pm2_home) {\n    // Override default conf file\n    this.pm2_home        = opts.pm2_home;\n    conf = util._extend(conf, path_structure(this.pm2_home));\n  }\n  else if (opts.independent == true && conf.IS_WINDOWS === false) {\n    // Create an unique pm2 instance\n    var crypto = require('crypto');\n    var random_file = crypto.randomBytes(8).toString('hex');\n    this.pm2_home = path.join('/tmp', random_file);\n\n    // If we dont explicitly tell to have a daemon\n    // It will go as in proc\n    if (typeof(opts.daemon_mode) == 'undefined')\n      this.daemon_mode = false;\n    conf = util._extend(conf, path_structure(this.pm2_home));\n  }\n\n  this._conf = conf;\n\n  if (conf.IS_WINDOWS) {\n    // Weird fix, may need to be dropped\n    // @todo windows connoisseur double check\n    if (process.stdout._handle && process.stdout._handle.setBlocking)\n      process.stdout._handle.setBlocking(true);\n  }\n\n  this.Client = new Client({\n    pm2_home : that.pm2_home,\n    conf     : this._conf,\n    secret_key : this.secret_key,\n    public_key : this.public_key,\n    daemon_mode : this.daemon_mode,\n    machine_name : this.machine_name\n  });\n\n  this.gl_interact_infos = null;\n  this.gl_is_km_linked = false;\n\n  try {\n    var pid = fs.readFileSync(conf.INTERACTOR_PID_PATH);\n    pid = parseInt(pid.toString().trim());\n    process.kill(pid, 0);\n    that.gl_is_km_linked = true;\n  } catch(e) {\n    that.gl_is_km_linked = false;\n  }\n\n  // For testing purposes\n  if (this.secret_key && process.env.NODE_ENV == 'local_test')\n    that.gl_is_km_linked = true;\n\n  KMDaemon.getInteractInfo(this._conf, function(i_err, interact) {\n    that.gl_interact_infos = interact;\n  });\n};\n\n\n//////////////////////////\n// Load all API methods //\n//////////////////////////\n\n\n/**\n * Connect to PM2\n * Calling this command is now optional\n *\n * @param {Function} cb callback once pm2 is ready for commands\n */\nAPI.prototype.connect = function(noDaemon, cb) {\n  var that = this;\n  this.start_timer = new Date();\n\n  if (typeof(cb) == 'undefined') {\n    cb = noDaemon;\n    noDaemon = false;\n  } else if (noDaemon === true) {\n    // Backward compatibility with PM2 1.x\n    this.Client.daemon_mode = false;\n    this.daemon_mode = false;\n  }\n\n  this.Client.start(function(err, meta) {\n    if (err)\n      return cb(err);\n\n    if (meta.new_pm2_instance == false && that.daemon_mode === true)\n      return cb(err, meta);\n\n    // If new pm2 instance has been popped\n    // Launch all modules\n    Modularizer.launchAll(that, function(err_mod) {\n      return cb(err, meta);\n    });\n  });\n}\n\n/**\n * Usefull when custom PM2 created with independent flag set to true\n * This will cleanup the newly created instance\n * by removing folder, killing PM2 and so on\n *\n * @param {Function} cb callback once cleanup is successfull\n */\nAPI.prototype.destroy = function(cb) {\n  var exec = require('shelljs').exec;\n  var that = this;\n\n  debug('Killing and deleting current deamon');\n\n  this.killDaemon(function() {\n    var cmd = 'rm -rf ' + that.pm2_home;\n    var test_path = path.join(that.pm2_home, 'module_conf.json');\n    var test_path_2 = path.join(that.pm2_home, 'pm2.pid');\n\n    if (that.pm2_home.indexOf('.pm2') > -1)\n      return cb(new Error('Destroy is not a allowed method on .pm2'));\n\n    if (fs.accessSync) {\n      fs.access(test_path, fs.R_OK, function(err) {\n        if (err) return cb(err);\n        debug('Deleting temporary folder %s', that.pm2_home);\n        exec(cmd, cb);\n      });\n      return false;\n    }\n\n    // Support for Node 0.10\n    fs.exists(test_path, function(exist) {\n      if (exist) {\n        debug('Deleting temporary folder %s', that.pm2_home);\n        exec(cmd, cb);\n      }\n      return cb(null);\n    });\n  });\n};\n\n/**\n * Disconnect from PM2 instance\n * This will allow your software to exit by itself\n *\n * @param {Function} [cb] optional callback once connection closed\n */\nAPI.prototype.disconnect = API.prototype.close = function(cb) {\n  var that = this;\n\n  if (!cb) cb = function() {};\n\n  this.Client.close(function(err, data) {\n    debug('The session lasted %ds', (new Date() - that.start_timer) / 1000);\n    return cb(err, data);\n  });\n};\n\n/**\n * Launch modules\n *\n * @param {Function} cb callback once pm2 has launched modules\n */\nAPI.prototype.launchModules = function(cb) {\n  Modularizer.launchAll(this, cb);\n};\n\nthrow new Error('muhahahaha');\n\n/**\n * Enable bus allowing to retrieve various process event\n * like logs, restarts, reloads\n *\n * @param {Function} cb callback called with 1st param err and 2nb param the bus\n */\nAPI.prototype.launchBus = function(cb) {\n  this.Client.launchBus(cb);\n};\n\n/**\n * Exit methods for API\n * @param {Integer} code exit code for terminal\n */\nAPI.prototype.exitCli = function(code) {\n  var that = this;\n\n  // Do nothing if PM2 called programmatically (also in speedlist)\n  if (conf.PM2_PROGRAMMATIC && process.env.PM2_USAGE != 'CLI') return false;\n\n  KMDaemon.disconnectRPC(function() {\n    that.Client.close(function() {\n      code = code || 0;\n      // Safe exits process after all streams are drained.\n      // file descriptor flag.\n      var fds = 0;\n      // exits process when stdout (1) and sdterr(2) are both drained.\n      function tryToExit() {\n        if ((fds & 1) && (fds & 2)) {\n          debug('This command took %ds to execute', (new Date() - that.start_timer) / 1000);\n          process.exit(code);\n        }\n      }\n\n      [process.stdout, process.stderr].forEach(function(std) {\n        var fd = std.fd;\n        if (!std.bufferSize) {\n          // bufferSize equals 0 means current stream is drained.\n          fds = fds | fd;\n        } else {\n          // Appends nothing to the std queue, but will trigger `tryToExit` event on `drain`.\n          std.write && std.write('', function() {\n            fds = fds | fd;\n            tryToExit();\n          });\n        }\n        // Does not write anything more.\n        delete std.write;\n      });\n      tryToExit();\n    });\n  });\n};\n\n////////////////////////////\n// Application management //\n////////////////////////////\n\n/**\n * Start a file or json with configuration\n * @param {Object||String} cmd script to start or json\n * @param {Function} cb called when application has been started\n */\nAPI.prototype.start = function(cmd, opts, cb) {\n  if (typeof(opts) == \"function\") {\n    cb = opts;\n    opts = {};\n  }\n  if (!opts)\n    opts = {};\n\n  var that = this;\n\n  if (util.isArray(opts.watch) && opts.watch.length === 0)\n    opts.watch = (opts.rawArgs ? !!~opts.rawArgs.indexOf('--watch') : !!~process.argv.indexOf('--watch')) || false;\n\n  if (Common.isConfigFile(cmd) || (typeof(cmd) === 'object'))\n    that._startJson(cmd, opts, 'restartProcessId', cb);\n  else {\n    that._startScript(cmd, opts, cb);\n  }\n};\n\n/**\n * Reset process counters\n *\n * @method resetMetaProcess\n */\nAPI.prototype.reset = function(process_name, cb) {\n  var that = this;\n\n  function processIds(ids, cb) {\n    eachLimit(ids, conf.CONCURRENT_ACTIONS, function(id, next) {\n      that.Client.executeRemote('resetMetaProcessId', id, function(err, res) {\n        if (err) console.error(err);\n        Common.printOut(conf.PREFIX_MSG + 'Resetting meta for process id %d', id);\n        return next();\n      });\n    }, function(err) {\n      if (err) return cb(Common.retErr(err));\n      return cb ? cb(null, {success:true}) : that.speedList();\n    });\n  }\n\n  if (process_name == 'all') {\n    that.Client.getAllProcessId(function(err, ids) {\n      if (err) {\n        Common.printError(err);\n        return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);\n      }\n      return processIds(ids, cb);\n    });\n  }\n  else if (isNaN(process_name)) {\n    that.Client.getProcessIdByName(process_name, function(err, ids) {\n      if (err) {\n        Common.printError(err);\n        return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);\n      }\n      if (ids.length === 0) {\n        Common.printError('Unknown process name');\n        return cb ? cb(new Error('Unknown process name')) : that.exitCli(conf.ERROR_EXIT);\n      }\n      return processIds(ids, cb);\n    });\n  } else {\n    processIds([process_name], cb);\n  }\n};\n\n/**\n * Update daemonized PM2 Daemon\n *\n * @param {Function} cb callback when pm2 has been upgraded\n */\nAPI.prototype.update = function(cb) {\n  var that = this;\n\n  Common.printOut('Be sure to have the latest version by doing `npm install pm2@latest -g` before doing this procedure.');\n\n  // Dump PM2 processes\n  that.Client.executeRemote('notifyKillPM2', {}, function() {});\n\n  that.getVersion(function(err, new_version) {\n    // If not linked to keymetrics, and update pm2 to latest, display motd.update\n    if (!that.gl_is_km_linked && !err && (pkg.version != new_version)) {\n      var dt = fs.readFileSync(path.join(__dirname, that._conf.KEYMETRICS_UPDATE));\n      console.log(dt.toString());\n    }\n\n    that.dump(function(err) {\n      debug('Dumping successfull', err);\n      that.killDaemon(function() {\n        debug('------------------ Everything killed', arguments);\n        that.Client.launchDaemon({interactor:false}, function(err, child) {\n          that.Client.launchRPC(function() {\n            that.resurrect(function() {\n              Common.printOut(chalk.blue.bold('>>>>>>>>>> PM2 updated'));\n              Modularizer.launchAll(that, function() {\n                KMDaemon.launchAndInteract(that._conf, null, function(err, data, interactor_proc) {\n                  // Interactor error can be skipped here\n                  return cb ? cb(null, {success:true}) : that.speedList();\n                });\n              });\n            });\n          });\n        });\n      });\n    });\n  });\n\n  return false;\n};\n\n/**\n * Reload an application\n *\n * @param {String} process_name Application Name or All\n * @param {Object} opts         Options\n * @param {Function} cb         Callback\n */\nAPI.prototype.reload = function(process_name, opts, cb) {\n  var that = this;\n\n  if (typeof(opts) == \"function\") {\n    cb = opts;\n    opts = {};\n  }\n\n  var delay = Common.lockReload();\n\n  if (delay > 0 && opts.force != true) {\n    Common.printError(conf.PREFIX_MSG_ERR + 'Reload already in progress, please try again in ' + Math.floor((conf.RELOAD_LOCK_TIMEOUT - delay) / 1000) + ' seconds or use --force');\n    return cb ? cb(new Error('Reload in progress')) : that.exitCli(conf.ERROR_EXIT);\n  }\n\n  if (Common.isConfigFile(process_name))\n    that._startJson(process_name, opts, 'reloadProcessId', function(err, apps) {\n      Common.unlockReload();\n      if (err)\n        return cb ? cb(err) : that.exitCli(conf.ERROR_EXIT);\n      return cb ? cb(null, apps) : that.exitCli(conf.SUCCESS_EXIT);\n    });\n  else {\n    if (opts && !opts.updateEnv)\n      Common.printOut(IMMUTABLE_MSG);\n\n    that._operate('reloadProcessId', process_name, opts, function(err, apps) {\n      Common.unlockReload();\n\n      if (err)\n        return cb ? cb(err) : that.exitCli(conf.ERROR_EXIT);\n      return cb ? cb(null, apps) : that.exitCli(conf.SUCCESS_EXIT);\n    });\n  }\n};\n\n/**\n * Restart process\n *\n * @param {String} cmd   Application Name / Process id / JSON application file / 'all'\n * @param {Object} opts  Extra options to be updated\n * @param {Function} cb  Callback\n */\nAPI.prototype.restart = function(cmd, opts, cb) {\n  if (typeof(opts) == \"function\") {\n    cb = opts;\n    opts = {};\n  }\n  var that = this;\n\n  if (typeof(cmd) === 'number')\n    cmd = cmd.toString();\n\n  if (cmd == \"-\") {\n    // Restart from PIPED JSON\n    process.stdin.resume();\n    process.stdin.setEncoding('utf8');\n    process.stdin.on('data', function (param) {\n      process.stdin.pause();\n      that.actionFromJson('restartProcessId', param, opts, 'pipe', cb);\n    });\n  }\n  else if (Common.isConfigFile(cmd) || typeof(cmd) === 'object')\n    that._startJson(cmd, opts, 'restartProcessId', cb);\n  else {\n    if (opts && !opts.updateEnv)\n      Common.printOut(IMMUTABLE_MSG);\n    that._operate('restartProcessId', cmd, opts, cb);\n  }\n};\n\n/**\n * Delete process\n *\n * @param {String} process_name Application Name / Process id / Application file / 'all'\n * @param {Function} cb Callback\n */\nAPI.prototype.delete = function(process_name, jsonVia, cb) {\n  var that = this;\n\n  if (typeof(jsonVia) === \"function\") {\n    cb = jsonVia;\n    jsonVia = null;\n  }\n  if (typeof(process_name) === \"number\") {\n    process_name = process_name.toString();\n  }\n\n  if (jsonVia == 'pipe')\n    return that.actionFromJson('deleteProcessId', process_name, commander, 'pipe', cb);\n  if (Common.isConfigFile(process_name))\n    return that.actionFromJson('deleteProcessId', process_name, commander, 'file', cb);\n  else\n    that._operate('deleteProcessId', process_name, cb);\n};\n\n/**\n * Stop process\n *\n * @param {String} process_name Application Name / Process id / Application file / 'all'\n * @param {Function} cb Callback\n */\nAPI.prototype.stop = function(process_name, cb) {\n  var that = this;\n\n  if (typeof(process_name) === 'number')\n    process_name = process_name.toString();\n\n  if (process_name == \"-\") {\n    process.stdin.resume();\n    process.stdin.setEncoding('utf8');\n    process.stdin.on('data', function (param) {\n      process.stdin.pause();\n      that.actionFromJson('stopProcessId', param, commander, 'pipe', cb);\n    });\n  }\n  else if (Common.isConfigFile(process_name))\n    that.actionFromJson('stopProcessId', process_name, commander, 'file', cb);\n  else\n    that._operate('stopProcessId', process_name, cb);\n};\n\n/**\n * Get list of all processes managed\n *\n * @param {Function} cb Callback\n */\nAPI.prototype.list = function(opts, cb) {\n  var that = this;\n\n  if (typeof(opts) == 'function') {\n    cb = opts;\n    opts = null;\n  }\n\n  that.Client.executeRemote('getMonitorData', {}, function(err, list) {\n    if (err) {\n      Common.printError(err);\n      return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);\n    }\n\n    if (opts && opts.rawArgs && opts.rawArgs.indexOf('--watch') > -1) {\n      var moment = require('moment');\n      function show() {\n        process.stdout.write('\\033[2J');\n        process.stdout.write('\\033[0f');\n        console.log('Last refresh: ', moment().format('LTS'));\n        that.Client.executeRemote('getMonitorData', {}, function(err, list) {\n          UX.dispAsTable(list, null);\n        });\n      }\n\n      show();\n      setInterval(show, 900);\n      return false;\n    }\n\n    return cb ? cb(null, list) : that.speedList();\n  });\n};\n\n/**\n * Kill Daemon\n *\n * @param {Function} cb Callback\n */\nAPI.prototype.killDaemon = API.prototype.kill = function(cb) {\n  var that = this;\n\n  var semver = require('semver');\n  Common.printOut(conf.PREFIX_MSG + 'Stopping PM2...');\n\n  that.Client.executeRemote('notifyKillPM2', {}, function() {});\n\n  that.killAllModules(function() {\n    that._operate('deleteProcessId', 'all', function(err, list) {\n      Common.printOut(conf.PREFIX_MSG + 'All processes have been stopped and deleted');\n      process.env.PM2_SILENT = 'false';\n\n      that.killInteract(function(err, data) {\n        that.Client.killDaemon(function(err, res) {\n          if (err) Common.printError(err);\n          Common.printOut(conf.PREFIX_MSG + 'PM2 stopped');\n          return cb ? cb(err, res) : that.exitCli(conf.SUCCESS_EXIT);\n        });\n      });\n    });\n  });\n};\n\n/////////////////////\n// Private methods //\n/////////////////////\n\n/**\n * Method to START / RESTART a script\n *\n * @private\n * @param {string} script script name (will be resolved according to location)\n */\nAPI.prototype._startScript = function(script, opts, cb) {\n  if (typeof opts == \"function\") {\n    cb = opts;\n    opts = {};\n  }\n  var that = this;\n\n  var app_conf = Config.transCMDToConf(opts);\n  var appConf = {};\n\n  if (!!opts.executeCommand)\n    app_conf.exec_mode = 'fork';\n  else if (opts.instances !== undefined)\n    app_conf.exec_mode = 'cluster';\n  else\n    app_conf.exec_mode = 'fork';\n\n  if (typeof app_conf.name == 'function'){\n    delete app_conf.name;\n  }\n\n  delete app_conf.args;\n\n  var argsIndex;\n\n  if (opts.rawArgs && (argsIndex = opts.rawArgs.indexOf('--')) >= 0) {\n    app_conf.args = opts.rawArgs.slice(argsIndex + 1);\n  }\n  else if (opts.scriptArgs) {\n    app_conf.args = opts.scriptArgs;\n  }\n\n  app_conf.script = script;\n\n  if ((appConf = Common.verifyConfs(app_conf)) instanceof Error)\n    return cb ? cb(Common.retErr(appConf)) : that.exitCli(conf.ERROR_EXIT);\n\n  app_conf = appConf[0];\n\n  app_conf.username = Common.getCurrentUsername();\n\n  /**\n   * If -w option, write configuration to configuration.json file\n   */\n  if (appConf.write) {\n    var dst_path = path.join(process.env.PWD || process.cwd(), app_conf.name + '-pm2.json');\n    Common.printOut(conf.PREFIX_MSG + 'Writing configuration to', chalk.blue(dst_path));\n    // pretty JSON\n    try {\n      fs.writeFileSync(dst_path, JSON.stringify(app_conf, null, 2));\n    } catch (e) {\n      console.error(e.stack || e);\n    }\n  }\n\n  /**\n   * If start <app_name> start/restart application\n   */\n  function restartExistingProcessName(cb) {\n    if (!isNaN(script) ||\n        (typeof script === 'string' && script.indexOf('/') != -1) ||\n        (typeof script === 'string' && path.extname(script) !== ''))\n      return cb(null);\n\n    if (script !== 'all') {\n      that.Client.getProcessIdByName(script, function(err, ids) {\n        if (err && cb) return cb(err);\n        if (ids.length > 0) {\n          that._operate('restartProcessId', script, opts, function(err, list) {\n            if (err) return cb(err);\n            Common.printOut(conf.PREFIX_MSG + 'Process successfully started');\n            return cb(true, list);\n          });\n        }\n        else return cb(null);\n      });\n    }\n    else {\n      that._operate('restartProcessId', 'all', function(err, list) {\n        if (err) return cb(err);\n        Common.printOut(conf.PREFIX_MSG + 'Process successfully started');\n        return cb(true, list);\n      });\n    }\n  }\n\n  function restartExistingProcessId(cb) {\n    if (isNaN(script)) return cb(null);\n\n    that._operate('restartProcessId', script, opts, function(err, list) {\n      if (err) return cb(err);\n      Common.printOut(conf.PREFIX_MSG + 'Process successfully started');\n      return cb(true, list);\n    });\n  }\n\n  /**\n   * Restart a process with the same full path\n   * Or start it\n   */\n  function restartExistingProcessPath(cb) {\n    that.Client.executeRemote('getMonitorData', {}, function(err, procs) {\n      if (err) return cb ? cb(new Error(err)) : that.exitCli(conf.ERROR_EXIT);\n\n      var full_path = path.resolve(that.cwd, script);\n      var managed_script = null;\n\n      procs.forEach(function(proc) {\n        if (proc.pm2_env.pm_exec_path == full_path &&\n            proc.pm2_env.name == app_conf.name)\n          managed_script = proc;\n      });\n\n      if (managed_script &&\n          (managed_script.pm2_env.status == conf.STOPPED_STATUS ||\n           managed_script.pm2_env.status == conf.STOPPING_STATUS ||\n           managed_script.pm2_env.status == conf.ERRORED_STATUS)) {\n        // Restart process if stopped\n        var app_name = managed_script.pm2_env.name;\n\n        that._operate('restartProcessId', app_name, opts, function(err, list) {\n          if (err) return cb ? cb(new Error(err)) : that.exitCli(conf.ERROR_EXIT);\n          Common.printOut(conf.PREFIX_MSG + 'Process successfully started');\n          return cb(true, list);\n        });\n        return false;\n      }\n      else if (managed_script && !opts.force) {\n        Common.printError(conf.PREFIX_MSG_ERR + 'Script already launched, add -f option to force re-execution');\n        return cb(new Error('Script already launched'));\n      }\n\n      var resolved_paths = null;\n\n      try {\n        resolved_paths = Common.resolveAppAttributes({\n          cwd      : that.cwd,\n          pm2_home : that.pm2_home\n        }, app_conf);\n      } catch(e) {\n        Common.printError(e);\n        return cb(Common.retErr(e));\n      }\n\n      Common.printOut(conf.PREFIX_MSG + 'Starting %s in %s (%d instance' + (resolved_paths.instances > 1 ? 's' : '') + ')',\n                      resolved_paths.pm_exec_path, resolved_paths.exec_mode, resolved_paths.instances);\n\n      if (!resolved_paths.env) resolved_paths.env = {};\n\n      // Set PM2 HOME in case of child process using PM2 API\n      resolved_paths.env['PM2_HOME'] = that.pm2_home;\n\n      var additional_env = Modularizer.getAdditionalConf(resolved_paths.name);\n      util._extend(resolved_paths.env, additional_env);\n\n      // Is KM linked?\n      resolved_paths.km_link = that.gl_is_km_linked;\n\n      that.Client.executeRemote('prepare', resolved_paths, function(err, data) {\n        if (err) {\n          Common.printError(conf.PREFIX_MSG_ERR + 'Error while launching application', err.stack || err);\n          return cb(Common.retErr(err));\n        }\n\n        Common.printOut(conf.PREFIX_MSG + 'Done.');\n        return cb(true, data);\n      });\n      return false;\n    });\n  }\n\n  series([\n    restartExistingProcessName,\n    restartExistingProcessId,\n    restartExistingProcessPath\n  ], function(err, data) {\n\n    if (err instanceof Error)\n      return cb ? cb(err) : that.exitCli(conf.ERROR_EXIT);\n\n    var ret = {};\n    data.forEach(function(_dt) {\n      if (_dt !== undefined)\n        ret = _dt;\n    });\n\n    return cb ? cb(null, ret) : that.speedList();\n  });\n};\n\n/**\n * Method to start/restart/reload processes from a JSON file\n * It will start app not started\n * Can receive only option to skip applications\n *\n * @private\n */\nAPI.prototype._startJson = function(file, opts, action, pipe, cb) {\n  var config     = {};\n  var appConf    = {};\n  var deployConf = {};\n  var apps_info  = [];\n  var that = this;\n\n  if (typeof(cb) === 'undefined' && typeof(pipe) === 'function') {\n    cb = pipe;\n  }\n\n  if (typeof(file) === 'object') {\n    config = file;\n  } else if (pipe === 'pipe') {\n    config = Common.parseConfig(file, 'pipe');\n  } else {\n    var data = null;\n\n    var isAbsolute = false\n\n    //node 0.11 compatibility #2815\n    if (typeof path.isAbsolute === 'function') {\n      isAbsolute = path.isAbsolute(file)\n    } else {\n      isAbsolute = require('./tools/IsAbsolute.js')(file)\n    }\n\n    var file_path = isAbsolute ? file : path.join(that.cwd, file);\n\n    debug('Resolved filepath %s', file_path);\n\n    try {\n      data = fs.readFileSync(file_path);\n    } catch(e) {\n      Common.printError(conf.PREFIX_MSG_ERR + 'File ' + file +' not found');\n      return cb ? cb(Common.retErr(e)) : that.exitCli(conf.ERROR_EXIT);\n    }\n\n    try {\n      config = Common.parseConfig(data, file);\n    } catch(e) {\n      Common.printError(conf.PREFIX_MSG_ERR + 'File ' + file + ' malformated');\n      console.error(e);\n      return cb ? cb(Common.retErr(e)) : that.exitCli(conf.ERROR_EXIT);\n    }\n  }\n\n  if (config.deploy)\n    deployConf = config.deploy;\n\n  if (config.apps)\n    appConf = config.apps;\n  else if (config.pm2)\n    appConf = config.pm2;\n  else\n    appConf = config;\n\n  if (!Array.isArray(appConf))\n    appConf = [appConf]; //convert to array\n\n  if ((appConf = Common.verifyConfs(appConf)) instanceof Error)\n    return cb ? cb(appConf) : that.exitCli(conf.ERROR_EXIT);\n\n  process.env.PM2_JSON_PROCESSING = true;\n\n  // Get App list\n  var apps_name = [];\n  var proc_list = {};\n\n  // Here we pick only the field we want from the CLI when starting a JSON\n  appConf.forEach(function(app) {\n    // --only <app>\n    if (opts.only && opts.only != app.name)\n      return false;\n    // --watch\n    if (!app.watch && opts.watch && opts.watch === true)\n      app.watch = true;\n    // --ignore-watch\n    if (!app.ignore_watch && opts.ignore_watch)\n      app.ignore_watch = opts.ignore_watch;\n    // --instances <nb>\n    if (opts.instances && typeof(opts.instances) === 'number')\n      app.instances = opts.instances;\n    // --uid <user>\n    if (opts.uid)\n      app.uid = opts.uid;\n    // --gid <user>\n    if (opts.gid)\n      app.gid = opts.gid;\n    // Specific\n    if (app.append_env_to_name && opts.env)\n      app.name += ('-' + opts.env);\n    app.username = Common.getCurrentUsername();\n    apps_name.push(app.name);\n  });\n\n  that.Client.executeRemote('getMonitorData', {}, function(err, raw_proc_list) {\n    if (err) {\n      Common.printError(err);\n      return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);\n    }\n\n    /**\n     * Uniquify in memory process list\n     */\n    raw_proc_list.forEach(function(proc) {\n      proc_list[proc.name] = proc;\n    });\n\n    /**\n     * Auto detect application already started\n     * and act on them depending on action\n     */\n    eachLimit(Object.keys(proc_list), conf.CONCURRENT_ACTIONS, function(proc_name, next) {\n      // Skip app name (--only option)\n      if (apps_name.indexOf(proc_name) == -1)\n        return next();\n\n      if (!(action == 'reloadProcessId' ||\n            action == 'softReloadProcessId' ||\n            action == 'restartProcessId'))\n        throw new Error('Wrong action called');\n\n      var apps = appConf.filter(function(app) {\n        return app.name == proc_name;\n      });\n\n      var envs = apps.map(function(app){\n        // Binds env_diff to env and returns it.\n        return Common.mergeEnvironmentVariables(app, opts.env, deployConf);\n      });\n\n      // Assigns own enumerable properties of all\n      // Notice: if people use the same name in different apps,\n      //         duplicated envs will be overrode by the last one\n      var env = envs.reduce(function(e1, e2){\n        return util._extend(e1, e2);\n      });\n\n      // When we are processing JSON, allow to keep the new env by default\n      env.updateEnv = true;\n\n      // Pass `env` option\n      that._operate(action, proc_name, env, function(err, ret) {\n        if (err) Common.printError(err);\n\n        // For return\n        apps_info = apps_info.concat(ret);\n\n        that.Client.notifyGod(action, proc_name);\n        // And Remove from array to spy\n        apps_name.splice(apps_name.indexOf(proc_name), 1);\n        return next();\n      });\n\n    }, function(err) {\n      if (err) return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);\n      if (apps_name.length > 0 && action != 'start')\n        Common.printOut(conf.PREFIX_MSG_WARNING + 'Applications %s not running, starting...', apps_name.join(', '));\n      // Start missing apps\n      return startApps(apps_name, function(err, apps) {\n        apps_info = apps_info.concat(apps);\n        return cb ? cb(err, apps_info) : that.speedList(err ? 1 : 0);\n      });\n    });\n    return false;\n  });\n\n  function startApps(app_name_to_start, cb) {\n    var apps_to_start = [];\n    var apps_started = [];\n\n    appConf.forEach(function(app, i) {\n      if (app_name_to_start.indexOf(app.name) != -1) {\n        apps_to_start.push(appConf[i]);\n      }\n    });\n\n    eachLimit(apps_to_start, conf.CONCURRENT_ACTIONS, function(app, next) {\n      if (opts.cwd)\n        app.cwd = opts.cwd;\n      if (opts.force_name)\n        app.name = opts.force_name;\n      if (opts.started_as_module)\n        app.pmx_module = true;\n\n      var resolved_paths = null;\n\n      // hardcode script name to use `serve` feature inside a process file\n      if (app.script === 'serve') {\n        app.script = path.resolve(__dirname, 'API', 'Serve.js')\n      }\n\n      try {\n        resolved_paths = Common.resolveAppAttributes({\n          cwd      : that.cwd,\n          pm2_home : that.pm2_home\n        }, app);\n      } catch (e) {\n        return next();\n      }\n\n      if (!resolved_paths.env) resolved_paths.env = {};\n\n      // Set PM2 HOME in case of child process using PM2 API\n      resolved_paths.env['PM2_HOME'] = that.pm2_home;\n\n      var additional_env = Modularizer.getAdditionalConf(resolved_paths.name);\n      util._extend(resolved_paths.env, additional_env);\n\n      resolved_paths.env = Common.mergeEnvironmentVariables(resolved_paths, opts.env, deployConf);\n\n      delete resolved_paths.env.current_conf;\n\n      // Is KM linked?\n      resolved_paths.km_link = that.gl_is_km_linked;\n\n      that.Client.executeRemote('prepare', resolved_paths, function(err, data) {\n        if (err) {\n          Common.printError(conf.PREFIX_MSG_ERR + 'Process failed to launch %s', err.message ? err.message : err);\n          return next();\n        }\n        if (data.length === 0) {\n          Common.printError(conf.PREFIX_MSG_ERR + 'Process config loading failed', data);\n          return next();\n        }\n\n        Common.printOut(conf.PREFIX_MSG + 'App [%s] launched (%d instances)', data[0].pm2_env.name, data.length);\n        apps_started = apps_started.concat(data);\n        next();\n      });\n\n    }, function(err) {\n      return cb ? cb(err || null, apps_started) : that.speedList();\n    });\n    return false;\n  }\n};\n\n/**\n * Apply a RPC method on the json file\n * @private\n * @method actionFromJson\n * @param {string} action RPC Method\n * @param {object} options\n * @param {string|object} file file\n * @param {string} jsonVia action type (=only 'pipe' ?)\n * @param {Function}\n */\nAPI.prototype.actionFromJson = function(action, file, opts, jsonVia, cb) {\n  var appConf = {};\n  var ret_processes = [];\n  var that = this;\n\n  //accept programmatic calls\n  if (typeof file == 'object') {\n    cb = typeof jsonVia == 'function' ? jsonVia : cb;\n    appConf = file;\n  }\n  else if (jsonVia == 'file') {\n    var data = null;\n\n    try {\n      data = fs.readFileSync(file);\n    } catch(e) {\n      Common.printError(conf.PREFIX_MSG_ERR + 'File ' + file +' not found');\n      return cb ? cb(Common.retErr(e)) : that.exitCli(conf.ERROR_EXIT);\n    }\n\n    try {\n      appConf = Common.parseConfig(data, file);\n    } catch(e) {\n      Common.printError(conf.PREFIX_MSG_ERR + 'File ' + file + ' malformated');\n      console.error(e);\n      return cb ? cb(Common.retErr(e)) : that.exitCli(conf.ERROR_EXIT);\n    }\n  } else if (jsonVia == 'pipe') {\n    appConf = Common.parseConfig(file, 'pipe');\n  } else {\n    Common.printError('Bad call to actionFromJson, jsonVia should be one of file, pipe');\n    return that.exitCli(conf.ERROR_EXIT);\n  }\n\n  // Backward compatibility\n  if (appConf.apps)\n    appConf = appConf.apps;\n\n  if (!Array.isArray(appConf))\n    appConf = [appConf];\n\n  if ((appConf = Common.verifyConfs(appConf)) instanceof Error)\n    return cb ? cb(appConf) : that.exitCli(conf.ERROR_EXIT);\n\n  eachLimit(appConf, conf.CONCURRENT_ACTIONS, function(proc, next1) {\n    var name = '';\n    var new_env;\n\n    if (!proc.name)\n      name = path.basename(proc.script);\n    else\n      name = proc.name;\n\n    if (opts.only && opts.only != name)\n      return process.nextTick(next1);\n\n    if (opts && opts.env)\n      new_env = Common.mergeEnvironmentVariables(proc, opts.env);\n    else\n      new_env = Common.mergeEnvironmentVariables(proc);\n\n    that.Client.getProcessIdByName(name, function(err, ids) {\n      if (err) {\n        Common.printError(err);\n        return next1();\n      }\n      if (!ids) return next1();\n\n      eachLimit(ids, conf.CONCURRENT_ACTIONS, function(id, next2) {\n        var opts = {};\n\n        //stopProcessId could accept options to?\n        if (action == 'restartProcessId') {\n          opts = {id : id, env : new_env};\n        } else {\n          opts = id;\n        }\n\n        that.Client.executeRemote(action, opts, function(err, res) {\n          ret_processes.push(res);\n          if (err) {\n            Common.printError(err);\n            return next2();\n          }\n\n          if (action == 'restartProcessId') {\n            that.Client.notifyGod('restart', id);\n          } else if (action == 'deleteProcessId') {\n            that.Client.notifyGod('delete', id);\n          } else if (action == 'stopProcessId') {\n            that.Client.notifyGod('stop', id);\n          }\n\n          Common.printOut(conf.PREFIX_MSG + '[%s](%d) \\u2713', name, id);\n          return next2();\n        });\n      }, function(err) {\n        return next1(null, ret_processes);\n      });\n    });\n  }, function(err) {\n    if (cb) return cb(null, ret_processes);\n    else return that.speedList();\n  });\n};\n\n\n/**\n * Main function to operate with PM2 daemon\n *\n * @param {String} action_name  Name of action (restartProcessId, deleteProcessId, stopProcessId)\n * @param {String} process_name can be 'all', a id integer or process name\n * @param {Object} envs         object with CLI options / environment\n */\nAPI.prototype._operate = function(action_name, process_name, envs, cb) {\n  var that = this;\n  var update_env = false;\n  var ret = [];\n\n  // Make sure all options exist\n  if (!envs)\n    envs = {};\n\n  if (typeof(envs) == 'function'){\n    cb = envs;\n    envs = {};\n  }\n\n  // Set via env.update (JSON processing)\n  if (envs.updateEnv === true)\n    update_env = true;\n\n  var concurrent_actions = envs.parallel || conf.CONCURRENT_ACTIONS;\n\n  if (!process.env.PM2_JSON_PROCESSING || envs.commands) {\n    envs = that._handleAttributeUpdate(envs);\n  }\n\n  /**\n   * Set current updated configuration if not passed\n   */\n  if (!envs.current_conf) {\n    var _conf = fclone(envs);\n    envs = {\n      current_conf : _conf\n    }\n\n    // Is KM linked?\n    envs.current_conf.km_link = that.gl_is_km_linked;\n  }\n\n  /**\n   * Operate action on specific process id\n   */\n  function processIds(ids, cb) {\n    Common.printOut(conf.PREFIX_MSG + 'Applying action %s on app [%s](ids: %s)', action_name, process_name, ids);\n\n    if (action_name == 'deleteProcessId')\n      concurrent_actions = 10;\n\n    eachLimit(ids, concurrent_actions, function(id, next) {\n      var opts;\n\n      // These functions need extra param to be passed\n      if (action_name == 'restartProcessId' ||\n          action_name == 'reloadProcessId' ||\n          action_name == 'softReloadProcessId') {\n        var new_env = {};\n\n        if (update_env === true) {\n          if (conf.PM2_PROGRAMMATIC == true)\n            new_env = Common.safeExtend({}, process.env);\n          else\n            new_env = util._extend({}, process.env);\n\n          Object.keys(envs).forEach(function(k) {\n            new_env[k] = envs[k];\n          });\n        }\n        else {\n          new_env = envs;\n        }\n\n        opts = {\n          id  : id,\n          env : new_env\n        };\n      }\n      else {\n        opts = id;\n      }\n\n      that.Client.executeRemote(action_name, opts, function(err, res) {\n        if (err) {\n          Common.printError(conf.PREFIX_MSG_ERR + 'Process %s not found', id);\n          return next('Process not found');\n        }\n\n        if (action_name == 'restartProcessId') {\n          that.Client.notifyGod('restart', id);\n        } else if (action_name == 'deleteProcessId') {\n          that.Client.notifyGod('delete', id);\n        } else if (action_name == 'stopProcessId') {\n          that.Client.notifyGod('stop', id);\n        } else if (action_name == 'reloadProcessId') {\n          that.Client.notifyGod('reload', id);\n        } else if (action_name == 'softReloadProcessId') {\n          that.Client.notifyGod('graceful reload', id);\n        }\n\n        if (!Array.isArray(res))\n          res = [res];\n\n        // Filter return\n        res.forEach(function(proc) {\n          Common.printOut(conf.PREFIX_MSG + '[%s](%d) \\u2713', proc.pm2_env ? proc.pm2_env.name : process_name, id);\n\n          if (!proc.pm2_env) return false;\n\n          ret.push({\n            name         : proc.pm2_env.name,\n            pm_id        : proc.pm2_env.pm_id,\n            status       : proc.pm2_env.status,\n            restart_time : proc.pm2_env.restart_time,\n            pm2_env : {\n              name         : proc.pm2_env.name,\n              pm_id        : proc.pm2_env.pm_id,\n              status       : proc.pm2_env.status,\n              restart_time : proc.pm2_env.restart_time,\n              env          : proc.pm2_env.env\n            }\n          });\n        });\n\n        return next();\n      });\n    }, function(err) {\n      if (err) return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);\n      return cb ? cb(null, ret) : that.speedList();\n    });\n  }\n\n  if (process_name == 'all') {\n    that.Client.getAllProcessId(function(err, ids) {\n      if (err) {\n        Common.printError(err);\n        return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);\n      }\n      if (!ids || ids.length === 0) {\n        Common.printError(conf.PREFIX_MSG_WARNING + 'No process found');\n        return cb ? cb(new Error('process name not found')) : that.exitCli(conf.ERROR_EXIT);\n      }\n\n      return processIds(ids, cb);\n    });\n  }\n  // operate using regex\n  else if (isNaN(process_name) && process_name[0] === '/' && process_name[process_name.length - 1] === '/') {\n    var regex = new RegExp(process_name.replace(/\\//g, ''));\n\n    that.Client.executeRemote('getMonitorData', {}, function(err, list) {\n      if (err) {\n        Common.printError('Error retrieving process list: ' + err);\n        return cb(err);\n      }\n      var found_proc = [];\n      list.forEach(function(proc) {\n        if (regex.test(proc.pm2_env.name)) {\n          found_proc.push(proc.pm_id);\n        }\n      });\n\n      if (found_proc.length === 0) {\n        Common.printError(conf.PREFIX_MSG_WARNING + 'No process found');\n        return cb ? cb(new Error('process name not found')) : that.exitCli(conf.ERROR_EXIT);\n      }\n\n      return processIds(found_proc, cb);\n    });\n  }\n  else if (isNaN(process_name)) {\n    /**\n     * We can not stop or delete a module but we can restart it\n     * to refresh configuration variable\n     */\n    var allow_module_restart = action_name == 'restartProcessId' ? true : false;\n\n    that.Client.getProcessIdByName(process_name, allow_module_restart, function(err, ids) {\n      if (err) {\n        Common.printError(err);\n        return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);\n      }\n      if (!ids || ids.length === 0) {\n        Common.printError(conf.PREFIX_MSG_ERR + 'Process %s not found', process_name);\n        return cb ? cb(new Error('process name not found')) : that.exitCli(conf.ERROR_EXIT);\n      }\n\n      /**\n       * Determine if the process to restart is a module\n       * if yes load configuration variables and merge with the current environment\n       */\n      var additional_env = Modularizer.getAdditionalConf(process_name);\n      util._extend(envs, additional_env);\n\n      return processIds(ids, cb);\n    });\n  } else {\n    // Check if application name as number is an app name\n    that.Client.getProcessIdByName(process_name, function(err, ids) {\n      if (ids.length > 0)\n        return processIds(ids, cb);\n      // Else operate on pm id\n      return processIds([process_name], cb);\n    });\n  }\n};\n\n/**\n * Converts CamelCase Commander.js arguments\n * to Underscore\n * (nodeArgs -> node_args)\n */\nAPI.prototype._handleAttributeUpdate = function(opts) {\n  var conf = Config.transCMDToConf(opts);\n  var that = this;\n\n  if (typeof(conf.name) != 'string')\n    delete conf.name;\n\n  var argsIndex = 0;\n  if (opts.rawArgs && (argsIndex = opts.rawArgs.indexOf('--')) >= 0) {\n    conf.args = opts.rawArgs.slice(argsIndex + 1);\n  }\n\n  var appConf = Common.verifyConfs(conf)[0];\n\n  if (appConf instanceof Error) {\n    Common.printError('Error while transforming CamelCase args to underscore');\n    return appConf;\n  }\n\n  if (argsIndex == -1)\n    delete appConf.args;\n  if (appConf.name == 'undefined')\n    delete appConf.name;\n\n  delete appConf.exec_mode;\n\n  if (util.isArray(appConf.watch) && appConf.watch.length === 0) {\n    if (!~opts.rawArgs.indexOf('--watch'))\n      delete appConf.watch\n  }\n\n  // Force deletion of defaults values set by commander\n  // to avoid overriding specified configuration by user\n  if (appConf.treekill === true)\n    delete appConf.treekill;\n  if (appConf.pmx === true)\n    delete appConf.pmx;\n  if (appConf.vizion === true)\n    delete appConf.vizion;\n  if (appConf.automation === true)\n    delete appConf.automation;\n  if (appConf.autorestart === true)\n    delete appConf.autorestart;\n\n  return appConf;\n};\n\nAPI.prototype.getProcessIdByName = function(name, cb) {\n  var that = this;\n\n  this.Client.getProcessIdByName(name, function(err, id) {\n    if (err) {\n      Common.printError(err);\n      return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);\n    }\n    console.log(id);\n    return cb ? cb(null, id) : that.exitCli(conf.SUCCESS_EXIT);\n  });\n};\n\n/**\n * Description\n * @method jlist\n * @param {} debug\n * @return\n */\nAPI.prototype.jlist = function(debug) {\n  var that = this;\n\n  that.Client.executeRemote('getMonitorData', {}, function(err, list) {\n    if (err) {\n      Common.printError(err);\n      that.exitCli(conf.ERROR_EXIT);\n    }\n\n    if (debug) {\n      process.stdout.write(util.inspect(list, false, null, false));\n    }\n    else {\n      process.stdout.write(JSON.stringify(list));\n    }\n\n    that.exitCli(conf.SUCCESS_EXIT);\n\n  });\n};\n\nvar gl_retry = 0;\n\n/**\n * Description\n * @method speedList\n * @return\n */\nAPI.prototype.speedList = function(code) {\n  var that = this;\n\n  // Do nothing if PM2 called programmatically and not called from CLI (also in exitCli)\n  if (conf.PM2_PROGRAMMATIC && process.env.PM2_USAGE != 'CLI') return false;\n\n  that.Client.executeRemote('getMonitorData', {}, function(err, list) {\n    if (err) {\n      if (gl_retry == 0) {\n        gl_retry += 1;\n        return setTimeout(that.speedList.bind(that), 1400);\n      }\n      console.error('Error retrieving process list: %s.\\nA process seems to be on infinite loop, retry in 5 seconds',err);\n      return that.exitCli(conf.ERROR_EXIT);\n    }\n    if (process.stdout.isTTY === false) {\n      UX.miniDisplay(list);\n    }\n    else if (commander.miniList && !commander.silent)\n      UX.miniDisplay(list);\n    else if (!commander.silent) {\n      if (that.gl_interact_infos) {\n        Common.printOut(chalk.green.bold('●') + ' Agent Online | Dashboard Access: ' + chalk.bold('https://app.keymetrics.io/#/r/%s') + ' | Server name: %s', that.gl_interact_infos.public_key, that.gl_interact_infos.machine_name);\n      }\n      UX.dispAsTable(list, commander);\n      Common.printOut(chalk.white.italic(' Use `pm2 show <id|name>` to get more details about an app'));\n    }\n\n    if (that.Client.daemon_mode == false) {\n      Common.printOut('[--no-daemon] Continue to stream logs');\n      Common.printOut('[--no-daemon] Exit on target PM2 exit pid=' + fs.readFileSync(conf.PM2_PID_FILE_PATH).toString());\n      global._auto_exit = true;\n      return that.streamLogs('all', 0, false, 'HH:mm:ss', false);\n    }\n    else if (commander.attach === true) {\n      return that.streamLogs('all', 0, false, null, false);\n    }\n    else {\n      return that.exitCli(code ? code : conf.SUCCESS_EXIT);\n    }\n  });\n}\n\n/**\n * Scale up/down a process\n * @method scale\n */\nAPI.prototype.scale = function(app_name, number, cb) {\n  var that = this;\n\n  function addProcs(proc, value, cb) {\n    (function ex(proc, number) {\n      if (number-- === 0) return cb();\n      Common.printOut(conf.PREFIX_MSG + 'Scaling up application');\n      that.Client.executeRemote('duplicateProcessId', proc.pm2_env.pm_id, ex.bind(this, proc, number));\n    })(proc, number);\n  }\n\n  function rmProcs(procs, value, cb) {\n    var i = 0;\n\n    (function ex(procs, number) {\n      if (number++ === 0) return cb();\n      that._operate('deleteProcessId', procs[i++].pm2_env.pm_id, ex.bind(this, procs, number));\n    })(procs, number);\n  }\n\n  function end() {\n    return cb ? cb(null, {success:true}) : that.speedList();\n  }\n\n  this.Client.getProcessByName(app_name, function(err, procs) {\n    if (err) {\n      Common.printError(err);\n      return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);\n    }\n\n    if (!procs || procs.length === 0) {\n      Common.printError(conf.PREFIX_MSG_ERR + 'Application %s not found', app_name);\n      return cb ? cb(new Error('App not found')) : that.exitCli(conf.ERROR_EXIT);\n    }\n\n    var proc_number = procs.length;\n\n    if (typeof(number) === 'string' && number.indexOf('+') >= 0) {\n      number = parseInt(number, 10);\n      return addProcs(procs[0], number, end);\n    }\n    else if (typeof(number) === 'string' && number.indexOf('-') >= 0) {\n      number = parseInt(number, 10);\n      return rmProcs(procs[0], number, end);\n    }\n    else {\n      number = parseInt(number, 10);\n      number = number - proc_number;\n\n      if (number < 0)\n        return rmProcs(procs, number, end);\n      else if (number > 0)\n        return addProcs(procs[0], number, end);\n      else {\n        Common.printError(conf.PREFIX_MSG_ERR + 'Nothing to do');\n        return cb ? cb(new Error('Same process number')) : that.exitCli(conf.ERROR_EXIT);\n      }\n    }\n  });\n};\n\n/**\n * Description\n * @method describeProcess\n * @param {} pm2_id\n * @return\n */\nAPI.prototype.describe = function(pm2_id, cb) {\n  var that = this;\n\n  var found_proc = [];\n\n  that.Client.executeRemote('getMonitorData', {}, function(err, list) {\n    if (err) {\n      Common.printError('Error retrieving process list: ' + err);\n      that.exitCli(conf.ERROR_EXIT);\n    }\n\n    list.forEach(function(proc) {\n      if ((!isNaN(pm2_id)    && proc.pm_id == pm2_id) ||\n          (typeof(pm2_id) === 'string' && proc.name  == pm2_id)) {\n        found_proc.push(proc);\n      }\n    });\n\n    if (found_proc.length === 0) {\n      Common.printError(conf.PREFIX_MSG_WARNING + '%s doesn\\'t exist', pm2_id);\n      return cb ? cb(null, []) : that.exitCli(conf.ERROR_EXIT);\n    }\n\n    if (!cb) {\n      found_proc.forEach(function(proc) {\n        UX.describeTable(proc);\n      });\n    }\n\n    return cb ? cb(null, found_proc) : that.exitCli(conf.SUCCESS_EXIT);\n  });\n};\n\n/**\n * API method to perform a deep update of PM2\n * @method deepUpdate\n */\nAPI.prototype.deepUpdate = function(cb) {\n  var that = this;\n\n  Common.printOut(conf.PREFIX_MSG + 'Updating PM2...');\n\n  var exec = require('shelljs').exec;\n  var child = exec(\"npm i -g pm2@latest; pm2 update\", {async : true});\n\n  child.stdout.on('end', function() {\n    Common.printOut(conf.PREFIX_MSG + 'PM2 successfully updated');\n    cb ? cb(null, {success:true}) : that.exitCli(conf.SUCCESS_EXIT);\n  });\n};\n"
  },
  {
    "path": "examples/sourcemap-auto-resolve/README.md",
    "content": "\nHere is a source map support demo.\nSource map are automatically handled by pm2:\n\n```bash\n$ pm2 start process.config.js\n```\n\nHere is the result:\n\n```\nPM2        | App [API-minified] with id [1] and pid [10634], exited with code [1] via signal [SIGINT]\nPM2        | Starting execution sequence in -fork mode- for app name:API-minified id:1\nPM2        | App name:API-minified id:1 online\n1|API-mini | Error: muhahahaha\n1|API-mini |     at Object.<anonymous> (/home/unitech/keymetrics/pm2/examples/sourcemap-auto-resolve/API.js:226:7)\n1|API-mini |     at Module._compile (module.js:641:30)\n1|API-mini |     at Object.Module._extensions..js (module.js:652:10)\n1|API-mini |     at Module.load (module.js:560:32)\n1|API-mini |     at tryModuleLoad (module.js:503:12)\n1|API-mini |     at Function.Module._load (module.js:495:3)\n1|API-mini |     at Function._load (/home/unitech/keymetrics/pm2/node_modules/pmx/lib/transaction.js:94:21)\n1|API-mini |     at Object.<anonymous> (/home/unitech/keymetrics/pm2/lib/ProcessContainerFork.js:80:21)\n1|API-mini |     at Module._compile (module.js:641:30)\n1|API-mini |     at Object.Module._extensions..js (module.js:652:10)\nPM2        | App [API-minified] with id [1] and pid [10660], exited with code [1] via signal [SIGINT]\nPM2        | Starting execution sequence in -fork mode- for app name:API-minified id:1\n```\n\nYou will see that the line has been resolved:\n\n```\n1|API-mini | Error: muhahahaha\n1|API-mini |     at Object.<anonymous> (/home/unitech/keymetrics/pm2/examples/sourcemap-auto-resolve/API.js:226:7)\n1|API-mini |     at Module._compile (module.js:641:30)\n```\n"
  },
  {
    "path": "examples/sourcemap-auto-resolve/process.config.js",
    "content": "module.exports = {\n  apps : [{\n    name   : \"API-minified\",\n    script : \"API.min.js\"\n  }]\n}\n"
  },
  {
    "path": "examples/start-a-binary/ls.yml",
    "content": "apps:\n  - script : './ls'\n"
  },
  {
    "path": "examples/test-all-keymetrics-features/README.md",
    "content": "\nTry all features of Keymetrics:\n\n```bash\n# Make sure you've created keymetrics account\n$ pm2 register\n# Start all applications\n$ pm2 start process.config.js\n# Open Dashboard\n$ pm2 open\n```\n"
  },
  {
    "path": "examples/test-all-keymetrics-features/actions-fibonacci.js",
    "content": "\nvar stop = false;\n\n/**\n * Description\n * @method add\n * @param {} a\n * @param {} b\n * @return sum\n */\nfunction add(a, b) {\n  while (a.length < b.length) a.unshift(0);\n  while (a.length > b.length) b.unshift(0);\n  var carry = 0, sum = []\n  for (var i = a.length - 1; i >= 0; i--) {\n    var s = a[i] + b[i] + carry;\n    if (s >= 10) {\n      s = s - 10;\n      carry = 1;\n    } else {\n      carry = 0;\n    }\n    sum.unshift(s);\n  }\n  if (carry)\n    sum.unshift(carry);\n  return sum;\n}\n\n/**\n * Description\n * @method fib\n * @param {} n\n * @return CallExpression\n */\nfunction fib(n) {\n  var f1 = [0];\n  var f2 = [1];\n\n  while (n--) {\n    var f3 = add(f1, f2)\n    if (stop) return false;\n    f1 = f2;\n    f2 = f3;\n  }\n  return f1.join(\"\");\n}\n\n\nvar axm = require('@pm2/io');\n\naxm.action('load:start', function(reply) {\n  fib(50000);\n  reply({success : true});\n});\n\n\naxm.action('load:stop', function(reply) {\n  stop = true;\n  reply({success : true});\n});\n"
  },
  {
    "path": "examples/test-all-keymetrics-features/custom_action.js",
    "content": "\nvar axm = require('@pm2/io');\n\naxm.action('getEnv', function(reply) {\n  reply(process.env);\n});\n\naxm.action('sayHello', function(reply) {\n  reply({\n    msg : 'Yes hello and so? Xie Xie'\n  });\n});\n\naxm.action('throwError', function(reply) {\n  //@todo : replying a error does not work\n  reply(new Error('Error thrown'));\n  throw new Error('asdadsadsasd');\n});\n"
  },
  {
    "path": "examples/test-all-keymetrics-features/custom_action_with_params.js",
    "content": "\nvar axm = require('@pm2/io');\n\naxm.action('refresh:db', { comment : 'Refresh the database' }, function(reply) {\n  console.log('Refreshing');\n  reply({success : true});\n});\n\naxm.action('chanme:ladb', { comment : 'Refresh la BIG database' }, function(reply) {\n  console.log('Refreshing BIG DB');\n  reply({success : true});\n});\n\naxm.action('rm:rf', { comment : 'Delete moi ca plus vite que ca !' }, function(reply) {\n  console.log('RMING RFING');\n  reply({success : true});\n});\n\naxm.action('rm:roff', function(reply) {\n  console.log('RMING RFING');\n  reply({success : true});\n});\n"
  },
  {
    "path": "examples/test-all-keymetrics-features/event.js",
    "content": "\nvar axm = require('@pm2/io');\n\nsetInterval(function() {\n\n  axm.emit('content:page:created', {\n    msg : 'A CMS page has been created',\n    user : 'Francois Debiole'\n  });\n\n}, 1000);\n"
  },
  {
    "path": "examples/test-all-keymetrics-features/http_app.js",
    "content": "\n\nvar io = require('@pm2/io').init({ http : true });\nvar probe = io.probe();\n\nvar http  = require('http');\n\n/**\n * Probe system #3 - Meter\n *\n * Probe things that are measured as events / interval.\n */\nvar meter = probe.meter({\n  name    : 'req/min',\n  seconds : 60\n});\n\n\nhttp.createServer(function(req, res) {\n  // Then mark it at every connections\n  meter.mark();\n  setTimeout(function() {\n    res.end('Thanks');\n  }, 500);\n}).listen(5005);\n\nfunction doRequest() {\n  var options = {\n    hostname : '127.0.0.1',\n    port     : 5005,\n    path     : '/users',\n    method   : 'GET',\n    headers  : { 'Content-Type': 'application/json' }\n  };\n\n  var req = http.request(options, function(res) {\n    res.setEncoding('utf8');\n    res.on('data', function (data) {\n      console.log(data);\n    });\n  });\n  req.on('error', function(e) {\n    console.log('problem with request: ' + e.message);\n  });\n  req.end();\n\n}\n\nsetInterval(function() {\n  doRequest();\n}, 1000);\nprocess.on('message', function(msg) {\n  if (msg == 'shutdown') {\n    console.log('Closing all connections...');\n    setTimeout(function() {\n      console.log('Finished closing connections');\n      process.exit(0);\n    }, 500);\n  }\n});\n"
  },
  {
    "path": "examples/test-all-keymetrics-features/http_transaction.js",
    "content": "\n\nvar axm = require('@pm2/io');\n\nvar probe = axm.probe();\n\nvar http = require('http');\n\nvar meter = probe.meter({\n  name    : 'req/min',\n  seconds : 60\n});\n\nhttp.createServer(function(req, res) {\n  res.writeHead(200);\n  meter.mark();\n  setTimeout(function() {\n    res.end('transaction');\n  }, 1000);\n}).listen(10010);\n\nsetInterval(function() {\n  request(['/user', '/bla', '/user/lol/delete', '/POST/POST'][Math.floor((Math.random() * 4))]);\n}, 1500);\n\nfunction makeid()\n{\n  var text = \"\";\n  var possible = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\";\n\n  for( var i=0; i < 5; i++ )\n    text += possible.charAt(Math.floor(Math.random() * possible.length));\n\n  return text;\n}\n\nfunction request(path) {\n  var options = {\n    hostname: '127.0.0.1'\n    ,port: 9010\n    ,path: path || '/users'\n    ,method: 'GET'\n    ,headers: { 'Content-Type': 'application/json' }\n  };\n\n  var req = http.request(options, function(res) {\n    res.setEncoding('utf8');\n    res.on('data', function (data) {\n      console.log(data); // I can't parse it because, it's a string. why?\n    });\n  });\n  req.on('error', function(e) {\n    console.log('problem with request: ' + e.message);\n  });\n  req.end();\n}\n"
  },
  {
    "path": "examples/test-all-keymetrics-features/pm2_probe.js",
    "content": "var io     = require('@pm2/io');\nvar pm2     = require('../..');\nvar fs      = require('fs');\nvar path    = require('path');\n\nvar conf = io.initModule({\n  comment          : 'This module monitors PM2',\n  errors           : true,\n  latency          : false,\n  versioning       : false,\n  show_module_meta : false,\n  module_type      : 'database',\n\n  widget : {\n    theme            : ['#111111', '#1B2228', '#807C7C', '#807C7C'],\n    logo             : 'https://keymetrics.io/assets/images/pm2.20d3ef.png?v=0b71a506ce'\n  }\n});\n\nvar probe = io.probe();\n\nvar pm2_procs = 0;\n\npm2.connect(function() {\n\n  setInterval(function() {\n    pm2.list(function(err, procs) {\n      pm2_procs = procs.length;\n    });\n  }, 2000);\n\n  var metric = probe.metric({\n    name  : 'Processes',\n    value : function() {\n      return pm2_procs;\n    }\n  });\n});\n"
  },
  {
    "path": "examples/test-all-keymetrics-features/probes.js",
    "content": "\n\nvar io = require('@pm2/io');\n\nvar users = {\n  'alex'  : 'ok',\n  'musta' : 'fa'\n};\n\n/**\n * Monitor synchronous return of functions\n */\nvar rt_users = io.metric({\n  name : 'Realtime user',\n  value : function() {\n    return Object.keys(users).length;\n  }\n});\n\n/**\n * Monitor value\n */\nvar cheerio = io.metric({\n  name : 'Cheerio',\n  value : true\n});\n\n/**\n * Meter for HTTP\n */\nvar meter = io.meter({\n  name    : 'req/min',\n  seconds : 60\n});\n\nvar http  = require('http');\n\nhttp.createServer(function(req, res) {\n  meter.mark();\n  res.end('Thanks');\n}).listen(5006);\n\n/**\n * Meter example\n */\n\nvar meter2 = io.meter({\n  name    : 'random',\n  seconds : 1\n});\n\nsetInterval(function() {\n  meter2.mark(Math.random() * 100);\n}, 10);\n\n\nsetTimeout(function() {\n  cheerio.set(false);\n  counter.inc();\n}, 1100);\n\n/**\n * Counter\n */\n\nvar counter = io.counter({\n  name : 'Downloads'\n});\n\ncounter.inc();\ncounter.dec();\ncounter.inc();\ncounter.inc();\n\n\n//axm.catchAll();\n\nio.action('throw error', function(reply) {\n  setTimeout(function() {\n    console.log('log message from echo auto kill');\n    throw new Error('Exitasdsadasdsda unacepted 222222 !!');\n  }, 2000);\n});\n\n\nio.action('dec', function(reply) {\n  counter.dec();\n  reply({success : true});\n});\n\nio.action('inc', function(reply) {\n  counter.inc();\n  reply({success : true});\n});\n\nio.action('do:query', function(reply) {\n  var options = {\n    hostname : '127.0.0.1',\n    port     : 5005,\n    path     : '/users',\n    method   : 'GET',\n    headers  : { 'Content-Type': 'application/json' }\n  };\n\n  var req = http.request(options, function(res) {\n    res.setEncoding('utf8');\n    res.on('data', function (data) {\n      console.log(data); // I can't parse it because, it's a string. why?\n    });\n  });\n  req.on('error', function(e) {\n    console.log('problem with request: ' + e.message);\n  });\n  req.end();\n\n  reply({success : true});\n});\n"
  },
  {
    "path": "examples/test-all-keymetrics-features/process-load.config.js",
    "content": "module.exports = {\n  pm2 : [{\n    script : \"http_app.js\",\n    instances : 10\n  }, {\n    script : \"throw.js\",\n    instances : 10\n  }]\n}\n"
  },
  {
    "path": "examples/test-all-keymetrics-features/process-transpose.js",
    "content": "\nvar Probe = require('@pm2/io').probe();\n\nvar counter = 0;\n\n// var metric = Probe.transpose({\n//   name : 'data-flow',\n//   data : function() {\n//     return {\n//       a : {\n//         b  : {\n//           data : 'textflow',\n//           array : [ 'yes', 'it', 'is' ]\n//         }\n//       }\n//     }\n//   }\n// });\n\nsetInterval(function() {\n}, 100);\n"
  },
  {
    "path": "examples/test-all-keymetrics-features/process.config.js",
    "content": "module.exports = {\n  \"pm2\" : [{\n    \"script\" : \"pm2_probe.js\"\n  }, {\n    \"script\" : \"event.js\"\n  }, {\n    \"script\" : \"http_app.js\",\n    \"instances\" : 4\n  }, {\n    \"script\" : \"probes.js\"\n  }, {\n    \"script\" : \"http_transaction.js\"\n  }, {\n    \"script\" : \"process-transpose.js\"\n  }, {\n    \"script\" : \"scoped-actions.js\"\n  }, {\n    \"script\" : \"custom_action.js\"\n  }, {\n    \"script\" : \"custom_action_with_params.js\"\n  }, {\n    \"script\" : \"http_transaction.js\"\n  }, {\n    \"script\" : \"throw.js\"\n  }]\n}\n"
  },
  {
    "path": "examples/test-all-keymetrics-features/scoped-actions.js",
    "content": "\nvar io = require('@pm2/io');\n\nio.scopedAction('simple test', function(data, emitter) {\n  var i = setInterval(function() {\n    emitter.send('output-stream');\n  }, 100);\n\n  setTimeout(function() {\n\n    emitter.end('end');\n    clearInterval(i);\n  }, 3000);\n});\n\nio.scopedAction('throwing error', function(data, emitter) {\n  var i = setInterval(function() {\n    emitter.send('output-stream');\n  }, 100);\n\n  setTimeout(function() {\n\n    throw new Error('errr');\n  }, 1000);\n});\n"
  },
  {
    "path": "examples/test-all-keymetrics-features/test-threshold.js",
    "content": "\nvar axm = require('@pm2/io');\n\n\nvar users = 55\nvar battery = 80\n\nvar door = 0\nvar door_2 = 0\n\naxm.action('tozero', function(reply) {\n  door = 0\n  reply({ok:true});\n});\n\n\naxm.action('toone', function(reply) {\n  door = 1\n  reply({ok:true});\n});\n\naxm.metric({\n  name : 'door',\n  value : function() {\n    return door\n  }\n});\n\n\naxm.action('tofalse', function(reply) {\n  door_2 = false\n  reply({ok:true});\n});\n\n\naxm.action('totrue', function(reply) {\n  door_2 = true\n  reply({ok:true});\n});\n\naxm.metric({\n  name : 'door2',\n  value : function() {\n    return door_2\n  }\n});\n\n\naxm.action('setval', function(reply) {\n  users = 50\n  reply(process.env);\n});\n\naxm.action('setbatterylow', function(reply) {\n  battery = 10\n  reply();\n});\n\naxm.action('sayHello', function(reply) {\n  reply({\n    msg : 'Yes hello and so? Xie Xie'\n  });\n});\n\naxm.metric({\n  name : 'random',\n  value : function() {\n    return Math.floor((Math.random() * 100) + 1);;\n  }\n});\n\n\naxm.metric({\n  name : 'Realtime user',\n  value : function() {\n    return users;\n  }\n});\n\n\naxm.metric({\n  name : 'neewmetric',\n  value : function() {\n    return 1;\n  }\n});\n\naxm.metric({\n  name : 'battery',\n  value : function() {\n    return battery;\n  }\n});\n"
  },
  {
    "path": "examples/test-all-keymetrics-features/throw.js",
    "content": "\nvar axm = require('@pm2/io');\n\nsetTimeout(function() {\n  console.log('log message from echo auto kill');\n  throw new Error('Exitasdsadasdsda unacepted 222222 !!');\n}, 2000);\n"
  },
  {
    "path": "examples/treekill/app.js",
    "content": "\nconst spawn = require('child_process').spawn\nvar fs = require('fs');\n\nsetTimeout(function() {\n  daemonize('pm2 ls --watch');\n  // setTimeout(function() {\n  //   process.exit(0);\n  // }, 300);\n\n  setInterval(function() {}, 1000);\n}, 3000);\n\nvar daemonize = function exec (cmd, cb) {\n  const args = cmd.split(' ')\n  const bin = args.shift()\n\n  var logFile = '/tmp/test.log';\n  const fdOut = fs.openSync(logFile, 'a+')\n  const fdErr = fs.openSync(logFile, 'a+')\n\n  console.log(`Spawning command ${cmd}`)\n  console.log(`Log file: ${logFile}`)\n\n  const installInstance = spawn(bin, args, {\n    env: process.env,\n    detached: true,\n    stdio: ['ignore', fdOut, fdErr]\n  })\n\n  installInstance.unref()\n  if (typeof cb === 'function') {\n    cb()\n  }\n}\n"
  },
  {
    "path": "examples/treekill/process.json",
    "content": "{\n  \"apps\" : {\n    \"treekill\" : false,\n    \"script\" : \"app.js\"\n  }\n}\n"
  },
  {
    "path": "examples/udp/client.js",
    "content": "var udp = require('dgram');\n\n// -------------------- udp client ----------------\n\nvar buffer = require('buffer');\n\n// creating a client socket\nvar client = udp.createSocket('udp4');\n\n//buffer msg\nvar data = Buffer.from('siddheshrane');\n\nclient.on('message',function(msg,info){\n  console.log('Data received from server : ' + msg.toString());\n  console.log('Received %d bytes from %s:%d\\n',msg.length, info.address, info.port);\n});\n\nsetInterval(() => {\n  //sending msg\n  client.send(data,2222,'localhost',function(error){\n    if(error){\n      client.close();\n    }else{\n      console.log('Data sent !!!');\n    }\n  });\n}, 10)\n\nvar data1 = Buffer.from('hello');\nvar data2 = Buffer.from('world');\n\n//sending multiple msg\nclient.send([data1,data2],2222,'localhost',function(error){\n  if(error){\n    client.close();\n  }else{\n    console.log('Data sent !!!');\n  }\n});\n"
  },
  {
    "path": "examples/udp/server.js",
    "content": "var udp = require('dgram');\n\n// --------------------creating a udp server --------------------\n\n// creating a udp server\nvar server = udp.createSocket('udp4');\n\n// emits when any error occurs\nserver.on('error',function(error){\n  console.log('Error: ' + error);\n  server.close();\n});\n\n// emits on new datagram msg\nserver.on('message',function(msg,info){\n  console.log('Data received from client : ' + msg.toString());\n  console.log('Received %d bytes from %s:%d\\n',msg.length, info.address, info.port);\n\n  //sending msg\n  server.send(msg,info.port,'localhost',function(error){\n    if(error){\n      //client.close();\n    }else{\n      console.log('Data sent !!!');\n    }\n\n  });\n\n});\n\n//emits when socket is ready and listening for datagram msgs\nserver.on('listening',function(){\n  var address = server.address();\n  var port = address.port;\n  var family = address.family;\n  var ipaddr = address.address;\n  console.log('Server is listening at port' + port);\n  console.log('Server ip :' + ipaddr);\n  console.log('Server is IP4/IP6 : ' + family);\n});\n\n//emits after the socket is closed using socket.close();\nserver.on('close',function(){\n  console.log('Socket is closed !');\n});\n\nserver.bind(2222);\n"
  },
  {
    "path": "examples/using-pm2-and-transpilers/README.md",
    "content": "\n## Coffee Script\n\n```\n$ pm2 install coffee-script\n$ pm2 start echo.coffee\n```\n\n## Typescript\n\n```\n$ pm2 install typescript\n$ pm2 start http.ts\n```\n\n## Livescript\n\n\n```\n$ pm2 install livescript\n$ pm2 start echo.ls\n```\n"
  },
  {
    "path": "examples/using-pm2-and-transpilers/echo.coffee",
    "content": "#!/usr/bin/env coffee\n\nsetInterval (-> console.log 'ok'), 500"
  },
  {
    "path": "examples/using-pm2-and-transpilers/echo.ls",
    "content": "#!/usr/bin/env lsc\n\nsetInterval (-> console.log 'ok'), 500"
  },
  {
    "path": "examples/using-pm2-and-transpilers/echo.ts",
    "content": "\nclass Greeter {\n  constructor(public greeting: string) { }\n  greet() {\n    return \"<h1>\" + this.greeting + \"</h1>\";\n  }\n};\n\nvar greeter = new Greeter(\"Hello, world!\");\n\nconsole.log(greeter.greet());\n"
  },
  {
    "path": "examples/using-pm2-and-transpilers/http.ts",
    "content": "/// <reference path=\"./node.d.ts\" />\n\n/**\n * See the node.js TypeScript definition needed for this\n * example here: https://github.com/borisyankov/DefinitelyTyped\n */\n\nimport * as Http from \"http\";\n\nclass MyServer {\n  private header:Object = {'Content-Type': 'text/plain'};\n\n  constructor() {\n    var server:Http.Server = Http.createServer(this.onRequest);\n    server.listen(3000, () => {\n      console.log(\"Server started on port 3000\");\n    });\n  }\n\n  private onRequest(request:Http.ServerRequest, response:Http.ServerResponse):void {\n    response.writeHead(200, this.header);\n    response.end(\"Hello TypeScript & node.js\");\n  }\n}\n\nvar myServer = new MyServer();\n"
  },
  {
    "path": "examples/using-pm2-and-transpilers/node.d.ts",
    "content": "// Type definitions for Node.js v6.x\n// Project: http://nodejs.org/\n// Definitions by: Microsoft TypeScript <http://typescriptlang.org>, DefinitelyTyped <https://github.com/DefinitelyTyped/DefinitelyTyped>\n// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped\n\n/************************************************\n*                                               *\n*               Node.js v6.x API                *\n*                                               *\n************************************************/\n\ninterface Error {\n    stack?: string;\n}\n\ninterface ErrorConstructor {\n    captureStackTrace(targetObject: Object, constructorOpt?: Function): void;\n    stackTraceLimit: number;\n}\n\n// compat for TypeScript 1.8\n// if you use with --target es3 or --target es5 and use below definitions,\n// use the lib.es6.d.ts that is bundled with TypeScript 1.8.\ninterface MapConstructor { }\ninterface WeakMapConstructor { }\ninterface SetConstructor { }\ninterface WeakSetConstructor { }\n\n/************************************************\n*                                               *\n*                   GLOBAL                      *\n*                                               *\n************************************************/\ndeclare var process: NodeJS.Process;\ndeclare var global: NodeJS.Global;\n\ndeclare var __filename: string;\ndeclare var __dirname: string;\n\ndeclare function setTimeout(callback: (...args: any[]) => void, ms: number, ...args: any[]): NodeJS.Timer;\ndeclare function clearTimeout(timeoutId: NodeJS.Timer): void;\ndeclare function setInterval(callback: (...args: any[]) => void, ms: number, ...args: any[]): NodeJS.Timer;\ndeclare function clearInterval(intervalId: NodeJS.Timer): void;\ndeclare function setImmediate(callback: (...args: any[]) => void, ...args: any[]): any;\ndeclare function clearImmediate(immediateId: any): void;\n\ninterface NodeRequireFunction {\n    (id: string): any;\n}\n\ninterface NodeRequire extends NodeRequireFunction {\n    resolve(id: string): string;\n    cache: any;\n    extensions: any;\n    main: any;\n}\n\ndeclare var require: NodeRequire;\n\ninterface NodeModule {\n    exports: any;\n    require: NodeRequireFunction;\n    id: string;\n    filename: string;\n    loaded: boolean;\n    parent: any;\n    children: any[];\n}\n\ndeclare var module: NodeModule;\n\n// Same as module.exports\ndeclare var exports: any;\ndeclare var SlowBuffer: {\n    new (str: string, encoding?: string): Buffer;\n    new (size: number): Buffer;\n    new (size: Uint8Array): Buffer;\n    new (array: any[]): Buffer;\n    prototype: Buffer;\n    isBuffer(obj: any): boolean;\n    byteLength(string: string, encoding?: string): number;\n    concat(list: Buffer[], totalLength?: number): Buffer;\n};\n\n\n// Buffer class\ntype BufferEncoding = \"ascii\" | \"utf8\" | \"utf16le\" | \"ucs2\" | \"binary\" | \"hex\";\ninterface Buffer extends NodeBuffer { }\n\n/**\n * Raw data is stored in instances of the Buffer class.\n * A Buffer is similar to an array of integers but corresponds to a raw memory allocation outside the V8 heap.  A Buffer cannot be resized.\n * Valid string encodings: 'ascii'|'utf8'|'utf16le'|'ucs2'(alias of 'utf16le')|'base64'|'binary'(deprecated)|'hex'\n */\ndeclare var Buffer: {\n    /**\n     * Allocates a new buffer containing the given {str}.\n     *\n     * @param str String to store in buffer.\n     * @param encoding encoding to use, optional.  Default is 'utf8'\n     */\n    new (str: string, encoding?: string): Buffer;\n    /**\n     * Allocates a new buffer of {size} octets.\n     *\n     * @param size count of octets to allocate.\n     */\n    new (size: number): Buffer;\n    /**\n     * Allocates a new buffer containing the given {array} of octets.\n     *\n     * @param array The octets to store.\n     */\n    new (array: Uint8Array): Buffer;\n    /**\n     * Produces a Buffer backed by the same allocated memory as\n     * the given {ArrayBuffer}.\n     *\n     *\n     * @param arrayBuffer The ArrayBuffer with which to share memory.\n     */\n    new (arrayBuffer: ArrayBuffer): Buffer;\n    /**\n     * Allocates a new buffer containing the given {array} of octets.\n     *\n     * @param array The octets to store.\n     */\n    new (array: any[]): Buffer;\n    /**\n     * Copies the passed {buffer} data onto a new {Buffer} instance.\n     *\n     * @param buffer The buffer to copy.\n     */\n    new (buffer: Buffer): Buffer;\n    prototype: Buffer;\n    /**\n     * Allocates a new Buffer using an {array} of octets.\n     *\n     * @param array\n     */\n    from(array: any[]): Buffer;\n    /**\n     * When passed a reference to the .buffer property of a TypedArray instance,\n     * the newly created Buffer will share the same allocated memory as the TypedArray.\n     * The optional {byteOffset} and {length} arguments specify a memory range\n     * within the {arrayBuffer} that will be shared by the Buffer.\n     *\n     * @param arrayBuffer The .buffer property of a TypedArray or a new ArrayBuffer()\n     * @param byteOffset\n     * @param length\n     */\n    from(arrayBuffer: ArrayBuffer, byteOffset?: number, length?: number): Buffer;\n    /**\n     * Copies the passed {buffer} data onto a new Buffer instance.\n     *\n     * @param buffer\n     */\n    from(buffer: Buffer): Buffer;\n    /**\n     * Creates a new Buffer containing the given JavaScript string {str}.\n     * If provided, the {encoding} parameter identifies the character encoding.\n     * If not provided, {encoding} defaults to 'utf8'.\n     *\n     * @param str\n     */\n    from(str: string, encoding?: string): Buffer;\n    /**\n     * Returns true if {obj} is a Buffer\n     *\n     * @param obj object to test.\n     */\n    isBuffer(obj: any): obj is Buffer;\n    /**\n     * Returns true if {encoding} is a valid encoding argument.\n     * Valid string encodings in Node 0.12: 'ascii'|'utf8'|'utf16le'|'ucs2'(alias of 'utf16le')|'base64'|'binary'(deprecated)|'hex'\n     *\n     * @param encoding string to test.\n     */\n    isEncoding(encoding: string): boolean;\n    /**\n     * Gives the actual byte length of a string. encoding defaults to 'utf8'.\n     * This is not the same as String.prototype.length since that returns the number of characters in a string.\n     *\n     * @param string string to test.\n     * @param encoding encoding used to evaluate (defaults to 'utf8')\n     */\n    byteLength(string: string, encoding?: string): number;\n    /**\n     * Returns a buffer which is the result of concatenating all the buffers in the list together.\n     *\n     * If the list has no items, or if the totalLength is 0, then it returns a zero-length buffer.\n     * If the list has exactly one item, then the first item of the list is returned.\n     * If the list has more than one item, then a new Buffer is created.\n     *\n     * @param list An array of Buffer objects to concatenate\n     * @param totalLength Total length of the buffers when concatenated.\n     *   If totalLength is not provided, it is read from the buffers in the list. However, this adds an additional loop to the function, so it is faster to provide the length explicitly.\n     */\n    concat(list: Buffer[], totalLength?: number): Buffer;\n    /**\n     * The same as buf1.compare(buf2).\n     */\n    compare(buf1: Buffer, buf2: Buffer): number;\n    /**\n     * Allocates a new buffer of {size} octets.\n     *\n     * @param size count of octets to allocate.\n     * @param fill if specified, buffer will be initialized by calling buf.fill(fill).\n     *    If parameter is omitted, buffer will be filled with zeros.\n     * @param encoding encoding used for call to buf.fill while initalizing\n     */\n    alloc(size: number, fill?: string | Buffer | number, encoding?: string): Buffer;\n    /**\n     * Allocates a new buffer of {size} octets, leaving memory not initialized, so the contents\n     * of the newly created Buffer are unknown and may contain sensitive data.\n     *\n     * @param size count of octets to allocate\n     */\n    allocUnsafe(size: number): Buffer;\n    /**\n     * Allocates a new non-pooled buffer of {size} octets, leaving memory not initialized, so the contents\n     * of the newly created Buffer are unknown and may contain sensitive data.\n     *\n     * @param size count of octets to allocate\n     */\n    allocUnsafeSlow(size: number): Buffer;\n};\n\n/************************************************\n*                                               *\n*               GLOBAL INTERFACES               *\n*                                               *\n************************************************/\ndeclare namespace NodeJS {\n    export interface ErrnoException extends Error {\n        errno?: number;\n        code?: string;\n        path?: string;\n        syscall?: string;\n        stack?: string;\n    }\n\n    export interface EventEmitter {\n        addListener(event: string, listener: Function): this;\n        on(event: string, listener: Function): this;\n        once(event: string, listener: Function): this;\n        removeListener(event: string, listener: Function): this;\n        removeAllListeners(event?: string): this;\n        setMaxListeners(n: number): this;\n        getMaxListeners(): number;\n        listeners(event: string): Function[];\n        emit(event: string, ...args: any[]): boolean;\n        listenerCount(type: string): number;\n    }\n\n    export interface ReadableStream extends EventEmitter {\n        readable: boolean;\n        read(size?: number): string | Buffer;\n        setEncoding(encoding: string): void;\n        pause(): void;\n        resume(): void;\n        pipe<T extends WritableStream>(destination: T, options?: { end?: boolean; }): T;\n        unpipe<T extends WritableStream>(destination?: T): void;\n        unshift(chunk: string): void;\n        unshift(chunk: Buffer): void;\n        wrap(oldStream: ReadableStream): ReadableStream;\n    }\n\n    export interface WritableStream extends EventEmitter {\n        writable: boolean;\n        write(buffer: Buffer | string, cb?: Function): boolean;\n        write(str: string, encoding?: string, cb?: Function): boolean;\n        end(): void;\n        end(buffer: Buffer, cb?: Function): void;\n        end(str: string, cb?: Function): void;\n        end(str: string, encoding?: string, cb?: Function): void;\n    }\n\n    export interface ReadWriteStream extends ReadableStream, WritableStream { }\n\n    export interface Events extends EventEmitter { }\n\n    export interface Domain extends Events {\n        run(fn: Function): void;\n        add(emitter: Events): void;\n        remove(emitter: Events): void;\n        bind(cb: (err: Error, data: any) => any): any;\n        intercept(cb: (data: any) => any): any;\n        dispose(): void;\n\n        addListener(event: string, listener: Function): this;\n        on(event: string, listener: Function): this;\n        once(event: string, listener: Function): this;\n        removeListener(event: string, listener: Function): this;\n        removeAllListeners(event?: string): this;\n    }\n\n    export interface MemoryUsage {\n        rss: number;\n        heapTotal: number;\n        heapUsed: number;\n    }\n\n    export interface ProcessVersions {\n        http_parser: string;\n        node: string;\n        v8: string;\n        ares: string;\n        uv: string;\n        zlib: string;\n        modules: string;\n        openssl: string;\n    }\n\n    export interface Process extends EventEmitter {\n        stdout: WritableStream;\n        stderr: WritableStream;\n        stdin: ReadableStream;\n        argv: string[];\n        execArgv: string[];\n        execPath: string;\n        abort(): void;\n        chdir(directory: string): void;\n        cwd(): string;\n        env: any;\n        exit(code?: number): void;\n        exitCode: number;\n        getgid(): number;\n        setgid(id: number): void;\n        setgid(id: string): void;\n        getuid(): number;\n        setuid(id: number): void;\n        setuid(id: string): void;\n        version: string;\n        versions: ProcessVersions;\n        config: {\n            target_defaults: {\n                cflags: any[];\n                default_configuration: string;\n                defines: string[];\n                include_dirs: string[];\n                libraries: string[];\n            };\n            variables: {\n                clang: number;\n                host_arch: string;\n                node_install_npm: boolean;\n                node_install_waf: boolean;\n                node_prefix: string;\n                node_shared_openssl: boolean;\n                node_shared_v8: boolean;\n                node_shared_zlib: boolean;\n                node_use_dtrace: boolean;\n                node_use_etw: boolean;\n                node_use_openssl: boolean;\n                target_arch: string;\n                v8_no_strict_aliasing: number;\n                v8_use_snapshot: boolean;\n                visibility: string;\n            };\n        };\n        kill(pid: number, signal?: string | number): void;\n        pid: number;\n        title: string;\n        arch: string;\n        platform: string;\n        memoryUsage(): MemoryUsage;\n        nextTick(callback: Function): void;\n        umask(mask?: number): number;\n        uptime(): number;\n        hrtime(time?: number[]): number[];\n        domain: Domain;\n\n        // Worker\n        send?(message: any, sendHandle?: any): void;\n        disconnect(): void;\n        connected: boolean;\n    }\n\n    export interface Global {\n        Array: typeof Array;\n        ArrayBuffer: typeof ArrayBuffer;\n        Boolean: typeof Boolean;\n        Buffer: typeof Buffer;\n        DataView: typeof DataView;\n        Date: typeof Date;\n        Error: typeof Error;\n        EvalError: typeof EvalError;\n        Float32Array: typeof Float32Array;\n        Float64Array: typeof Float64Array;\n        Function: typeof Function;\n        GLOBAL: Global;\n        Infinity: typeof Infinity;\n        Int16Array: typeof Int16Array;\n        Int32Array: typeof Int32Array;\n        Int8Array: typeof Int8Array;\n        Intl: typeof Intl;\n        JSON: typeof JSON;\n        Map: MapConstructor;\n        Math: typeof Math;\n        NaN: typeof NaN;\n        Number: typeof Number;\n        Object: typeof Object;\n        Promise: Function;\n        RangeError: typeof RangeError;\n        ReferenceError: typeof ReferenceError;\n        RegExp: typeof RegExp;\n        Set: SetConstructor;\n        String: typeof String;\n        Symbol: Function;\n        SyntaxError: typeof SyntaxError;\n        TypeError: typeof TypeError;\n        URIError: typeof URIError;\n        Uint16Array: typeof Uint16Array;\n        Uint32Array: typeof Uint32Array;\n        Uint8Array: typeof Uint8Array;\n        Uint8ClampedArray: Function;\n        WeakMap: WeakMapConstructor;\n        WeakSet: WeakSetConstructor;\n        clearImmediate: (immediateId: any) => void;\n        clearInterval: (intervalId: NodeJS.Timer) => void;\n        clearTimeout: (timeoutId: NodeJS.Timer) => void;\n        console: typeof console;\n        decodeURI: typeof decodeURI;\n        decodeURIComponent: typeof decodeURIComponent;\n        encodeURI: typeof encodeURI;\n        encodeURIComponent: typeof encodeURIComponent;\n        escape: (str: string) => string;\n        eval: typeof eval;\n        global: Global;\n        isFinite: typeof isFinite;\n        isNaN: typeof isNaN;\n        parseFloat: typeof parseFloat;\n        parseInt: typeof parseInt;\n        process: Process;\n        root: Global;\n        setImmediate: (callback: (...args: any[]) => void, ...args: any[]) => any;\n        setInterval: (callback: (...args: any[]) => void, ms: number, ...args: any[]) => NodeJS.Timer;\n        setTimeout: (callback: (...args: any[]) => void, ms: number, ...args: any[]) => NodeJS.Timer;\n        undefined: typeof undefined;\n        unescape: (str: string) => string;\n        gc: () => void;\n        v8debug?: any;\n    }\n\n    export interface Timer {\n        ref(): void;\n        unref(): void;\n    }\n}\n\n/**\n * @deprecated\n */\ninterface NodeBuffer extends Uint8Array {\n    write(string: string, offset?: number, length?: number, encoding?: string): number;\n    toString(encoding?: string, start?: number, end?: number): string;\n    toJSON(): any;\n    equals(otherBuffer: Buffer): boolean;\n    compare(otherBuffer: Buffer): number;\n    copy(targetBuffer: Buffer, targetStart?: number, sourceStart?: number, sourceEnd?: number): number;\n    slice(start?: number, end?: number): Buffer;\n    writeUIntLE(value: number, offset: number, byteLength: number, noAssert?: boolean): number;\n    writeUIntBE(value: number, offset: number, byteLength: number, noAssert?: boolean): number;\n    writeIntLE(value: number, offset: number, byteLength: number, noAssert?: boolean): number;\n    writeIntBE(value: number, offset: number, byteLength: number, noAssert?: boolean): number;\n    readUIntLE(offset: number, byteLength: number, noAssert?: boolean): number;\n    readUIntBE(offset: number, byteLength: number, noAssert?: boolean): number;\n    readIntLE(offset: number, byteLength: number, noAssert?: boolean): number;\n    readIntBE(offset: number, byteLength: number, noAssert?: boolean): number;\n    readUInt8(offset: number, noAssert?: boolean): number;\n    readUInt16LE(offset: number, noAssert?: boolean): number;\n    readUInt16BE(offset: number, noAssert?: boolean): number;\n    readUInt32LE(offset: number, noAssert?: boolean): number;\n    readUInt32BE(offset: number, noAssert?: boolean): number;\n    readInt8(offset: number, noAssert?: boolean): number;\n    readInt16LE(offset: number, noAssert?: boolean): number;\n    readInt16BE(offset: number, noAssert?: boolean): number;\n    readInt32LE(offset: number, noAssert?: boolean): number;\n    readInt32BE(offset: number, noAssert?: boolean): number;\n    readFloatLE(offset: number, noAssert?: boolean): number;\n    readFloatBE(offset: number, noAssert?: boolean): number;\n    readDoubleLE(offset: number, noAssert?: boolean): number;\n    readDoubleBE(offset: number, noAssert?: boolean): number;\n    writeUInt8(value: number, offset: number, noAssert?: boolean): number;\n    writeUInt16LE(value: number, offset: number, noAssert?: boolean): number;\n    writeUInt16BE(value: number, offset: number, noAssert?: boolean): number;\n    writeUInt32LE(value: number, offset: number, noAssert?: boolean): number;\n    writeUInt32BE(value: number, offset: number, noAssert?: boolean): number;\n    writeInt8(value: number, offset: number, noAssert?: boolean): number;\n    writeInt16LE(value: number, offset: number, noAssert?: boolean): number;\n    writeInt16BE(value: number, offset: number, noAssert?: boolean): number;\n    writeInt32LE(value: number, offset: number, noAssert?: boolean): number;\n    writeInt32BE(value: number, offset: number, noAssert?: boolean): number;\n    writeFloatLE(value: number, offset: number, noAssert?: boolean): number;\n    writeFloatBE(value: number, offset: number, noAssert?: boolean): number;\n    writeDoubleLE(value: number, offset: number, noAssert?: boolean): number;\n    writeDoubleBE(value: number, offset: number, noAssert?: boolean): number;\n    fill(value: any, offset?: number, end?: number): this;\n    // TODO: encoding param\n    indexOf(value: string | number | Buffer, byteOffset?: number): number;\n    // TODO: entries\n    // TODO: includes\n    // TODO: keys\n    // TODO: values\n}\n\n/************************************************\n*                                               *\n*                   MODULES                     *\n*                                               *\n************************************************/\ndeclare module \"buffer\" {\n    export var INSPECT_MAX_BYTES: number;\n    var BuffType: typeof Buffer;\n    var SlowBuffType: typeof SlowBuffer;\n    export { BuffType as Buffer, SlowBuffType as SlowBuffer };\n}\n\ndeclare module \"querystring\" {\n    export interface StringifyOptions {\n        encodeURIComponent?: Function;\n    }\n\n    export interface ParseOptions {\n        maxKeys?: number;\n        decodeURIComponent?: Function;\n    }\n\n    export function stringify<T>(obj: T, sep?: string, eq?: string, options?: StringifyOptions): string;\n    export function parse(str: string, sep?: string, eq?: string, options?: ParseOptions): any;\n    export function parse<T extends {}>(str: string, sep?: string, eq?: string, options?: ParseOptions): T;\n    export function escape(str: string): string;\n    export function unescape(str: string): string;\n}\n\ndeclare module \"events\" {\n    export class EventEmitter implements NodeJS.EventEmitter {\n        static EventEmitter: EventEmitter;\n        static listenerCount(emitter: EventEmitter, event: string): number; // deprecated\n        static defaultMaxListeners: number;\n\n        addListener(event: string, listener: Function): this;\n        on(event: string, listener: Function): this;\n        once(event: string, listener: Function): this;\n        prependListener(event: string, listener: Function): this;\n        prependOnceListener(event: string, listener: Function): this;\n        removeListener(event: string, listener: Function): this;\n        removeAllListeners(event?: string): this;\n        setMaxListeners(n: number): this;\n        getMaxListeners(): number;\n        listeners(event: string): Function[];\n        emit(event: string, ...args: any[]): boolean;\n        eventNames(): string[];\n        listenerCount(type: string): number;\n    }\n}\n\ndeclare module \"http\" {\n    import * as events from \"events\";\n    import * as net from \"net\";\n    import * as stream from \"stream\";\n\n    export interface RequestOptions {\n        protocol?: string;\n        host?: string;\n        hostname?: string;\n        family?: number;\n        port?: number;\n        localAddress?: string;\n        socketPath?: string;\n        method?: string;\n        path?: string;\n        headers?: { [key: string]: any };\n        auth?: string;\n        agent?: Agent | boolean;\n    }\n\n    export interface Server extends events.EventEmitter, net.Server {\n        setTimeout(msecs: number, callback: Function): void;\n        maxHeadersCount: number;\n        timeout: number;\n    }\n    /**\n     * @deprecated Use IncomingMessage\n     */\n    export interface ServerRequest extends IncomingMessage {\n        connection: net.Socket;\n    }\n    export interface ServerResponse extends events.EventEmitter, stream.Writable {\n        // Extended base methods\n        write(buffer: Buffer): boolean;\n        write(buffer: Buffer, cb?: Function): boolean;\n        write(str: string, cb?: Function): boolean;\n        write(str: string, encoding?: string, cb?: Function): boolean;\n        write(str: string, encoding?: string, fd?: string): boolean;\n\n        writeContinue(): void;\n        writeHead(statusCode: number, reasonPhrase?: string, headers?: any): void;\n        writeHead(statusCode: number, headers?: any): void;\n        statusCode: number;\n        statusMessage: string;\n        headersSent: boolean;\n        setHeader(name: string, value: string | string[]): void;\n        setTimeout(msecs: number, callback: Function): ServerResponse;\n        sendDate: boolean;\n        getHeader(name: string): string;\n        removeHeader(name: string): void;\n        write(chunk: any, encoding?: string): any;\n        addTrailers(headers: any): void;\n        finished: boolean;\n\n        // Extended base methods\n        end(): void;\n        end(buffer: Buffer, cb?: Function): void;\n        end(str: string, cb?: Function): void;\n        end(str: string, encoding?: string, cb?: Function): void;\n        end(data?: any, encoding?: string): void;\n    }\n    export interface ClientRequest extends events.EventEmitter, stream.Writable {\n        // Extended base methods\n        write(buffer: Buffer): boolean;\n        write(buffer: Buffer, cb?: Function): boolean;\n        write(str: string, cb?: Function): boolean;\n        write(str: string, encoding?: string, cb?: Function): boolean;\n        write(str: string, encoding?: string, fd?: string): boolean;\n\n        write(chunk: any, encoding?: string): void;\n        abort(): void;\n        setTimeout(timeout: number, callback?: Function): void;\n        setNoDelay(noDelay?: boolean): void;\n        setSocketKeepAlive(enable?: boolean, initialDelay?: number): void;\n\n        setHeader(name: string, value: string | string[]): void;\n        getHeader(name: string): string;\n        removeHeader(name: string): void;\n        addTrailers(headers: any): void;\n\n        // Extended base methods\n        end(): void;\n        end(buffer: Buffer, cb?: Function): void;\n        end(str: string, cb?: Function): void;\n        end(str: string, encoding?: string, cb?: Function): void;\n        end(data?: any, encoding?: string): void;\n    }\n    export interface IncomingMessage extends events.EventEmitter, stream.Readable {\n        httpVersion: string;\n        headers: any;\n        rawHeaders: string[];\n        trailers: any;\n        rawTrailers: any;\n        setTimeout(msecs: number, callback: Function): NodeJS.Timer;\n        /**\n         * Only valid for request obtained from http.Server.\n         */\n        method?: string;\n        /**\n         * Only valid for request obtained from http.Server.\n         */\n        url?: string;\n        /**\n         * Only valid for response obtained from http.ClientRequest.\n         */\n        statusCode?: number;\n        /**\n         * Only valid for response obtained from http.ClientRequest.\n         */\n        statusMessage?: string;\n        socket: net.Socket;\n    }\n    /**\n     * @deprecated Use IncomingMessage\n     */\n    export interface ClientResponse extends IncomingMessage { }\n\n    export interface AgentOptions {\n        /**\n         * Keep sockets around in a pool to be used by other requests in the future. Default = false\n         */\n        keepAlive?: boolean;\n        /**\n         * When using HTTP KeepAlive, how often to send TCP KeepAlive packets over sockets being kept alive. Default = 1000.\n         * Only relevant if keepAlive is set to true.\n         */\n        keepAliveMsecs?: number;\n        /**\n         * Maximum number of sockets to allow per host. Default for Node 0.10 is 5, default for Node 0.12 is Infinity\n         */\n        maxSockets?: number;\n        /**\n         * Maximum number of sockets to leave open in a free state. Only relevant if keepAlive is set to true. Default = 256.\n         */\n        maxFreeSockets?: number;\n    }\n\n    export class Agent {\n        maxSockets: number;\n        sockets: any;\n        requests: any;\n\n        constructor(opts?: AgentOptions);\n\n        /**\n         * Destroy any sockets that are currently in use by the agent.\n         * It is usually not necessary to do this. However, if you are using an agent with KeepAlive enabled,\n         * then it is best to explicitly shut down the agent when you know that it will no longer be used. Otherwise,\n         * sockets may hang open for quite a long time before the server terminates them.\n         */\n        destroy(): void;\n    }\n\n    export var METHODS: string[];\n\n    export var STATUS_CODES: {\n        [errorCode: number]: string;\n        [errorCode: string]: string;\n    };\n    export function createServer(requestListener?: (request: IncomingMessage, response: ServerResponse) => void): Server;\n    export function createClient(port?: number, host?: string): any;\n    export function request(options: RequestOptions, callback?: (res: IncomingMessage) => void): ClientRequest;\n    export function get(options: any, callback?: (res: IncomingMessage) => void): ClientRequest;\n    export var globalAgent: Agent;\n}\n\ndeclare module \"cluster\" {\n    import * as child from \"child_process\";\n    import * as events from \"events\";\n\n    export interface ClusterSettings {\n        exec?: string;\n        args?: string[];\n        silent?: boolean;\n    }\n\n    export interface Address {\n        address: string;\n        port: number;\n        addressType: string;\n    }\n\n    export class Worker extends events.EventEmitter {\n        id: string;\n        process: child.ChildProcess;\n        suicide: boolean;\n        send(message: any, sendHandle?: any): void;\n        kill(signal?: string): void;\n        destroy(signal?: string): void;\n        disconnect(): void;\n        isConnected(): boolean;\n        isDead(): boolean;\n    }\n\n    export var settings: ClusterSettings;\n    export var isMaster: boolean;\n    export var isWorker: boolean;\n    export function setupMaster(settings?: ClusterSettings): void;\n    export function fork(env?: any): Worker;\n    export function disconnect(callback?: Function): void;\n    export var worker: Worker;\n    export var workers: {\n        [index: string]: Worker\n    };\n\n    // Event emitter\n    export function addListener(event: string, listener: Function): void;\n    export function on(event: \"disconnect\", listener: (worker: Worker) => void): void;\n    export function on(event: \"exit\", listener: (worker: Worker, code: number, signal: string) => void): void;\n    export function on(event: \"fork\", listener: (worker: Worker) => void): void;\n    export function on(event: \"listening\", listener: (worker: Worker, address: any) => void): void;\n    export function on(event: \"message\", listener: (worker: Worker, message: any) => void): void;\n    export function on(event: \"online\", listener: (worker: Worker) => void): void;\n    export function on(event: \"setup\", listener: (settings: any) => void): void;\n    export function on(event: string, listener: Function): any;\n    export function once(event: string, listener: Function): void;\n    export function removeListener(event: string, listener: Function): void;\n    export function removeAllListeners(event?: string): void;\n    export function setMaxListeners(n: number): void;\n    export function listeners(event: string): Function[];\n    export function emit(event: string, ...args: any[]): boolean;\n}\n\ndeclare module \"zlib\" {\n    import * as stream from \"stream\";\n    export interface ZlibOptions { chunkSize?: number; windowBits?: number; level?: number; memLevel?: number; strategy?: number; dictionary?: any; }\n\n    export interface Gzip extends stream.Transform { }\n    export interface Gunzip extends stream.Transform { }\n    export interface Deflate extends stream.Transform { }\n    export interface Inflate extends stream.Transform { }\n    export interface DeflateRaw extends stream.Transform { }\n    export interface InflateRaw extends stream.Transform { }\n    export interface Unzip extends stream.Transform { }\n\n    export function createGzip(options?: ZlibOptions): Gzip;\n    export function createGunzip(options?: ZlibOptions): Gunzip;\n    export function createDeflate(options?: ZlibOptions): Deflate;\n    export function createInflate(options?: ZlibOptions): Inflate;\n    export function createDeflateRaw(options?: ZlibOptions): DeflateRaw;\n    export function createInflateRaw(options?: ZlibOptions): InflateRaw;\n    export function createUnzip(options?: ZlibOptions): Unzip;\n\n    export function deflate(buf: Buffer, callback: (error: Error, result: any) => void): void;\n    export function deflateSync(buf: Buffer, options?: ZlibOptions): any;\n    export function deflateRaw(buf: Buffer, callback: (error: Error, result: any) => void): void;\n    export function deflateRawSync(buf: Buffer, options?: ZlibOptions): any;\n    export function gzip(buf: Buffer, callback: (error: Error, result: any) => void): void;\n    export function gzipSync(buf: Buffer, options?: ZlibOptions): any;\n    export function gunzip(buf: Buffer, callback: (error: Error, result: any) => void): void;\n    export function gunzipSync(buf: Buffer, options?: ZlibOptions): any;\n    export function inflate(buf: Buffer, callback: (error: Error, result: any) => void): void;\n    export function inflateSync(buf: Buffer, options?: ZlibOptions): any;\n    export function inflateRaw(buf: Buffer, callback: (error: Error, result: any) => void): void;\n    export function inflateRawSync(buf: Buffer, options?: ZlibOptions): any;\n    export function unzip(buf: Buffer, callback: (error: Error, result: any) => void): void;\n    export function unzipSync(buf: Buffer, options?: ZlibOptions): any;\n\n    // Constants\n    export var Z_NO_FLUSH: number;\n    export var Z_PARTIAL_FLUSH: number;\n    export var Z_SYNC_FLUSH: number;\n    export var Z_FULL_FLUSH: number;\n    export var Z_FINISH: number;\n    export var Z_BLOCK: number;\n    export var Z_TREES: number;\n    export var Z_OK: number;\n    export var Z_STREAM_END: number;\n    export var Z_NEED_DICT: number;\n    export var Z_ERRNO: number;\n    export var Z_STREAM_ERROR: number;\n    export var Z_DATA_ERROR: number;\n    export var Z_MEM_ERROR: number;\n    export var Z_BUF_ERROR: number;\n    export var Z_VERSION_ERROR: number;\n    export var Z_NO_COMPRESSION: number;\n    export var Z_BEST_SPEED: number;\n    export var Z_BEST_COMPRESSION: number;\n    export var Z_DEFAULT_COMPRESSION: number;\n    export var Z_FILTERED: number;\n    export var Z_HUFFMAN_ONLY: number;\n    export var Z_RLE: number;\n    export var Z_FIXED: number;\n    export var Z_DEFAULT_STRATEGY: number;\n    export var Z_BINARY: number;\n    export var Z_TEXT: number;\n    export var Z_ASCII: number;\n    export var Z_UNKNOWN: number;\n    export var Z_DEFLATED: number;\n    export var Z_NULL: number;\n}\n\ndeclare module \"os\" {\n    export interface CpuInfo {\n        model: string;\n        speed: number;\n        times: {\n            user: number;\n            nice: number;\n            sys: number;\n            idle: number;\n            irq: number;\n        };\n    }\n\n    export interface NetworkInterfaceInfo {\n        address: string;\n        netmask: string;\n        family: string;\n        mac: string;\n        internal: boolean;\n    }\n\n    export function tmpdir(): string;\n    export function homedir(): string;\n    export function endianness(): \"BE\" | \"LE\";\n    export function hostname(): string;\n    export function type(): string;\n    export function platform(): string;\n    export function arch(): string;\n    export function release(): string;\n    export function uptime(): number;\n    export function loadavg(): number[];\n    export function totalmem(): number;\n    export function freemem(): number;\n    export function cpus(): CpuInfo[];\n    export function networkInterfaces(): { [index: string]: NetworkInterfaceInfo[] };\n    export var EOL: string;\n}\n\ndeclare module \"https\" {\n    import * as tls from \"tls\";\n    import * as events from \"events\";\n    import * as http from \"http\";\n\n    export interface ServerOptions {\n        pfx?: any;\n        key?: any;\n        passphrase?: string;\n        cert?: any;\n        ca?: any;\n        crl?: any;\n        ciphers?: string;\n        honorCipherOrder?: boolean;\n        requestCert?: boolean;\n        rejectUnauthorized?: boolean;\n        NPNProtocols?: any;\n        SNICallback?: (servername: string, cb:(err:Error,ctx:tls.SecureContext)=>any) => any;\n    }\n\n    export interface RequestOptions extends http.RequestOptions {\n        pfx?: any;\n        key?: any;\n        passphrase?: string;\n        cert?: any;\n        ca?: any;\n        ciphers?: string;\n        rejectUnauthorized?: boolean;\n        secureProtocol?: string;\n    }\n\n    export interface Agent extends http.Agent { }\n\n    export interface AgentOptions extends http.AgentOptions {\n        pfx?: any;\n        key?: any;\n        passphrase?: string;\n        cert?: any;\n        ca?: any;\n        ciphers?: string;\n        rejectUnauthorized?: boolean;\n        secureProtocol?: string;\n        maxCachedSessions?: number;\n    }\n\n    export var Agent: {\n        new (options?: AgentOptions): Agent;\n    };\n    export interface Server extends tls.Server { }\n    export function createServer(options: ServerOptions, requestListener?: Function): Server;\n    export function request(options: RequestOptions, callback?: (res: http.IncomingMessage) => void): http.ClientRequest;\n    export function get(options: RequestOptions, callback?: (res: http.IncomingMessage) => void): http.ClientRequest;\n    export var globalAgent: Agent;\n}\n\ndeclare module \"punycode\" {\n    export function decode(string: string): string;\n    export function encode(string: string): string;\n    export function toUnicode(domain: string): string;\n    export function toASCII(domain: string): string;\n    export var ucs2: ucs2;\n    interface ucs2 {\n        decode(string: string): number[];\n        encode(codePoints: number[]): string;\n    }\n    export var version: any;\n}\n\ndeclare module \"repl\" {\n    import * as stream from \"stream\";\n    import * as events from \"events\";\n\n    export interface ReplOptions {\n        prompt?: string;\n        input?: NodeJS.ReadableStream;\n        output?: NodeJS.WritableStream;\n        terminal?: boolean;\n        eval?: Function;\n        useColors?: boolean;\n        useGlobal?: boolean;\n        ignoreUndefined?: boolean;\n        writer?: Function;\n    }\n    export function start(options: ReplOptions): events.EventEmitter;\n}\n\ndeclare module \"readline\" {\n    import * as events from \"events\";\n    import * as stream from \"stream\";\n\n    export interface Key {\n        sequence?: string;\n        name?: string;\n        ctrl?: boolean;\n        meta?: boolean;\n        shift?: boolean;\n    }\n\n    export interface ReadLine extends events.EventEmitter {\n        setPrompt(prompt: string): void;\n        prompt(preserveCursor?: boolean): void;\n        question(query: string, callback: (answer: string) => void): void;\n        pause(): ReadLine;\n        resume(): ReadLine;\n        close(): void;\n        write(data: string | Buffer, key?: Key): void;\n    }\n\n    export interface Completer {\n        (line: string): CompleterResult;\n        (line: string, callback: (err: any, result: CompleterResult) => void): any;\n    }\n\n    export interface CompleterResult {\n        completions: string[];\n        line: string;\n    }\n\n    export interface ReadLineOptions {\n        input: NodeJS.ReadableStream;\n        output?: NodeJS.WritableStream;\n        completer?: Completer;\n        terminal?: boolean;\n        historySize?: number;\n    }\n\n    export function createInterface(input: NodeJS.ReadableStream, output?: NodeJS.WritableStream, completer?: Completer, terminal?: boolean): ReadLine;\n    export function createInterface(options: ReadLineOptions): ReadLine;\n\n    export function cursorTo(stream: NodeJS.WritableStream, x: number, y: number): void;\n    export function moveCursor(stream: NodeJS.WritableStream, dx: number | string, dy: number | string): void;\n    export function clearLine(stream: NodeJS.WritableStream, dir: number): void;\n    export function clearScreenDown(stream: NodeJS.WritableStream): void;\n}\n\ndeclare module \"vm\" {\n    export interface Context { }\n    export interface ScriptOptions {\n        filename?: string;\n        lineOffset?: number;\n        columnOffset?: number;\n        displayErrors?: boolean;\n        timeout?: number;\n        cachedData?: Buffer;\n        produceCachedData?: boolean;\n    }\n    export interface RunningScriptOptions {\n        filename?: string;\n        lineOffset?: number;\n        columnOffset?: number;\n        displayErrors?: boolean;\n        timeout?: number;\n    }\n    export class Script {\n        constructor(code: string, options?: ScriptOptions);\n        runInContext(contextifiedSandbox: Context, options?: RunningScriptOptions): any;\n        runInNewContext(sandbox?: Context, options?: RunningScriptOptions): any;\n        runInThisContext(options?: RunningScriptOptions): any;\n    }\n    export function createContext(sandbox?: Context): Context;\n    export function isContext(sandbox: Context): boolean;\n    export function runInContext(code: string, contextifiedSandbox: Context, options?: RunningScriptOptions): any;\n    export function runInDebugContext(code: string): any;\n    export function runInNewContext(code: string, sandbox?: Context, options?: RunningScriptOptions): any;\n    export function runInThisContext(code: string, options?: RunningScriptOptions): any;\n}\n\ndeclare module \"child_process\" {\n    import * as events from \"events\";\n    import * as stream from \"stream\";\n\n    export interface ChildProcess extends events.EventEmitter {\n        stdin: stream.Writable;\n        stdout: stream.Readable;\n        stderr: stream.Readable;\n        stdio: [stream.Writable, stream.Readable, stream.Readable];\n        pid: number;\n        kill(signal?: string): void;\n        send(message: any, sendHandle?: any): void;\n        connected: boolean;\n        disconnect(): void;\n        unref(): void;\n        ref(): void;\n    }\n\n    export interface SpawnOptions {\n        cwd?: string;\n        env?: any;\n        stdio?: any;\n        detached?: boolean;\n        uid?: number;\n        gid?: number;\n        shell?: boolean | string;\n    }\n    export function spawn(command: string, args?: string[], options?: SpawnOptions): ChildProcess;\n\n    export interface ExecOptions {\n        cwd?: string;\n        env?: any;\n        shell?: string;\n        timeout?: number;\n        maxBuffer?: number;\n        killSignal?: string;\n        uid?: number;\n        gid?: number;\n    }\n    export interface ExecOptionsWithStringEncoding extends ExecOptions {\n        encoding: BufferEncoding;\n    }\n    export interface ExecOptionsWithBufferEncoding extends ExecOptions {\n        encoding: string; // specify `null`.\n    }\n    export function exec(command: string, callback?: (error: Error, stdout: string, stderr: string) => void): ChildProcess;\n    export function exec(command: string, options: ExecOptionsWithStringEncoding, callback?: (error: Error, stdout: string, stderr: string) => void): ChildProcess;\n    // usage. child_process.exec(\"tsc\", {encoding: null as string}, (err, stdout, stderr) => {});\n    export function exec(command: string, options: ExecOptionsWithBufferEncoding, callback?: (error: Error, stdout: Buffer, stderr: Buffer) => void): ChildProcess;\n    export function exec(command: string, options: ExecOptions, callback?: (error: Error, stdout: string, stderr: string) => void): ChildProcess;\n\n    export interface ExecFileOptions {\n        cwd?: string;\n        env?: any;\n        timeout?: number;\n        maxBuffer?: number;\n        killSignal?: string;\n        uid?: number;\n        gid?: number;\n    }\n    export interface ExecFileOptionsWithStringEncoding extends ExecFileOptions {\n        encoding: BufferEncoding;\n    }\n    export interface ExecFileOptionsWithBufferEncoding extends ExecFileOptions {\n        encoding: string; // specify `null`.\n    }\n    export function execFile(file: string, callback?: (error: Error, stdout: string, stderr: string) => void): ChildProcess;\n    export function execFile(file: string, options?: ExecFileOptionsWithStringEncoding, callback?: (error: Error, stdout: string, stderr: string) => void): ChildProcess;\n    // usage. child_process.execFile(\"file.sh\", {encoding: null as string}, (err, stdout, stderr) => {});\n    export function execFile(file: string, options?: ExecFileOptionsWithBufferEncoding, callback?: (error: Error, stdout: Buffer, stderr: Buffer) => void): ChildProcess;\n    export function execFile(file: string, options?: ExecFileOptions, callback?: (error: Error, stdout: string, stderr: string) => void): ChildProcess;\n    export function execFile(file: string, args?: string[], callback?: (error: Error, stdout: string, stderr: string) => void): ChildProcess;\n    export function execFile(file: string, args?: string[], options?: ExecFileOptionsWithStringEncoding, callback?: (error: Error, stdout: string, stderr: string) => void): ChildProcess;\n    // usage. child_process.execFile(\"file.sh\", [\"foo\"], {encoding: null as string}, (err, stdout, stderr) => {});\n    export function execFile(file: string, args?: string[], options?: ExecFileOptionsWithBufferEncoding, callback?: (error: Error, stdout: Buffer, stderr: Buffer) => void): ChildProcess;\n    export function execFile(file: string, args?: string[], options?: ExecFileOptions, callback?: (error: Error, stdout: string, stderr: string) => void): ChildProcess;\n\n    export interface ForkOptions {\n        cwd?: string;\n        env?: any;\n        execPath?: string;\n        execArgv?: string[];\n        silent?: boolean;\n        uid?: number;\n        gid?: number;\n    }\n    export function fork(modulePath: string, args?: string[], options?: ForkOptions): ChildProcess;\n\n    export interface SpawnSyncOptions {\n        cwd?: string;\n        input?: string | Buffer;\n        stdio?: any;\n        env?: any;\n        uid?: number;\n        gid?: number;\n        timeout?: number;\n        killSignal?: string;\n        maxBuffer?: number;\n        encoding?: string;\n        shell?: boolean | string;\n    }\n    export interface SpawnSyncOptionsWithStringEncoding extends SpawnSyncOptions {\n        encoding: BufferEncoding;\n    }\n    export interface SpawnSyncOptionsWithBufferEncoding extends SpawnSyncOptions {\n        encoding: string; // specify `null`.\n    }\n    export interface SpawnSyncReturns<T> {\n        pid: number;\n        output: string[];\n        stdout: T;\n        stderr: T;\n        status: number;\n        signal: string;\n        error: Error;\n    }\n    export function spawnSync(command: string): SpawnSyncReturns<Buffer>;\n    export function spawnSync(command: string, options?: SpawnSyncOptionsWithStringEncoding): SpawnSyncReturns<string>;\n    export function spawnSync(command: string, options?: SpawnSyncOptionsWithBufferEncoding): SpawnSyncReturns<Buffer>;\n    export function spawnSync(command: string, options?: SpawnSyncOptions): SpawnSyncReturns<Buffer>;\n    export function spawnSync(command: string, args?: string[], options?: SpawnSyncOptionsWithStringEncoding): SpawnSyncReturns<string>;\n    export function spawnSync(command: string, args?: string[], options?: SpawnSyncOptionsWithBufferEncoding): SpawnSyncReturns<Buffer>;\n    export function spawnSync(command: string, args?: string[], options?: SpawnSyncOptions): SpawnSyncReturns<Buffer>;\n\n    export interface ExecSyncOptions {\n        cwd?: string;\n        input?: string | Buffer;\n        stdio?: any;\n        env?: any;\n        shell?: string;\n        uid?: number;\n        gid?: number;\n        timeout?: number;\n        killSignal?: string;\n        maxBuffer?: number;\n        encoding?: string;\n    }\n    export interface ExecSyncOptionsWithStringEncoding extends ExecSyncOptions {\n        encoding: BufferEncoding;\n    }\n    export interface ExecSyncOptionsWithBufferEncoding extends ExecSyncOptions {\n        encoding: string; // specify `null`.\n    }\n    export function execSync(command: string): Buffer;\n    export function execSync(command: string, options?: ExecSyncOptionsWithStringEncoding): string;\n    export function execSync(command: string, options?: ExecSyncOptionsWithBufferEncoding): Buffer;\n    export function execSync(command: string, options?: ExecSyncOptions): Buffer;\n\n    export interface ExecFileSyncOptions {\n        cwd?: string;\n        input?: string | Buffer;\n        stdio?: any;\n        env?: any;\n        uid?: number;\n        gid?: number;\n        timeout?: number;\n        killSignal?: string;\n        maxBuffer?: number;\n        encoding?: string;\n    }\n    export interface ExecFileSyncOptionsWithStringEncoding extends ExecFileSyncOptions {\n        encoding: BufferEncoding;\n    }\n    export interface ExecFileSyncOptionsWithBufferEncoding extends ExecFileSyncOptions {\n        encoding: string; // specify `null`.\n    }\n    export function execFileSync(command: string): Buffer;\n    export function execFileSync(command: string, options?: ExecFileSyncOptionsWithStringEncoding): string;\n    export function execFileSync(command: string, options?: ExecFileSyncOptionsWithBufferEncoding): Buffer;\n    export function execFileSync(command: string, options?: ExecFileSyncOptions): Buffer;\n    export function execFileSync(command: string, args?: string[], options?: ExecFileSyncOptionsWithStringEncoding): string;\n    export function execFileSync(command: string, args?: string[], options?: ExecFileSyncOptionsWithBufferEncoding): Buffer;\n    export function execFileSync(command: string, args?: string[], options?: ExecFileSyncOptions): Buffer;\n}\n\ndeclare module \"url\" {\n    export interface Url {\n        href?: string;\n        protocol?: string;\n        auth?: string;\n        hostname?: string;\n        port?: string;\n        host?: string;\n        pathname?: string;\n        search?: string;\n        query?: string | any;\n        slashes?: boolean;\n        hash?: string;\n        path?: string;\n    }\n\n    export function parse(urlStr: string, parseQueryString?: boolean, slashesDenoteHost?: boolean): Url;\n    export function format(url: Url): string;\n    export function resolve(from: string, to: string): string;\n}\n\ndeclare module \"dns\" {\n    export function lookup(domain: string, family: number, callback: (err: Error, address: string, family: number) => void): string;\n    export function lookup(domain: string, callback: (err: Error, address: string, family: number) => void): string;\n    export function resolve(domain: string, rrtype: string, callback: (err: Error, addresses: string[]) => void): string[];\n    export function resolve(domain: string, callback: (err: Error, addresses: string[]) => void): string[];\n    export function resolve4(domain: string, callback: (err: Error, addresses: string[]) => void): string[];\n    export function resolve6(domain: string, callback: (err: Error, addresses: string[]) => void): string[];\n    export function resolveMx(domain: string, callback: (err: Error, addresses: string[]) => void): string[];\n    export function resolveTxt(domain: string, callback: (err: Error, addresses: string[]) => void): string[];\n    export function resolveSrv(domain: string, callback: (err: Error, addresses: string[]) => void): string[];\n    export function resolveNs(domain: string, callback: (err: Error, addresses: string[]) => void): string[];\n    export function resolveCname(domain: string, callback: (err: Error, addresses: string[]) => void): string[];\n    export function reverse(ip: string, callback: (err: Error, domains: string[]) => void): string[];\n}\n\ndeclare module \"net\" {\n    import * as stream from \"stream\";\n\n    export interface Socket extends stream.Duplex {\n        // Extended base methods\n        write(buffer: Buffer): boolean;\n        write(buffer: Buffer, cb?: Function): boolean;\n        write(str: string, cb?: Function): boolean;\n        write(str: string, encoding?: string, cb?: Function): boolean;\n        write(str: string, encoding?: string, fd?: string): boolean;\n\n        connect(port: number, host?: string, connectionListener?: Function): void;\n        connect(path: string, connectionListener?: Function): void;\n        bufferSize: number;\n        setEncoding(encoding?: string): void;\n        write(data: any, encoding?: string, callback?: Function): void;\n        destroy(): void;\n        pause(): void;\n        resume(): void;\n        setTimeout(timeout: number, callback?: Function): void;\n        setNoDelay(noDelay?: boolean): void;\n        setKeepAlive(enable?: boolean, initialDelay?: number): void;\n        address(): { port: number; family: string; address: string; };\n        unref(): void;\n        ref(): void;\n\n        remoteAddress: string;\n        remoteFamily: string;\n        remotePort: number;\n        localAddress: string;\n        localPort: number;\n        bytesRead: number;\n        bytesWritten: number;\n\n        // Extended base methods\n        end(): void;\n        end(buffer: Buffer, cb?: Function): void;\n        end(str: string, cb?: Function): void;\n        end(str: string, encoding?: string, cb?: Function): void;\n        end(data?: any, encoding?: string): void;\n    }\n\n    export var Socket: {\n        new (options?: { fd?: string; type?: string; allowHalfOpen?: boolean; }): Socket;\n    };\n\n    export interface ListenOptions {\n        port?: number;\n        host?: string;\n        backlog?: number;\n        path?: string;\n        exclusive?: boolean;\n    }\n\n    export interface Server extends Socket {\n        listen(port: number, hostname?: string, backlog?: number, listeningListener?: Function): Server;\n        listen(port: number, hostname?: string, listeningListener?: Function): Server;\n        listen(port: number, backlog?: number, listeningListener?: Function): Server;\n        listen(port: number, listeningListener?: Function): Server;\n        listen(path: string, backlog?: number, listeningListener?: Function): Server;\n        listen(path: string, listeningListener?: Function): Server;\n        listen(handle: any, backlog?: number, listeningListener?: Function): Server;\n        listen(handle: any, listeningListener?: Function): Server;\n        listen(options: ListenOptions, listeningListener?: Function): Server;\n        close(callback?: Function): Server;\n        address(): { port: number; family: string; address: string; };\n        getConnections(cb: (error: Error, count: number) => void): void;\n        ref(): Server;\n        unref(): Server;\n        maxConnections: number;\n        connections: number;\n    }\n    export function createServer(connectionListener?: (socket: Socket) => void): Server;\n    export function createServer(options?: { allowHalfOpen?: boolean; }, connectionListener?: (socket: Socket) => void): Server;\n    export function connect(options: { port: number, host?: string, localAddress?: string, localPort?: string, family?: number, allowHalfOpen?: boolean; }, connectionListener?: Function): Socket;\n    export function connect(port: number, host?: string, connectionListener?: Function): Socket;\n    export function connect(path: string, connectionListener?: Function): Socket;\n    export function createConnection(options: { port: number, host?: string, localAddress?: string, localPort?: string, family?: number, allowHalfOpen?: boolean; }, connectionListener?: Function): Socket;\n    export function createConnection(port: number, host?: string, connectionListener?: Function): Socket;\n    export function createConnection(path: string, connectionListener?: Function): Socket;\n    export function isIP(input: string): number;\n    export function isIPv4(input: string): boolean;\n    export function isIPv6(input: string): boolean;\n}\n\ndeclare module \"dgram\" {\n    import * as events from \"events\";\n\n    interface RemoteInfo {\n        address: string;\n        port: number;\n        size: number;\n    }\n\n    interface AddressInfo {\n        address: string;\n        family: string;\n        port: number;\n    }\n\n    export function createSocket(type: string, callback?: (msg: Buffer, rinfo: RemoteInfo) => void): Socket;\n\n    interface Socket extends events.EventEmitter {\n        send(buf: Buffer, offset: number, length: number, port: number, address: string, callback?: (error: Error, bytes: number) => void): void;\n        bind(port: number, address?: string, callback?: () => void): void;\n        close(): void;\n        address(): AddressInfo;\n        setBroadcast(flag: boolean): void;\n        setMulticastTTL(ttl: number): void;\n        setMulticastLoopback(flag: boolean): void;\n        addMembership(multicastAddress: string, multicastInterface?: string): void;\n        dropMembership(multicastAddress: string, multicastInterface?: string): void;\n    }\n}\n\ndeclare module \"fs\" {\n    import * as stream from \"stream\";\n    import * as events from \"events\";\n\n    interface Stats {\n        isFile(): boolean;\n        isDirectory(): boolean;\n        isBlockDevice(): boolean;\n        isCharacterDevice(): boolean;\n        isSymbolicLink(): boolean;\n        isFIFO(): boolean;\n        isSocket(): boolean;\n        dev: number;\n        ino: number;\n        mode: number;\n        nlink: number;\n        uid: number;\n        gid: number;\n        rdev: number;\n        size: number;\n        blksize: number;\n        blocks: number;\n        atime: Date;\n        mtime: Date;\n        ctime: Date;\n        birthtime: Date;\n    }\n\n    interface FSWatcher extends events.EventEmitter {\n        close(): void;\n    }\n\n    export interface ReadStream extends stream.Readable {\n        close(): void;\n        destroy(): void;\n    }\n    export interface WriteStream extends stream.Writable {\n        close(): void;\n        bytesWritten: number;\n    }\n\n    /**\n     * Asynchronous rename.\n     * @param oldPath\n     * @param newPath\n     * @param callback No arguments other than a possible exception are given to the completion callback.\n     */\n    export function rename(oldPath: string, newPath: string, callback?: (err?: NodeJS.ErrnoException) => void): void;\n    /**\n     * Synchronous rename\n     * @param oldPath\n     * @param newPath\n     */\n    export function renameSync(oldPath: string, newPath: string): void;\n    export function truncate(path: string | Buffer, callback?: (err?: NodeJS.ErrnoException) => void): void;\n    export function truncate(path: string | Buffer, len: number, callback?: (err?: NodeJS.ErrnoException) => void): void;\n    export function truncateSync(path: string | Buffer, len?: number): void;\n    export function ftruncate(fd: number, callback?: (err?: NodeJS.ErrnoException) => void): void;\n    export function ftruncate(fd: number, len: number, callback?: (err?: NodeJS.ErrnoException) => void): void;\n    export function ftruncateSync(fd: number, len?: number): void;\n    export function chown(path: string | Buffer, uid: number, gid: number, callback?: (err?: NodeJS.ErrnoException) => void): void;\n    export function chownSync(path: string | Buffer, uid: number, gid: number): void;\n    export function fchown(fd: number, uid: number, gid: number, callback?: (err?: NodeJS.ErrnoException) => void): void;\n    export function fchownSync(fd: number, uid: number, gid: number): void;\n    export function lchown(path: string | Buffer, uid: number, gid: number, callback?: (err?: NodeJS.ErrnoException) => void): void;\n    export function lchownSync(path: string | Buffer, uid: number, gid: number): void;\n    export function chmod(path: string | Buffer, mode: number, callback?: (err?: NodeJS.ErrnoException) => void): void;\n    export function chmod(path: string | Buffer, mode: string, callback?: (err?: NodeJS.ErrnoException) => void): void;\n    export function chmodSync(path: string | Buffer, mode: number): void;\n    export function chmodSync(path: string | Buffer, mode: string): void;\n    export function fchmod(fd: number, mode: number, callback?: (err?: NodeJS.ErrnoException) => void): void;\n    export function fchmod(fd: number, mode: string, callback?: (err?: NodeJS.ErrnoException) => void): void;\n    export function fchmodSync(fd: number, mode: number): void;\n    export function fchmodSync(fd: number, mode: string): void;\n    export function lchmod(path: string | Buffer, mode: number, callback?: (err?: NodeJS.ErrnoException) => void): void;\n    export function lchmod(path: string | Buffer, mode: string, callback?: (err?: NodeJS.ErrnoException) => void): void;\n    export function lchmodSync(path: string | Buffer, mode: number): void;\n    export function lchmodSync(path: string | Buffer, mode: string): void;\n    export function stat(path: string | Buffer, callback?: (err: NodeJS.ErrnoException, stats: Stats) => any): void;\n    export function lstat(path: string | Buffer, callback?: (err: NodeJS.ErrnoException, stats: Stats) => any): void;\n    export function fstat(fd: number, callback?: (err: NodeJS.ErrnoException, stats: Stats) => any): void;\n    export function statSync(path: string | Buffer): Stats;\n    export function lstatSync(path: string | Buffer): Stats;\n    export function fstatSync(fd: number): Stats;\n    export function link(srcpath: string | Buffer, dstpath: string | Buffer, callback?: (err?: NodeJS.ErrnoException) => void): void;\n    export function linkSync(srcpath: string | Buffer, dstpath: string | Buffer): void;\n    export function symlink(srcpath: string | Buffer, dstpath: string | Buffer, type?: string, callback?: (err?: NodeJS.ErrnoException) => void): void;\n    export function symlinkSync(srcpath: string | Buffer, dstpath: string | Buffer, type?: string): void;\n    export function readlink(path: string | Buffer, callback?: (err: NodeJS.ErrnoException, linkString: string) => any): void;\n    export function readlinkSync(path: string | Buffer): string;\n    export function realpath(path: string | Buffer, callback?: (err: NodeJS.ErrnoException, resolvedPath: string) => any): void;\n    export function realpath(path: string | Buffer, cache: { [path: string]: string }, callback: (err: NodeJS.ErrnoException, resolvedPath: string) => any): void;\n    export function realpathSync(path: string | Buffer, cache?: { [path: string]: string }): string;\n    /*\n     * Asynchronous unlink - deletes the file specified in {path}\n     *\n     * @param path\n     * @param callback No arguments other than a possible exception are given to the completion callback.\n     */\n    export function unlink(path: string | Buffer, callback?: (err?: NodeJS.ErrnoException) => void): void;\n    /*\n     * Synchronous unlink - deletes the file specified in {path}\n     *\n     * @param path\n     */\n    export function unlinkSync(path: string | Buffer): void;\n    /*\n     * Asynchronous rmdir - removes the directory specified in {path}\n     *\n     * @param path\n     * @param callback No arguments other than a possible exception are given to the completion callback.\n     */\n    export function rmdir(path: string | Buffer, callback?: (err?: NodeJS.ErrnoException) => void): void;\n    /*\n     * Synchronous rmdir - removes the directory specified in {path}\n     *\n     * @param path\n     */\n    export function rmdirSync(path: string | Buffer): void;\n    /*\n     * Asynchronous mkdir - creates the directory specified in {path}.  Parameter {mode} defaults to 0777.\n     *\n     * @param path\n     * @param callback No arguments other than a possible exception are given to the completion callback.\n     */\n    export function mkdir(path: string | Buffer, callback?: (err?: NodeJS.ErrnoException) => void): void;\n    /*\n     * Asynchronous mkdir - creates the directory specified in {path}.  Parameter {mode} defaults to 0777.\n     *\n     * @param path\n     * @param mode\n     * @param callback No arguments other than a possible exception are given to the completion callback.\n     */\n    export function mkdir(path: string | Buffer, mode: number, callback?: (err?: NodeJS.ErrnoException) => void): void;\n    /*\n     * Asynchronous mkdir - creates the directory specified in {path}.  Parameter {mode} defaults to 0777.\n     *\n     * @param path\n     * @param mode\n     * @param callback No arguments other than a possible exception are given to the completion callback.\n     */\n    export function mkdir(path: string | Buffer, mode: string, callback?: (err?: NodeJS.ErrnoException) => void): void;\n    /*\n     * Synchronous mkdir - creates the directory specified in {path}.  Parameter {mode} defaults to 0777.\n     *\n     * @param path\n     * @param mode\n     * @param callback No arguments other than a possible exception are given to the completion callback.\n     */\n    export function mkdirSync(path: string | Buffer, mode?: number): void;\n    /*\n     * Synchronous mkdir - creates the directory specified in {path}.  Parameter {mode} defaults to 0777.\n     *\n     * @param path\n     * @param mode\n     * @param callback No arguments other than a possible exception are given to the completion callback.\n     */\n    export function mkdirSync(path: string | Buffer, mode?: string): void;\n    /*\n     * Asynchronous mkdtemp - Creates a unique temporary directory. Generates six random characters to be appended behind a required prefix to create a unique temporary directory.\n     *\n     * @param prefix\n     * @param callback The created folder path is passed as a string to the callback's second parameter.\n     */\n    export function mkdtemp(prefix: string, callback?: (err: NodeJS.ErrnoException, folder: string) => void): void;\n    /*\n     * Synchronous mkdtemp - Creates a unique temporary directory. Generates six random characters to be appended behind a required prefix to create a unique temporary directory.\n     *\n     * @param prefix\n     * @returns Returns the created folder path.\n     */\n    export function mkdtempSync(prefix: string): string;\n    export function readdir(path: string | Buffer, callback?: (err: NodeJS.ErrnoException, files: string[]) => void): void;\n    export function readdirSync(path: string | Buffer): string[];\n    export function close(fd: number, callback?: (err?: NodeJS.ErrnoException) => void): void;\n    export function closeSync(fd: number): void;\n    export function open(path: string | Buffer, flags: string | number, callback: (err: NodeJS.ErrnoException, fd: number) => void): void;\n    export function open(path: string | Buffer, flags: string | number, mode: number, callback: (err: NodeJS.ErrnoException, fd: number) => void): void;\n    export function openSync(path: string | Buffer, flags: string | number, mode?: number): number;\n    export function utimes(path: string | Buffer, atime: number, mtime: number, callback?: (err?: NodeJS.ErrnoException) => void): void;\n    export function utimes(path: string | Buffer, atime: Date, mtime: Date, callback?: (err?: NodeJS.ErrnoException) => void): void;\n    export function utimesSync(path: string | Buffer, atime: number, mtime: number): void;\n    export function utimesSync(path: string | Buffer, atime: Date, mtime: Date): void;\n    export function futimes(fd: number, atime: number, mtime: number, callback?: (err?: NodeJS.ErrnoException) => void): void;\n    export function futimes(fd: number, atime: Date, mtime: Date, callback?: (err?: NodeJS.ErrnoException) => void): void;\n    export function futimesSync(fd: number, atime: number, mtime: number): void;\n    export function futimesSync(fd: number, atime: Date, mtime: Date): void;\n    export function fsync(fd: number, callback?: (err?: NodeJS.ErrnoException) => void): void;\n    export function fsyncSync(fd: number): void;\n    export function write(fd: number, buffer: Buffer, offset: number, length: number, position: number, callback?: (err: NodeJS.ErrnoException, written: number, buffer: Buffer) => void): void;\n    export function write(fd: number, buffer: Buffer, offset: number, length: number, callback?: (err: NodeJS.ErrnoException, written: number, buffer: Buffer) => void): void;\n    export function write(fd: number, data: any, callback?: (err: NodeJS.ErrnoException, written: number, str: string) => void): void;\n    export function write(fd: number, data: any, offset: number, callback?: (err: NodeJS.ErrnoException, written: number, str: string) => void): void;\n    export function write(fd: number, data: any, offset: number, encoding: string, callback?: (err: NodeJS.ErrnoException, written: number, str: string) => void): void;\n    export function writeSync(fd: number, buffer: Buffer, offset: number, length: number, position?: number): number;\n    export function writeSync(fd: number, data: any, position?: number, enconding?: string): number;\n    export function read(fd: number, buffer: Buffer, offset: number, length: number, position: number, callback?: (err: NodeJS.ErrnoException, bytesRead: number, buffer: Buffer) => void): void;\n    export function readSync(fd: number, buffer: Buffer, offset: number, length: number, position: number): number;\n    /*\n     * Asynchronous readFile - Asynchronously reads the entire contents of a file.\n     *\n     * @param fileName\n     * @param encoding\n     * @param callback - The callback is passed two arguments (err, data), where data is the contents of the file.\n     */\n    export function readFile(filename: string, encoding: string, callback: (err: NodeJS.ErrnoException, data: string) => void): void;\n    /*\n     * Asynchronous readFile - Asynchronously reads the entire contents of a file.\n     *\n     * @param fileName\n     * @param options An object with optional {encoding} and {flag} properties.  If {encoding} is specified, readFile returns a string; otherwise it returns a Buffer.\n     * @param callback - The callback is passed two arguments (err, data), where data is the contents of the file.\n     */\n    export function readFile(filename: string, options: { encoding: string; flag?: string; }, callback: (err: NodeJS.ErrnoException, data: string) => void): void;\n    /*\n     * Asynchronous readFile - Asynchronously reads the entire contents of a file.\n     *\n     * @param fileName\n     * @param options An object with optional {encoding} and {flag} properties.  If {encoding} is specified, readFile returns a string; otherwise it returns a Buffer.\n     * @param callback - The callback is passed two arguments (err, data), where data is the contents of the file.\n     */\n    export function readFile(filename: string, options: { flag?: string; }, callback: (err: NodeJS.ErrnoException, data: Buffer) => void): void;\n    /*\n     * Asynchronous readFile - Asynchronously reads the entire contents of a file.\n     *\n     * @param fileName\n     * @param callback - The callback is passed two arguments (err, data), where data is the contents of the file.\n     */\n    export function readFile(filename: string, callback: (err: NodeJS.ErrnoException, data: Buffer) => void): void;\n    /*\n     * Synchronous readFile - Synchronously reads the entire contents of a file.\n     *\n     * @param fileName\n     * @param encoding\n     */\n    export function readFileSync(filename: string, encoding: string): string;\n    /*\n     * Synchronous readFile - Synchronously reads the entire contents of a file.\n     *\n     * @param fileName\n     * @param options An object with optional {encoding} and {flag} properties.  If {encoding} is specified, readFileSync returns a string; otherwise it returns a Buffer.\n     */\n    export function readFileSync(filename: string, options: { encoding: string; flag?: string; }): string;\n    /*\n     * Synchronous readFile - Synchronously reads the entire contents of a file.\n     *\n     * @param fileName\n     * @param options An object with optional {encoding} and {flag} properties.  If {encoding} is specified, readFileSync returns a string; otherwise it returns a Buffer.\n     */\n    export function readFileSync(filename: string, options?: { flag?: string; }): Buffer;\n    export function writeFile(filename: string, data: any, callback?: (err: NodeJS.ErrnoException) => void): void;\n    export function writeFile(filename: string, data: any, options: { encoding?: string; mode?: number; flag?: string; }, callback?: (err: NodeJS.ErrnoException) => void): void;\n    export function writeFile(filename: string, data: any, options: { encoding?: string; mode?: string; flag?: string; }, callback?: (err: NodeJS.ErrnoException) => void): void;\n    export function writeFileSync(filename: string, data: any, options?: { encoding?: string; mode?: number; flag?: string; }): void;\n    export function writeFileSync(filename: string, data: any, options?: { encoding?: string; mode?: string; flag?: string; }): void;\n    export function appendFile(filename: string, data: any, options: { encoding?: string; mode?: number; flag?: string; }, callback?: (err: NodeJS.ErrnoException) => void): void;\n    export function appendFile(filename: string, data: any, options: { encoding?: string; mode?: string; flag?: string; }, callback?: (err: NodeJS.ErrnoException) => void): void;\n    export function appendFile(filename: string, data: any, callback?: (err: NodeJS.ErrnoException) => void): void;\n    export function appendFileSync(filename: string, data: any, options?: { encoding?: string; mode?: number; flag?: string; }): void;\n    export function appendFileSync(filename: string, data: any, options?: { encoding?: string; mode?: string; flag?: string; }): void;\n    export function watchFile(filename: string, listener: (curr: Stats, prev: Stats) => void): void;\n    export function watchFile(filename: string, options: { persistent?: boolean; interval?: number; }, listener: (curr: Stats, prev: Stats) => void): void;\n    export function unwatchFile(filename: string, listener?: (curr: Stats, prev: Stats) => void): void;\n    export function watch(filename: string, listener?: (event: string, filename: string) => any): FSWatcher;\n    export function watch(filename: string, encoding: string, listener?: (event: string, filename: string | Buffer) => any): FSWatcher;\n    export function watch(filename: string, options: { persistent?: boolean; recursive?: boolean; encoding?: string }, listener?: (event: string, filename: string | Buffer) => any): FSWatcher;\n    export function exists(path: string | Buffer, callback?: (exists: boolean) => void): void;\n    export function existsSync(path: string | Buffer): boolean;\n    /** Constant for fs.access(). File is visible to the calling process. */\n    export var F_OK: number;\n    /** Constant for fs.access(). File can be read by the calling process. */\n    export var R_OK: number;\n    /** Constant for fs.access(). File can be written by the calling process. */\n    export var W_OK: number;\n    /** Constant for fs.access(). File can be executed by the calling process. */\n    export var X_OK: number;\n    /** Tests a user's permissions for the file specified by path. */\n    export function access(path: string | Buffer, callback: (err: NodeJS.ErrnoException) => void): void;\n    export function access(path: string | Buffer, mode: number, callback: (err: NodeJS.ErrnoException) => void): void;\n    /** Synchronous version of fs.access. This throws if any accessibility checks fail, and does nothing otherwise. */\n    export function accessSync(path: string | Buffer, mode?: number): void;\n    export function createReadStream(path: string | Buffer, options?: {\n        flags?: string;\n        encoding?: string;\n        fd?: number;\n        mode?: number;\n        autoClose?: boolean;\n        start?: number;\n        end?: number;\n    }): ReadStream;\n    export function createWriteStream(path: string | Buffer, options?: {\n        flags?: string;\n        encoding?: string;\n        fd?: number;\n        mode?: number;\n    }): WriteStream;\n}\n\ndeclare module \"path\" {\n\n    /**\n     * A parsed path object generated by path.parse() or consumed by path.format().\n     */\n    export interface ParsedPath {\n        /**\n         * The root of the path such as '/' or 'c:\\'\n         */\n        root: string;\n        /**\n         * The full directory path such as '/home/user/dir' or 'c:\\path\\dir'\n         */\n        dir: string;\n        /**\n         * The file name including extension (if any) such as 'index.html'\n         */\n        base: string;\n        /**\n         * The file extension (if any) such as '.html'\n         */\n        ext: string;\n        /**\n         * The file name without extension (if any) such as 'index'\n         */\n        name: string;\n    }\n\n    /**\n     * Normalize a string path, reducing '..' and '.' parts.\n     * When multiple slashes are found, they're replaced by a single one; when the path contains a trailing slash, it is preserved. On Windows backslashes are used.\n     *\n     * @param p string path to normalize.\n     */\n    export function normalize(p: string): string;\n    /**\n     * Join all arguments together and normalize the resulting path.\n     * Arguments must be strings. In v0.8, non-string arguments were silently ignored. In v0.10 and up, an exception is thrown.\n     *\n     * @param paths string paths to join.\n     */\n    export function join(...paths: any[]): string;\n    /**\n     * Join all arguments together and normalize the resulting path.\n     * Arguments must be strings. In v0.8, non-string arguments were silently ignored. In v0.10 and up, an exception is thrown.\n     *\n     * @param paths string paths to join.\n     */\n    export function join(...paths: string[]): string;\n    /**\n     * The right-most parameter is considered {to}.  Other parameters are considered an array of {from}.\n     *\n     * Starting from leftmost {from} paramter, resolves {to} to an absolute path.\n     *\n     * If {to} isn't already absolute, {from} arguments are prepended in right to left order, until an absolute path is found. If after using all {from} paths still no absolute path is found, the current working directory is used as well. The resulting path is normalized, and trailing slashes are removed unless the path gets resolved to the root directory.\n     *\n     * @param pathSegments string paths to join.  Non-string arguments are ignored.\n     */\n    export function resolve(...pathSegments: any[]): string;\n    /**\n     * Determines whether {path} is an absolute path. An absolute path will always resolve to the same location, regardless of the working directory.\n     *\n     * @param path path to test.\n     */\n    export function isAbsolute(path: string): boolean;\n    /**\n     * Solve the relative path from {from} to {to}.\n     * At times we have two absolute paths, and we need to derive the relative path from one to the other. This is actually the reverse transform of path.resolve.\n     *\n     * @param from\n     * @param to\n     */\n    export function relative(from: string, to: string): string;\n    /**\n     * Return the directory name of a path. Similar to the Unix dirname command.\n     *\n     * @param p the path to evaluate.\n     */\n    export function dirname(p: string): string;\n    /**\n     * Return the last portion of a path. Similar to the Unix basename command.\n     * Often used to extract the file name from a fully qualified path.\n     *\n     * @param p the path to evaluate.\n     * @param ext optionally, an extension to remove from the result.\n     */\n    export function basename(p: string, ext?: string): string;\n    /**\n     * Return the extension of the path, from the last '.' to end of string in the last portion of the path.\n     * If there is no '.' in the last portion of the path or the first character of it is '.', then it returns an empty string\n     *\n     * @param p the path to evaluate.\n     */\n    export function extname(p: string): string;\n    /**\n     * The platform-specific file separator. '\\\\' or '/'.\n     */\n    export var sep: string;\n    /**\n     * The platform-specific file delimiter. ';' or ':'.\n     */\n    export var delimiter: string;\n    /**\n     * Returns an object from a path string - the opposite of format().\n     *\n     * @param pathString path to evaluate.\n     */\n    export function parse(pathString: string): ParsedPath;\n    /**\n     * Returns a path string from an object - the opposite of parse().\n     *\n     * @param pathString path to evaluate.\n     */\n    export function format(pathObject: ParsedPath): string;\n\n    export module posix {\n        export function normalize(p: string): string;\n        export function join(...paths: any[]): string;\n        export function resolve(...pathSegments: any[]): string;\n        export function isAbsolute(p: string): boolean;\n        export function relative(from: string, to: string): string;\n        export function dirname(p: string): string;\n        export function basename(p: string, ext?: string): string;\n        export function extname(p: string): string;\n        export var sep: string;\n        export var delimiter: string;\n        export function parse(p: string): ParsedPath;\n        export function format(pP: ParsedPath): string;\n    }\n\n    export module win32 {\n        export function normalize(p: string): string;\n        export function join(...paths: any[]): string;\n        export function resolve(...pathSegments: any[]): string;\n        export function isAbsolute(p: string): boolean;\n        export function relative(from: string, to: string): string;\n        export function dirname(p: string): string;\n        export function basename(p: string, ext?: string): string;\n        export function extname(p: string): string;\n        export var sep: string;\n        export var delimiter: string;\n        export function parse(p: string): ParsedPath;\n        export function format(pP: ParsedPath): string;\n    }\n}\n\ndeclare module \"string_decoder\" {\n    export interface NodeStringDecoder {\n        write(buffer: Buffer): string;\n        end(buffer?: Buffer): string;\n    }\n    export var StringDecoder: {\n        new (encoding?: string): NodeStringDecoder;\n    };\n}\n\ndeclare module \"tls\" {\n    import * as crypto from \"crypto\";\n    import * as net from \"net\";\n    import * as stream from \"stream\";\n\n    var CLIENT_RENEG_LIMIT: number;\n    var CLIENT_RENEG_WINDOW: number;\n\n    export interface Certificate {\n        /**\n         * Country code.\n         */\n        C: string;\n        /**\n         * Street.\n         */\n        ST: string;\n        /**\n         * Locality.\n         */\n        L: string;\n        /**\n         * Organization.\n         */\n        O: string;\n        /**\n         * Organizational unit.\n         */\n        OU: string;\n        /**\n         * Common name.\n         */\n        CN: string;\n    }\n\n    export interface CipherNameAndProtocol {\n        /**\n         * The cipher name.\n         */\n        name: string;\n        /**\n         * SSL/TLS protocol version.\n         */\n        version: string;\n    }\n\n    export class TLSSocket extends stream.Duplex {\n        /**\n         * Returns the bound address, the address family name and port of the underlying socket as reported by\n         * the operating system.\n         * @returns {any} - An object with three properties, e.g. { port: 12346, family: 'IPv4', address: '127.0.0.1' }.\n         */\n        address(): { port: number; family: string; address: string };\n        /**\n         * A boolean that is true if the peer certificate was signed by one of the specified CAs, otherwise false.\n         */\n        authorized: boolean;\n        /**\n         * The reason why the peer's certificate has not been verified.\n         * This property becomes available only when tlsSocket.authorized === false.\n         */\n        authorizationError: Error;\n        /**\n         * Static boolean value, always true.\n         * May be used to distinguish TLS sockets from regular ones.\n         */\n        encrypted: boolean;\n        /**\n         * Returns an object representing the cipher name and the SSL/TLS protocol version of the current connection.\n         * @returns {CipherNameAndProtocol} - Returns an object representing the cipher name\n         * and the SSL/TLS protocol version of the current connection.\n         */\n        getCipher(): CipherNameAndProtocol;\n        /**\n         * Returns an object representing the peer's certificate.\n         * The returned object has some properties corresponding to the field of the certificate.\n         * If detailed argument is true the full chain with issuer property will be returned,\n         * if false only the top certificate without issuer property.\n         * If the peer does not provide a certificate, it returns null or an empty object.\n         * @param {boolean} detailed - If true; the full chain with issuer property will be returned.\n         * @returns {any} - An object representing the peer's certificate.\n         */\n        getPeerCertificate(detailed?: boolean): {\n            subject: Certificate;\n            issuerInfo: Certificate;\n            issuer: Certificate;\n            raw: any;\n            valid_from: string;\n            valid_to: string;\n            fingerprint: string;\n            serialNumber: string;\n        };\n        /**\n         * Could be used to speed up handshake establishment when reconnecting to the server.\n         * @returns {any} - ASN.1 encoded TLS session or undefined if none was negotiated.\n         */\n        getSession(): any;\n        /**\n         * NOTE: Works only with client TLS sockets.\n         * Useful only for debugging, for session reuse provide session option to tls.connect().\n         * @returns {any} - TLS session ticket or undefined if none was negotiated.\n         */\n        getTLSTicket(): any;\n        /**\n         * The string representation of the local IP address.\n         */\n        localAddress: string;\n        /**\n         * The numeric representation of the local port.\n         */\n        localPort: string;\n        /**\n         * The string representation of the remote IP address.\n         * For example, '74.125.127.100' or '2001:4860:a005::68'.\n         */\n        remoteAddress: string;\n        /**\n         * The string representation of the remote IP family. 'IPv4' or 'IPv6'.\n         */\n        remoteFamily: string;\n        /**\n         * The numeric representation of the remote port. For example, 443.\n         */\n        remotePort: number;\n        /**\n         * Initiate TLS renegotiation process.\n         *\n         * NOTE: Can be used to request peer's certificate after the secure connection has been established.\n         * ANOTHER NOTE: When running as the server, socket will be destroyed with an error after handshakeTimeout timeout.\n         * @param {TlsOptions} options - The options may contain the following fields: rejectUnauthorized,\n         * requestCert (See tls.createServer() for details).\n         * @param {Function} callback - callback(err) will be executed with null as err, once the renegotiation\n         * is successfully completed.\n         */\n        renegotiate(options: TlsOptions, callback: (err: Error) => any): any;\n        /**\n         * Set maximum TLS fragment size (default and maximum value is: 16384, minimum is: 512).\n         * Smaller fragment size decreases buffering latency on the client: large fragments are buffered by\n         * the TLS layer until the entire fragment is received and its integrity is verified;\n         * large fragments can span multiple roundtrips, and their processing can be delayed due to packet\n         * loss or reordering. However, smaller fragments add extra TLS framing bytes and CPU overhead,\n         * which may decrease overall server throughput.\n         * @param {number} size - TLS fragment size (default and maximum value is: 16384, minimum is: 512).\n         * @returns {boolean} - Returns true on success, false otherwise.\n         */\n        setMaxSendFragment(size: number): boolean;\n    }\n\n    export interface TlsOptions {\n        host?: string;\n        port?: number;\n        pfx?: any;   //string or buffer\n        key?: any;   //string or buffer\n        passphrase?: string;\n        cert?: any;\n        ca?: any;    //string or buffer\n        crl?: any;   //string or string array\n        ciphers?: string;\n        honorCipherOrder?: any;\n        requestCert?: boolean;\n        rejectUnauthorized?: boolean;\n        NPNProtocols?: any;  //array or Buffer;\n        SNICallback?: (servername: string, cb:(err:Error,ctx:SecureContext)=>any) => any;\n    }\n\n    export interface ConnectionOptions {\n        host?: string;\n        port?: number;\n        socket?: net.Socket;\n        pfx?: string | Buffer\n        key?: string | Buffer\n        passphrase?: string;\n        cert?: string | Buffer\n        ca?: (string | Buffer)[];\n        rejectUnauthorized?: boolean;\n        NPNProtocols?: (string | Buffer)[];\n        servername?: string;\n    }\n\n    export interface Server extends net.Server {\n        close(): Server;\n        address(): { port: number; family: string; address: string; };\n        addContext(hostName: string, credentials: {\n            key: string;\n            cert: string;\n            ca: string;\n        }): void;\n        maxConnections: number;\n        connections: number;\n    }\n\n    export interface ClearTextStream extends stream.Duplex {\n        authorized: boolean;\n        authorizationError: Error;\n        getPeerCertificate(): any;\n        getCipher: {\n            name: string;\n            version: string;\n        };\n        address: {\n            port: number;\n            family: string;\n            address: string;\n        };\n        remoteAddress: string;\n        remotePort: number;\n    }\n\n    export interface SecurePair {\n        encrypted: any;\n        cleartext: any;\n    }\n\n    export interface SecureContextOptions {\n        pfx?: string | Buffer;\n        key?: string | Buffer;\n        passphrase?: string;\n        cert?: string | Buffer;\n        ca?: string | Buffer;\n        crl?: string | string[]\n        ciphers?: string;\n        honorCipherOrder?: boolean;\n    }\n\n    export interface SecureContext {\n        context: any;\n    }\n\n    export function createServer(options: TlsOptions, secureConnectionListener?: (cleartextStream: ClearTextStream) => void): Server;\n    export function connect(options: TlsOptions, secureConnectionListener?: () => void): ClearTextStream;\n    export function connect(port: number, host?: string, options?: ConnectionOptions, secureConnectListener?: () => void): ClearTextStream;\n    export function connect(port: number, options?: ConnectionOptions, secureConnectListener?: () => void): ClearTextStream;\n    export function createSecurePair(credentials?: crypto.Credentials, isServer?: boolean, requestCert?: boolean, rejectUnauthorized?: boolean): SecurePair;\n    export function createSecureContext(details: SecureContextOptions): SecureContext;\n}\n\ndeclare module \"crypto\" {\n    export interface CredentialDetails {\n        pfx: string;\n        key: string;\n        passphrase: string;\n        cert: string;\n        ca: string | string[];\n        crl: string | string[];\n        ciphers: string;\n    }\n    export interface Credentials { context?: any; }\n    export function createCredentials(details: CredentialDetails): Credentials;\n    export function createHash(algorithm: string): Hash;\n    export function createHmac(algorithm: string, key: string): Hmac;\n    export function createHmac(algorithm: string, key: Buffer): Hmac;\n    export interface Hash {\n        update(data: any, input_encoding?: string): Hash;\n        digest(encoding: 'buffer'): Buffer;\n        digest(encoding: string): any;\n        digest(): Buffer;\n    }\n    export interface Hmac extends NodeJS.ReadWriteStream {\n        update(data: any, input_encoding?: string): Hmac;\n        digest(encoding: 'buffer'): Buffer;\n        digest(encoding: string): any;\n        digest(): Buffer;\n    }\n    export function createCipher(algorithm: string, password: any): Cipher;\n    export function createCipheriv(algorithm: string, key: any, iv: any): Cipher;\n    export interface Cipher extends NodeJS.ReadWriteStream {\n        update(data: Buffer): Buffer;\n        update(data: string, input_encoding: \"utf8\" | \"ascii\" | \"binary\"): Buffer;\n        update(data: Buffer, input_encoding: any, output_encoding: \"binary\" | \"base64\" | \"hex\"): string;\n        update(data: string, input_encoding: \"utf8\" | \"ascii\" | \"binary\", output_encoding: \"binary\" | \"base64\" | \"hex\"): string;\n        final(): Buffer;\n        final(output_encoding: string): string;\n        setAutoPadding(auto_padding: boolean): void;\n        getAuthTag(): Buffer;\n    }\n    export function createDecipher(algorithm: string, password: any): Decipher;\n    export function createDecipheriv(algorithm: string, key: any, iv: any): Decipher;\n    export interface Decipher extends NodeJS.ReadWriteStream {\n        update(data: Buffer): Buffer;\n        update(data: string, input_encoding: \"binary\" | \"base64\" | \"hex\"): Buffer;\n        update(data: Buffer, input_encoding: any, output_encoding: \"utf8\" | \"ascii\" | \"binary\"): string;\n        update(data: string, input_encoding: \"binary\" | \"base64\" | \"hex\", output_encoding: \"utf8\" | \"ascii\" | \"binary\"): string;\n        final(): Buffer;\n        final(output_encoding: string): string;\n        setAutoPadding(auto_padding: boolean): void;\n        setAuthTag(tag: Buffer): void;\n    }\n    export function createSign(algorithm: string): Signer;\n    export interface Signer extends NodeJS.WritableStream {\n        update(data: any): void;\n        sign(private_key: string, output_format: string): string;\n    }\n    export function createVerify(algorith: string): Verify;\n    export interface Verify extends NodeJS.WritableStream {\n        update(data: any): void;\n        verify(object: string, signature: string, signature_format?: string): boolean;\n    }\n    export function createDiffieHellman(prime_length: number): DiffieHellman;\n    export function createDiffieHellman(prime: number, encoding?: string): DiffieHellman;\n    export interface DiffieHellman {\n        generateKeys(encoding?: string): string;\n        computeSecret(other_public_key: string, input_encoding?: string, output_encoding?: string): string;\n        getPrime(encoding?: string): string;\n        getGenerator(encoding: string): string;\n        getPublicKey(encoding?: string): string;\n        getPrivateKey(encoding?: string): string;\n        setPublicKey(public_key: string, encoding?: string): void;\n        setPrivateKey(public_key: string, encoding?: string): void;\n    }\n    export function getDiffieHellman(group_name: string): DiffieHellman;\n    export function pbkdf2(password: string | Buffer, salt: string | Buffer, iterations: number, keylen: number, callback: (err: Error, derivedKey: Buffer) => any): void;\n    export function pbkdf2(password: string | Buffer, salt: string | Buffer, iterations: number, keylen: number, digest: string, callback: (err: Error, derivedKey: Buffer) => any): void;\n    export function pbkdf2Sync(password: string | Buffer, salt: string | Buffer, iterations: number, keylen: number): Buffer;\n    export function pbkdf2Sync(password: string | Buffer, salt: string | Buffer, iterations: number, keylen: number, digest: string): Buffer;\n    export function randomBytes(size: number): Buffer;\n    export function randomBytes(size: number, callback: (err: Error, buf: Buffer) => void): void;\n    export function pseudoRandomBytes(size: number): Buffer;\n    export function pseudoRandomBytes(size: number, callback: (err: Error, buf: Buffer) => void): void;\n    export interface RsaPublicKey {\n        key: string;\n        padding?: any;\n    }\n    export interface RsaPrivateKey {\n        key: string;\n        passphrase?: string,\n        padding?: any;\n    }\n    export function publicEncrypt(public_key: string | RsaPublicKey, buffer: Buffer): Buffer\n    export function privateDecrypt(private_key: string | RsaPrivateKey, buffer: Buffer): Buffer\n}\n\ndeclare module \"stream\" {\n    import * as events from \"events\";\n\n    export class Stream extends events.EventEmitter {\n        pipe<T extends NodeJS.WritableStream>(destination: T, options?: { end?: boolean; }): T;\n    }\n\n    export interface ReadableOptions {\n        highWaterMark?: number;\n        encoding?: string;\n        objectMode?: boolean;\n        read?: (size?: number) => any;\n    }\n\n    export class Readable extends events.EventEmitter implements NodeJS.ReadableStream {\n        readable: boolean;\n        constructor(opts?: ReadableOptions);\n        _read(size: number): void;\n        read(size?: number): any;\n        setEncoding(encoding: string): void;\n        pause(): void;\n        resume(): void;\n        pipe<T extends NodeJS.WritableStream>(destination: T, options?: { end?: boolean; }): T;\n        unpipe<T extends NodeJS.WritableStream>(destination?: T): void;\n        unshift(chunk: any): void;\n        wrap(oldStream: NodeJS.ReadableStream): NodeJS.ReadableStream;\n        push(chunk: any, encoding?: string): boolean;\n    }\n\n    export interface WritableOptions {\n        highWaterMark?: number;\n        decodeStrings?: boolean;\n        objectMode?: boolean;\n        write?: (chunk: string|Buffer, encoding: string, callback: Function) => any;\n        writev?: (chunks: {chunk: string|Buffer, encoding: string}[], callback: Function) => any;\n    }\n\n    export class Writable extends events.EventEmitter implements NodeJS.WritableStream {\n        writable: boolean;\n        constructor(opts?: WritableOptions);\n        _write(chunk: any, encoding: string, callback: Function): void;\n        write(chunk: any, cb?: Function): boolean;\n        write(chunk: any, encoding?: string, cb?: Function): boolean;\n        end(): void;\n        end(chunk: any, cb?: Function): void;\n        end(chunk: any, encoding?: string, cb?: Function): void;\n    }\n\n    export interface DuplexOptions extends ReadableOptions, WritableOptions {\n        allowHalfOpen?: boolean;\n        readableObjectMode?: boolean;\n        writableObjectMode?: boolean;\n    }\n\n    // Note: Duplex extends both Readable and Writable.\n    export class Duplex extends Readable implements NodeJS.ReadWriteStream {\n        writable: boolean;\n        constructor(opts?: DuplexOptions);\n        _write(chunk: any, encoding: string, callback: Function): void;\n        write(chunk: any, cb?: Function): boolean;\n        write(chunk: any, encoding?: string, cb?: Function): boolean;\n        end(): void;\n        end(chunk: any, cb?: Function): void;\n        end(chunk: any, encoding?: string, cb?: Function): void;\n    }\n\n    export interface TransformOptions extends ReadableOptions, WritableOptions {\n        transform?: (chunk: string|Buffer, encoding: string, callback: Function) => any;\n        flush?: (callback: Function) => any;\n    }\n\n    // Note: Transform lacks the _read and _write methods of Readable/Writable.\n    export class Transform extends events.EventEmitter implements NodeJS.ReadWriteStream {\n        readable: boolean;\n        writable: boolean;\n        constructor(opts?: TransformOptions);\n        _transform(chunk: any, encoding: string, callback: Function): void;\n        _flush(callback: Function): void;\n        read(size?: number): any;\n        setEncoding(encoding: string): void;\n        pause(): void;\n        resume(): void;\n        pipe<T extends NodeJS.WritableStream>(destination: T, options?: { end?: boolean; }): T;\n        unpipe<T extends NodeJS.WritableStream>(destination?: T): void;\n        unshift(chunk: any): void;\n        wrap(oldStream: NodeJS.ReadableStream): NodeJS.ReadableStream;\n        push(chunk: any, encoding?: string): boolean;\n        write(chunk: any, cb?: Function): boolean;\n        write(chunk: any, encoding?: string, cb?: Function): boolean;\n        end(): void;\n        end(chunk: any, cb?: Function): void;\n        end(chunk: any, encoding?: string, cb?: Function): void;\n    }\n\n    export class PassThrough extends Transform { }\n}\n\ndeclare module \"util\" {\n    export interface InspectOptions {\n        showHidden?: boolean;\n        depth?: number;\n        colors?: boolean;\n        customInspect?: boolean;\n    }\n\n    export function format(format: any, ...param: any[]): string;\n    export function debug(string: string): void;\n    export function error(...param: any[]): void;\n    export function puts(...param: any[]): void;\n    export function print(...param: any[]): void;\n    export function log(string: string): void;\n    export function inspect(object: any, showHidden?: boolean, depth?: number, color?: boolean): string;\n    export function inspect(object: any, options: InspectOptions): string;\n    export function isArray(object: any): boolean;\n    export function isRegExp(object: any): boolean;\n    export function isDate(object: any): boolean;\n    export function isError(object: any): boolean;\n    export function inherits(constructor: any, superConstructor: any): void;\n    export function debuglog(key: string): (msg: string, ...param: any[]) => void;\n}\n\ndeclare module \"assert\" {\n    function internal(value: any, message?: string): void;\n    namespace internal {\n        export class AssertionError implements Error {\n            name: string;\n            message: string;\n            actual: any;\n            expected: any;\n            operator: string;\n            generatedMessage: boolean;\n\n            constructor(options?: {\n                message?: string; actual?: any; expected?: any;\n                operator?: string; stackStartFunction?: Function\n            });\n        }\n\n        export function fail(actual?: any, expected?: any, message?: string, operator?: string): void;\n        export function ok(value: any, message?: string): void;\n        export function equal(actual: any, expected: any, message?: string): void;\n        export function notEqual(actual: any, expected: any, message?: string): void;\n        export function deepEqual(actual: any, expected: any, message?: string): void;\n        export function notDeepEqual(acutal: any, expected: any, message?: string): void;\n        export function strictEqual(actual: any, expected: any, message?: string): void;\n        export function notStrictEqual(actual: any, expected: any, message?: string): void;\n        export function deepStrictEqual(actual: any, expected: any, message?: string): void;\n        export function notDeepStrictEqual(actual: any, expected: any, message?: string): void;\n        export var throws: {\n            (block: Function, message?: string): void;\n            (block: Function, error: Function, message?: string): void;\n            (block: Function, error: RegExp, message?: string): void;\n            (block: Function, error: (err: any) => boolean, message?: string): void;\n        };\n\n        export var doesNotThrow: {\n            (block: Function, message?: string): void;\n            (block: Function, error: Function, message?: string): void;\n            (block: Function, error: RegExp, message?: string): void;\n            (block: Function, error: (err: any) => boolean, message?: string): void;\n        };\n\n        export function ifError(value: any): void;\n    }\n\n    export = internal;\n}\n\ndeclare module \"tty\" {\n    import * as net from \"net\";\n\n    export function isatty(fd: number): boolean;\n    export interface ReadStream extends net.Socket {\n        isRaw: boolean;\n        setRawMode(mode: boolean): void;\n        isTTY: boolean;\n    }\n    export interface WriteStream extends net.Socket {\n        columns: number;\n        rows: number;\n        isTTY: boolean;\n    }\n}\n\ndeclare module \"domain\" {\n    import * as events from \"events\";\n\n    export class Domain extends events.EventEmitter implements NodeJS.Domain {\n        run(fn: Function): void;\n        add(emitter: events.EventEmitter): void;\n        remove(emitter: events.EventEmitter): void;\n        bind(cb: (err: Error, data: any) => any): any;\n        intercept(cb: (data: any) => any): any;\n        dispose(): void;\n    }\n\n    export function create(): Domain;\n}\n\ndeclare module \"constants\" {\n    export var E2BIG: number;\n    export var EACCES: number;\n    export var EADDRINUSE: number;\n    export var EADDRNOTAVAIL: number;\n    export var EAFNOSUPPORT: number;\n    export var EAGAIN: number;\n    export var EALREADY: number;\n    export var EBADF: number;\n    export var EBADMSG: number;\n    export var EBUSY: number;\n    export var ECANCELED: number;\n    export var ECHILD: number;\n    export var ECONNABORTED: number;\n    export var ECONNREFUSED: number;\n    export var ECONNRESET: number;\n    export var EDEADLK: number;\n    export var EDESTADDRREQ: number;\n    export var EDOM: number;\n    export var EEXIST: number;\n    export var EFAULT: number;\n    export var EFBIG: number;\n    export var EHOSTUNREACH: number;\n    export var EIDRM: number;\n    export var EILSEQ: number;\n    export var EINPROGRESS: number;\n    export var EINTR: number;\n    export var EINVAL: number;\n    export var EIO: number;\n    export var EISCONN: number;\n    export var EISDIR: number;\n    export var ELOOP: number;\n    export var EMFILE: number;\n    export var EMLINK: number;\n    export var EMSGSIZE: number;\n    export var ENAMETOOLONG: number;\n    export var ENETDOWN: number;\n    export var ENETRESET: number;\n    export var ENETUNREACH: number;\n    export var ENFILE: number;\n    export var ENOBUFS: number;\n    export var ENODATA: number;\n    export var ENODEV: number;\n    export var ENOENT: number;\n    export var ENOEXEC: number;\n    export var ENOLCK: number;\n    export var ENOLINK: number;\n    export var ENOMEM: number;\n    export var ENOMSG: number;\n    export var ENOPROTOOPT: number;\n    export var ENOSPC: number;\n    export var ENOSR: number;\n    export var ENOSTR: number;\n    export var ENOSYS: number;\n    export var ENOTCONN: number;\n    export var ENOTDIR: number;\n    export var ENOTEMPTY: number;\n    export var ENOTSOCK: number;\n    export var ENOTSUP: number;\n    export var ENOTTY: number;\n    export var ENXIO: number;\n    export var EOPNOTSUPP: number;\n    export var EOVERFLOW: number;\n    export var EPERM: number;\n    export var EPIPE: number;\n    export var EPROTO: number;\n    export var EPROTONOSUPPORT: number;\n    export var EPROTOTYPE: number;\n    export var ERANGE: number;\n    export var EROFS: number;\n    export var ESPIPE: number;\n    export var ESRCH: number;\n    export var ETIME: number;\n    export var ETIMEDOUT: number;\n    export var ETXTBSY: number;\n    export var EWOULDBLOCK: number;\n    export var EXDEV: number;\n    export var WSAEINTR: number;\n    export var WSAEBADF: number;\n    export var WSAEACCES: number;\n    export var WSAEFAULT: number;\n    export var WSAEINVAL: number;\n    export var WSAEMFILE: number;\n    export var WSAEWOULDBLOCK: number;\n    export var WSAEINPROGRESS: number;\n    export var WSAEALREADY: number;\n    export var WSAENOTSOCK: number;\n    export var WSAEDESTADDRREQ: number;\n    export var WSAEMSGSIZE: number;\n    export var WSAEPROTOTYPE: number;\n    export var WSAENOPROTOOPT: number;\n    export var WSAEPROTONOSUPPORT: number;\n    export var WSAESOCKTNOSUPPORT: number;\n    export var WSAEOPNOTSUPP: number;\n    export var WSAEPFNOSUPPORT: number;\n    export var WSAEAFNOSUPPORT: number;\n    export var WSAEADDRINUSE: number;\n    export var WSAEADDRNOTAVAIL: number;\n    export var WSAENETDOWN: number;\n    export var WSAENETUNREACH: number;\n    export var WSAENETRESET: number;\n    export var WSAECONNABORTED: number;\n    export var WSAECONNRESET: number;\n    export var WSAENOBUFS: number;\n    export var WSAEISCONN: number;\n    export var WSAENOTCONN: number;\n    export var WSAESHUTDOWN: number;\n    export var WSAETOOMANYREFS: number;\n    export var WSAETIMEDOUT: number;\n    export var WSAECONNREFUSED: number;\n    export var WSAELOOP: number;\n    export var WSAENAMETOOLONG: number;\n    export var WSAEHOSTDOWN: number;\n    export var WSAEHOSTUNREACH: number;\n    export var WSAENOTEMPTY: number;\n    export var WSAEPROCLIM: number;\n    export var WSAEUSERS: number;\n    export var WSAEDQUOT: number;\n    export var WSAESTALE: number;\n    export var WSAEREMOTE: number;\n    export var WSASYSNOTREADY: number;\n    export var WSAVERNOTSUPPORTED: number;\n    export var WSANOTINITIALISED: number;\n    export var WSAEDISCON: number;\n    export var WSAENOMORE: number;\n    export var WSAECANCELLED: number;\n    export var WSAEINVALIDPROCTABLE: number;\n    export var WSAEINVALIDPROVIDER: number;\n    export var WSAEPROVIDERFAILEDINIT: number;\n    export var WSASYSCALLFAILURE: number;\n    export var WSASERVICE_NOT_FOUND: number;\n    export var WSATYPE_NOT_FOUND: number;\n    export var WSA_E_NO_MORE: number;\n    export var WSA_E_CANCELLED: number;\n    export var WSAEREFUSED: number;\n    export var SIGHUP: number;\n    export var SIGINT: number;\n    export var SIGILL: number;\n    export var SIGABRT: number;\n    export var SIGFPE: number;\n    export var SIGKILL: number;\n    export var SIGSEGV: number;\n    export var SIGTERM: number;\n    export var SIGBREAK: number;\n    export var SIGWINCH: number;\n    export var SSL_OP_ALL: number;\n    export var SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION: number;\n    export var SSL_OP_CIPHER_SERVER_PREFERENCE: number;\n    export var SSL_OP_CISCO_ANYCONNECT: number;\n    export var SSL_OP_COOKIE_EXCHANGE: number;\n    export var SSL_OP_CRYPTOPRO_TLSEXT_BUG: number;\n    export var SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS: number;\n    export var SSL_OP_EPHEMERAL_RSA: number;\n    export var SSL_OP_LEGACY_SERVER_CONNECT: number;\n    export var SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER: number;\n    export var SSL_OP_MICROSOFT_SESS_ID_BUG: number;\n    export var SSL_OP_MSIE_SSLV2_RSA_PADDING: number;\n    export var SSL_OP_NETSCAPE_CA_DN_BUG: number;\n    export var SSL_OP_NETSCAPE_CHALLENGE_BUG: number;\n    export var SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG: number;\n    export var SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG: number;\n    export var SSL_OP_NO_COMPRESSION: number;\n    export var SSL_OP_NO_QUERY_MTU: number;\n    export var SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION: number;\n    export var SSL_OP_NO_SSLv2: number;\n    export var SSL_OP_NO_SSLv3: number;\n    export var SSL_OP_NO_TICKET: number;\n    export var SSL_OP_NO_TLSv1: number;\n    export var SSL_OP_NO_TLSv1_1: number;\n    export var SSL_OP_NO_TLSv1_2: number;\n    export var SSL_OP_PKCS1_CHECK_1: number;\n    export var SSL_OP_PKCS1_CHECK_2: number;\n    export var SSL_OP_SINGLE_DH_USE: number;\n    export var SSL_OP_SINGLE_ECDH_USE: number;\n    export var SSL_OP_SSLEAY_080_CLIENT_DH_BUG: number;\n    export var SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG: number;\n    export var SSL_OP_TLS_BLOCK_PADDING_BUG: number;\n    export var SSL_OP_TLS_D5_BUG: number;\n    export var SSL_OP_TLS_ROLLBACK_BUG: number;\n    export var ENGINE_METHOD_DSA: number;\n    export var ENGINE_METHOD_DH: number;\n    export var ENGINE_METHOD_RAND: number;\n    export var ENGINE_METHOD_ECDH: number;\n    export var ENGINE_METHOD_ECDSA: number;\n    export var ENGINE_METHOD_CIPHERS: number;\n    export var ENGINE_METHOD_DIGESTS: number;\n    export var ENGINE_METHOD_STORE: number;\n    export var ENGINE_METHOD_PKEY_METHS: number;\n    export var ENGINE_METHOD_PKEY_ASN1_METHS: number;\n    export var ENGINE_METHOD_ALL: number;\n    export var ENGINE_METHOD_NONE: number;\n    export var DH_CHECK_P_NOT_SAFE_PRIME: number;\n    export var DH_CHECK_P_NOT_PRIME: number;\n    export var DH_UNABLE_TO_CHECK_GENERATOR: number;\n    export var DH_NOT_SUITABLE_GENERATOR: number;\n    export var NPN_ENABLED: number;\n    export var RSA_PKCS1_PADDING: number;\n    export var RSA_SSLV23_PADDING: number;\n    export var RSA_NO_PADDING: number;\n    export var RSA_PKCS1_OAEP_PADDING: number;\n    export var RSA_X931_PADDING: number;\n    export var RSA_PKCS1_PSS_PADDING: number;\n    export var POINT_CONVERSION_COMPRESSED: number;\n    export var POINT_CONVERSION_UNCOMPRESSED: number;\n    export var POINT_CONVERSION_HYBRID: number;\n    export var O_RDONLY: number;\n    export var O_WRONLY: number;\n    export var O_RDWR: number;\n    export var S_IFMT: number;\n    export var S_IFREG: number;\n    export var S_IFDIR: number;\n    export var S_IFCHR: number;\n    export var S_IFBLK: number;\n    export var S_IFIFO: number;\n    export var S_IFSOCK: number;\n    export var S_IRWXU: number;\n    export var S_IRUSR: number;\n    export var S_IWUSR: number;\n    export var S_IXUSR: number;\n    export var S_IRWXG: number;\n    export var S_IRGRP: number;\n    export var S_IWGRP: number;\n    export var S_IXGRP: number;\n    export var S_IRWXO: number;\n    export var S_IROTH: number;\n    export var S_IWOTH: number;\n    export var S_IXOTH: number;\n    export var S_IFLNK: number;\n    export var O_CREAT: number;\n    export var O_EXCL: number;\n    export var O_NOCTTY: number;\n    export var O_DIRECTORY: number;\n    export var O_NOATIME: number;\n    export var O_NOFOLLOW: number;\n    export var O_SYNC: number;\n    export var O_SYMLINK: number;\n    export var O_DIRECT: number;\n    export var O_NONBLOCK: number;\n    export var O_TRUNC: number;\n    export var O_APPEND: number;\n    export var F_OK: number;\n    export var R_OK: number;\n    export var W_OK: number;\n    export var X_OK: number;\n    export var UV_UDP_REUSEADDR: number;\n}\n"
  },
  {
    "path": "examples/wait-ready/app.js",
    "content": "const http = require('http');\n\nprocess.on('SIGINT', (msg) => {\n  console.log('Just got SIGINTed, but I dont care');\n  process.exit(0);\n});\n\nconst port = 3000;\n\nconst app = http.createServer((_, res) => {\n  res.writeHead(200);\n  res.end('Hello!');\n});\n\nsetTimeout(() => {\n  app.listen(port, () => {\n    console.log(`Listening on ${port}`);\n    if (process.send) {\n      process.send('ready');\n    }\n  });\n}, 10000);\n"
  },
  {
    "path": "examples/wait-ready/ecosystem.json",
    "content": "{\n  \"apps\": [{\n    \"name\": \"app\",\n    \"script\": \"app.js\",\n    \"instances\": 2,\n    \"exec_mode\": \"cluster\",\n    \"wait_ready\": true,\n    \"kill_timeout\" : 2000,\n    \"listen_timeout\": 1000\n  }]\n}\n"
  },
  {
    "path": "examples/wait-ready/http-simple.js",
    "content": "var http = require('http');\n\nvar server = http.createServer(function(req, res) {\n  res.writeHead(200);\n  res.end('hey');\n}).listen(process.env.PORT || 8000, function() {\n  console.log('App listening on port %d in env %s', process.env.PORT || 8000, process.env.NODE_ENV);\n\n  // 1# Notify application ready\n  setTimeout(function() {\n    process.send('ready');\n  }, 2000);\n\n});\n\n// // 2# Handle on Exit\nprocess.on('SIGINT', function() {\n  console.log('Cleanup on exit');\n\n  server.on('close', function() {\n    console.log('Connections closed');\n    process.exit(0);\n  });\n\n  server.close();\n});\n"
  },
  {
    "path": "index.js",
    "content": "/**\n * Copyright 2013-2022 the PM2 project authors. All rights reserved.\n * Use of this source code is governed by a license that\n * can be found in the LICENSE file.\n */\n\nprocess.env.PM2_PROGRAMMATIC = 'true';\n\nvar API = require('./lib/API.js');\n\nmodule.exports = new API;\nmodule.exports.custom = API;\n"
  },
  {
    "path": "lib/API/Configuration.js",
    "content": "\nvar Common               = require('../Common.js');\nvar cst                  = require('../../constants.js');\nvar UX                   = require('./UX');\nvar chalk                = require('ansis');\nvar Configuration        = require('../Configuration.js');\n\nmodule.exports = function(CLI) {\n\n  CLI.prototype.get = function(key, cb) {\n    var that = this;\n\n    if (!key || key == 'all') {\n      displayConf(function(err, data) {\n        if (err)\n          return cb ? cb(Common.retErr(err)) : that.exitCli(cst.ERROR_EXIT);\n        return cb ? cb(null, {success:true}) : that.exitCli(cst.SUCCESS_EXIT);\n      });\n      return false;\n    }\n    Configuration.get(key, function(err, data) {\n      if (err) {\n        return cb ? cb(Common.retErr(err)) : that.exitCli(cst.ERROR_EXIT);\n      }\n      // pm2 conf module-name\n      if (key.indexOf(':') === -1 && key.indexOf('.') === -1) {\n        displayConf(key, function() {\n          console.log('Modules configuration. Copy/Paste line to edit values.')\n          return cb ? cb(null, {success:true}) : that.exitCli(cst.SUCCESS_EXIT)\n        });\n        return false;\n      }\n      // pm2 conf module-name:key\n      var module_name, key_name;\n\n      if (key.indexOf(':') > -1) {\n        module_name = key.split(':')[0];\n        key_name    = key.split(':')[1];\n      } else if (key.indexOf('.') > -1) {\n        module_name = key.split('.')[0];\n        key_name    = key.split('.')[1];\n      }\n\n      Common.printOut('Value for module ' + chalk.blue(module_name), 'key ' + chalk.blue(key_name) + ': ' + chalk.bold.green(data));\n\n\n      return cb ? cb(null, {success:true}) : that.exitCli(cst.SUCCESS_EXIT);\n    });\n  };\n\n  CLI.prototype.set = function(key, value, cb) {\n    var that = this;\n\n    if (!key) {\n      interactiveConfigEdit(function(err) {\n        if (err)\n          return cb ? cb(Common.retErr(err)) : that.exitCli(cst.ERROR_EXIT);\n        return cb ? cb(null, {success:true}) : that.exitCli(cst.SUCCESS_EXIT);\n      });\n      return false;\n    }\n\n    /**\n     * Set value\n     */\n    Configuration.set(key, value, function(err) {\n      if (err)\n        return cb ? cb(Common.retErr(err)) : that.exitCli(cst.ERROR_EXIT);\n\n      var values = [];\n\n      if (key.indexOf('.') > -1)\n        values = key.split('.');\n\n      if (key.indexOf(':') > -1)\n        values = key.split(':');\n\n      if (values && values.length > 1) {\n        // The first element is the app name (module_conf.json)\n        var app_name = values[0];\n\n        process.env.PM2_PROGRAMMATIC = 'true';\n        that.restart(app_name, {\n          updateEnv : true\n        }, function(err, data) {\n          process.env.PM2_PROGRAMMATIC = 'false';\n          if (!err)\n            Common.printOut(cst.PREFIX_MSG + 'Module %s restarted', app_name);\n          Common.log('Setting changed')\n          displayConf(app_name, function() {\n            return cb ? cb(null, {success:true}) : that.exitCli(cst.SUCCESS_EXIT);\n          });\n        });\n        return false;\n      }\n      displayConf(null, function() {\n        return cb ? cb(null, {success:true}) : that.exitCli(cst.SUCCESS_EXIT);\n      });\n    });\n  };\n\n  CLI.prototype.multiset = function(serial, cb) {\n    var that = this;\n\n    Configuration.multiset(serial, function(err, data) {\n      if (err)\n        return cb ? cb({success:false, err:err}) : that.exitCli(cst.ERROR_EXIT);\n\n      var values = [];\n      var key = serial.match(/(?:[^ \"]+|\"[^\"]*\")+/g)[0];\n\n      if (key.indexOf('.') > -1)\n        values = key.split('.');\n\n      if (key.indexOf(':') > -1)\n        values = key.split(':');\n\n      if (values && values.length > 1) {\n        // The first element is the app name (module_conf.json)\n        var app_name = values[0];\n\n        process.env.PM2_PROGRAMMATIC = 'true';\n        that.restart(app_name, {\n          updateEnv : true\n        }, function(err, data) {\n          process.env.PM2_PROGRAMMATIC = 'false';\n          if (!err)\n            Common.printOut(cst.PREFIX_MSG + 'Module %s restarted', app_name);\n          displayConf(app_name, function() {\n            return cb ? cb(null, {success:true}) : that.exitCli(cst.SUCCESS_EXIT)\n          });\n        });\n        return false;\n      }\n      displayConf(app_name, function() {\n        return cb ? cb(null, {success:true}) : that.exitCli(cst.SUCCESS_EXIT)\n      });\n    });\n  };\n\n  CLI.prototype.unset = function(key, cb) {\n    var that = this;\n\n    Configuration.unset(key, function(err) {\n      if (err) {\n        return cb ? cb(Common.retErr(err)) : that.exitCli(cst.ERROR_EXIT);\n      }\n\n      displayConf(function() { cb ? cb(null, {success:true}) : that.exitCli(cst.SUCCESS_EXIT) });\n    });\n  };\n\n  CLI.prototype.conf = function(key, value, cb) {\n    var that = this;\n\n    if (typeof(value) === 'function') {\n      cb = value;\n      value = null;\n    }\n\n    // If key + value = set\n    if (key && value) {\n      that.set(key, value, function(err) {\n        if (err)\n          return cb ? cb(Common.retErr(err)) : that.exitCli(cst.ERROR_EXIT);\n        return cb ? cb(null, {success:true}) : that.exitCli(cst.SUCCESS_EXIT);\n      });\n    }\n    // If only key = get\n    else if (key) {\n      that.get(key, function(err, data) {\n        if (err)\n          return cb ? cb(Common.retErr(err)) : that.exitCli(cst.ERROR_EXIT);\n        return cb ? cb(null, {success:true}) : that.exitCli(cst.SUCCESS_EXIT);\n      });\n    }\n    else {\n      interactiveConfigEdit(function(err) {\n        if (err)\n          return cb ? cb(Common.retErr(err)) : that.exitCli(cst.ERROR_EXIT);\n        return cb ? cb(null, {success:true}) : that.exitCli(cst.SUCCESS_EXIT);\n      });\n    }\n  };\n\n};\n\nfunction interactiveConfigEdit(cb) {\n  UX.helpers.openEditor(cst.PM2_MODULE_CONF_FILE, function(err, data) {\n    Common.printOut(chalk.bold('Module configuration (%s) edited.'), cst.PM2_MODULE_CONF_FILE);\n    Common.printOut(chalk.bold('To take changes into account, please restart module related.'), cst.PM2_MODULE_CONF_FILE);\n    if (err)\n      return cb(Common.retErr(err));\n    return cb(null, {success:true});\n  });\n\n}\n\n/**\n * Configuration\n */\nfunction displayConf(target_app, cb) {\n  if (typeof(target_app) == 'function') {\n    cb = target_app;\n    target_app = null;\n  }\n\n  Configuration.getAll(function(err, data) {\n    UX.helpers.dispKeys(data, target_app);\n    return cb();\n  });\n}\n"
  },
  {
    "path": "lib/API/Containerizer.js",
    "content": "\nvar spawn   = require('child_process').spawn;\nvar exec    = require('child_process').exec;\nvar chalk   = require('ansis');\nvar util    = require('util');\nvar fmt     = require('../tools/fmt.js');\nvar fs      = require('fs');\nvar path    = require('path');\nvar cst     = require('../../constants.js');\nvar Promise = require('../tools/promise.min.js');\n\nfunction pspawn(cmd) {\n  return new Promise(function(resolve, reject) {\n    var p_cmd = cmd.split(' ');\n\n    var install_instance = spawn(p_cmd[0], p_cmd.splice(1, cmd.length), {\n      stdio : 'inherit',\n      env : process.env,\n      shell : true\n    });\n\n    install_instance.on('close', function(code) {\n      if (code != 0) {\n        console.log(chalk.bold.red('Command failed'));\n        return reject(new Error('Bad cmd return'));\n      }\n      return resolve();\n    });\n\n    install_instance.on('error', function (err) {\n      return reject(err);\n    });\n  });\n}\n\nfunction checkDockerSetup() {\n  return new Promise(function(resolve, reject) {\n    exec(\"docker version -f '{{.Client.Version}}'\", function(err, stdout, stderr) {\n      if (err) {\n        console.error(chalk.red.bold('[Docker access] Error while trying to use docker command'));\n        if (err.message && err.message.indexOf('Cannot connect to the Docker') > -1) {\n          console.log();\n          console.log(chalk.blue.bold('[Solution] Setup Docker to be able to be used without sudo rights:'));\n          console.log(chalk.bold('$ sudo groupadd docker'));\n          console.log(chalk.bold('$ sudo usermod -aG docker $USER'));\n          console.log(chalk.bold('Then LOGOUT and LOGIN your Linux session'));\n          console.log('Read more: http://bit.ly/29JGdCE');\n        }\n        return reject(err);\n      }\n      return resolve();\n    });\n  });\n}\n\n/**\n * Switch Dockerfile mode\n * check test/programmatic/containerizer.mocha.js\n */\nfunction parseAndSwitch(file_content, main_file, opts) {\n  var lines = file_content.split('\\n');\n  var mode = opts.mode;\n\n  lines[0] = 'FROM keymetrics/pm2:' + opts.node_version;\n\n  for (var i = 0; i < lines.length; i++) {\n    var line = lines[i];\n\n    if (['## DISTRIBUTION MODE', '## DEVELOPMENT MODE'].indexOf(line) > -1 ||\n        i == lines.length - 1) {\n      lines.splice(i, lines.length);\n      lines[i] = '## ' + mode.toUpperCase() + ' MODE';\n      lines[i + 1] = 'ENV NODE_ENV=' + (mode == 'distribution' ? 'production' : mode);\n\n      if (mode == 'distribution') {\n        lines[i + 2] = 'COPY . /var/app';\n        lines[i + 3] = 'CMD [\"pm2-docker\", \"' + main_file + '\", \"--env\", \"production\"]';\n      }\n      if (mode == 'development') {\n        lines[i + 2] = 'CMD [\"pm2-dev\", \"' + main_file + '\", \"--env\", \"development\"]';\n      }\n      break;\n    }\n  };\n  lines = lines.join('\\n');\n  return lines;\n};\n\n/**\n * Replace ENV, COPY and CMD depending on the mode\n * @param {String} docker_filepath Dockerfile absolute path\n * @param {String} main_file       Main file to start in container\n * @param {String} mode            Mode to switch the Dockerfile\n */\nfunction switchDockerFile(docker_filepath, main_file, opts) {\n  return new Promise(function(resolve, reject) {\n    var data  = fs.readFileSync(docker_filepath, 'utf8').toString();\n\n    if (['distribution', 'development'].indexOf(opts.mode) == -1)\n      return reject(new Error('Unknown mode'));\n\n    var lines = parseAndSwitch(data, main_file, opts)\n    fs.writeFile(docker_filepath, lines, function(err) {\n      if (err) return reject(err);\n      resolve({\n        Dockerfile_path : docker_filepath,\n        Dockerfile : lines,\n        CMD : ''\n      });\n    })\n  });\n}\n\n/**\n * Generate sample Dockerfile (lib/templates/Dockerfiles)\n * @param {String} docker_filepath Dockerfile absolute path\n * @param {String} main_file       Main file to start in container\n * @param {String} mode            Mode to switch the Dockerfile\n */\nfunction generateDockerfile(docker_filepath, main_file, opts) {\n  return new Promise(function(resolve, reject) {\n    var tpl_file = path.join(cst.TEMPLATE_FOLDER, cst.DOCKERFILE_NODEJS);\n    var template = fs.readFileSync(tpl_file, {encoding: 'utf8'});\n    var CMD;\n\n    template = parseAndSwitch(template, main_file, opts);\n\n    fs.writeFile(docker_filepath, template, function(err) {\n      if (err) return reject(err);\n      resolve({\n        Dockerfile_path : docker_filepath,\n        Dockerfile : template,\n        CMD : CMD\n      });\n    });\n  });\n}\n\nfunction handleExit(CLI, opts, mode) {\n  process.on('SIGINT', function() {\n    CLI.disconnect();\n\n    if (mode != 'distribution')\n      return false;\n\n    exec('docker ps -lq', function(err, stdout, stderr) {\n      if (err) {\n        console.error(err);\n      }\n      require('vizion').analyze({folder : process.cwd()}, function recur_path(err, meta){\n        if (!err && meta.revision) {\n          var commit_id = util.format('#%s(%s) %s',\n                                      meta.branch,\n                                      meta.revision.slice(0, 5),\n                                      meta.comment);\n\n          console.log(chalk.bold.magenta('$ docker commit -m \"%s\" %s %s'),\n                      commit_id,\n                      stdout.replace('\\n', ''),\n                      opts.imageName);\n        }\n        else\n          console.log(chalk.bold.magenta('$ docker commit %s %s'), stdout.replace('\\n', ''), opts.imageName);\n\n        console.log(chalk.bold.magenta('$ docker push %s'), opts.imageName);\n      });\n    });\n  });\n}\n\nmodule.exports = function(CLI) {\n  CLI.prototype.generateDockerfile = function(script, opts) {\n    var docker_filepath = path.join(process.cwd(), 'Dockerfile');\n    var that = this;\n\n    fs.stat(docker_filepath, function(err, stat) {\n      if (err || opts.force == true) {\n        generateDockerfile(docker_filepath, script, {\n          mode : 'development'\n        })\n          .then(function() {\n            console.log(chalk.bold('New Dockerfile generated in current folder'));\n            console.log(chalk.bold('You can now run\\n$ pm2 docker:dev <file|config>'));\n            return that.exitCli(cst.SUCCESS_EXIT);\n          });\n        return false;\n      }\n      console.log(chalk.red.bold('Dockerfile already exists in this folder, use --force if you want to replace it'));\n      that.exitCli(cst.ERROR_EXIT);\n    });\n  };\n\n  CLI.prototype.dockerMode = function(script, opts, mode) {\n    var promptly = require('promptly');\n    var self = this;\n    handleExit(self, opts, mode);\n\n    if (mode == 'distribution' && !opts.imageName) {\n      console.error(chalk.bold.red('--image-name [name] option is missing'));\n      return self.exitCli(cst.ERROR_EXIT);\n    }\n\n    var template;\n    var app_path, main_script;\n    var image_name;\n    var node_version = opts.nodeVersion ? opts.nodeVersion.split('.')[0] : 'latest';\n\n    image_name   = opts.imageName || require('crypto').randomBytes(6).toString('hex');\n\n    if (script.indexOf('/') > -1) {\n      app_path  = path.join(process.cwd(), path.dirname(script));\n      main_script = path.basename(script);\n    }\n    else {\n      app_path  = process.cwd();\n      main_script = script;\n    }\n\n    checkDockerSetup()\n      .then(function() {\n        /////////////////////////\n        // Generate Dockerfile //\n        /////////////////////////\n        return new Promise(function(resolve, reject) {\n          var docker_filepath = path.join(process.cwd(), 'Dockerfile');\n\n          fs.stat(docker_filepath, function(err, stat) {\n            if (err) {\n              // Dockerfile does not exist, generate one\n              // console.log(chalk.blue.bold('Generating new Dockerfile'));\n              if (opts.force == true) {\n                return resolve(generateDockerfile(docker_filepath, main_script, {\n                  node_version : node_version,\n                  mode : mode\n                }));\n              }\n              if (opts.dockerdaemon)\n                return resolve(generateDockerfile(docker_filepath, main_script, {\n                    node_version : node_version,\n                    mode : mode\n                  }));\n              promptly.prompt('No Dockerfile in current directory, ok to generate a new one? (y/n)', function(err, value) {\n                if (value == 'y')\n                  return resolve(generateDockerfile(docker_filepath, main_script, {\n                    node_version : node_version,\n                    mode : mode\n                  }));\n                else\n                  return self.exitCli(cst.SUCCESS_EXIT);\n              });\n              return false;\n            }\n            return resolve(switchDockerFile(docker_filepath, main_script, {\n              node_version : node_version,\n              mode : mode\n            }));\n          });\n        });\n      })\n      .then(function(_template) {\n        template = _template;\n        return Promise.resolve();\n      })\n      .then(function() {\n        //////////////////\n        // Docker build //\n        //////////////////\n\n        var docker_build = util.format('docker build -t %s -f %s',\n                                       image_name,\n                                       template.Dockerfile_path);\n\n        if (opts.fresh == true)\n          docker_build += ' --no-cache';\n        docker_build += ' .';\n\n        console.log();\n        fmt.sep();\n        fmt.title('Building Boot System');\n        fmt.field('Type', chalk.cyan.bold('Docker'));\n        fmt.field('Mode', mode);\n        fmt.field('Image name', image_name);\n        fmt.field('Docker build command', docker_build);\n        fmt.field('Dockerfile path', template.Dockerfile_path);\n        fmt.sep();\n\n        return pspawn(docker_build);\n      })\n      .then(function() {\n        ////////////////\n        // Docker run //\n        ////////////////\n\n        var docker_run = 'docker run --net host';\n\n        if (opts.dockerdaemon == true)\n          docker_run += ' -d';\n        if (mode != 'distribution')\n          docker_run += util.format(' -v %s:/var/app -v /var/app/node_modules', app_path);\n        docker_run += ' ' + image_name;\n        var dockerfile_parsed = template.Dockerfile.split('\\n');\n        var base_image = dockerfile_parsed[0];\n        var run_cmd = dockerfile_parsed[dockerfile_parsed.length - 1];\n\n        console.log();\n        fmt.sep();\n        fmt.title('Booting');\n        fmt.field('Type', chalk.cyan.bold('Docker'));\n        fmt.field('Mode', mode);\n        fmt.field('Base Image', base_image);\n        fmt.field('Image Name', image_name);\n        fmt.field('Docker Command', docker_run);\n        fmt.field('RUN Command', run_cmd);\n        fmt.field('CWD', app_path);\n        fmt.sep();\n        return pspawn(docker_run);\n      })\n      .then(function() {\n        console.log(chalk.blue.bold('>>> Leaving Docker instance uuid=%s'), image_name);\n        self.disconnect();\n        return Promise.resolve();\n      })\n      .catch(function(err) {\n        console.log();\n        console.log(chalk.gray('Raw error=', err.message));\n        self.disconnect();\n      });\n\n  };\n\n};\n\nmodule.exports.generateDockerfile = generateDockerfile;\nmodule.exports.parseAndSwitch     = parseAndSwitch;\nmodule.exports.switchDockerFile   = switchDockerFile;\n"
  },
  {
    "path": "lib/API/Dashboard.js",
    "content": "/**\n * Copyright 2013-2022 the PM2 project authors. All rights reserved.\n * Use of this source code is governed by a license that\n * can be found in the LICENSE file.\n */\n\nvar os         = require('os');\nvar p          = require('path');\nvar blessed    = require('@pm2/blessed');\nvar debug      = require('debug')('pm2:monit');\nvar printf     = require('sprintf-js').sprintf;\n\n// Total memory\nconst totalMem = os.totalmem();\n\nvar Dashboard = {};\n\nvar DEFAULT_PADDING = {\n  top : 0,\n  left : 1,\n  right : 1\n};\n\nvar WIDTH_LEFT_PANEL = 30;\n\n/**\n * Synchronous Dashboard init method\n * @method init\n * @return this\n */\nDashboard.init = function() {\n  // Init Screen\n  this.screen = blessed.screen({\n    smartCSR: true,\n    fullUnicode: true\n  });\n  this.screen.title = 'PM2 Dashboard';\n\n  this.logLines = {}\n\n  this.list = blessed.list({\n    top: '0',\n    left: '0',\n    width: WIDTH_LEFT_PANEL + '%',\n    height: '70%',\n    padding: 0,\n    scrollbar: {\n      ch: ' ',\n      inverse: false\n    },\n    border: {\n      type: 'line'\n    },\n    keys: true,\n    autoCommandKeys: true,\n    tags: true,\n    style: {\n      selected: {\n        bg: 'blue',\n        fg: 'white'\n      },\n      scrollbar: {\n        bg: 'blue',\n        fg: 'black'\n      },\n      fg: 'white',\n      border: {\n        fg: 'blue'\n      },\n      header: {\n        fg: 'blue'\n      }\n    }\n  });\n\n  this.list.on('select item', (item, i) => {\n    this.logBox.clearItems()\n  })\n\n  this.logBox = blessed.list({\n    label: ' Logs ',\n    top: '0',\n    left: WIDTH_LEFT_PANEL + '%',\n    width: 100 - WIDTH_LEFT_PANEL + '%',\n    height: '70%',\n    padding: DEFAULT_PADDING,\n    scrollable: true,\n    scrollbar: {\n      ch: ' ',\n      inverse: false\n    },\n    keys: true,\n    autoCommandKeys: true,\n    tags: true,\n    border: {\n      type: 'line'\n    },\n    style: {\n      fg: 'white',\n      border: {\n        fg: 'white'\n      },\n      scrollbar: {\n        bg: 'blue',\n        fg: 'black'\n      }\n    }\n  });\n\n  this.metadataBox = blessed.box({\n    label: ' Metadata ',\n    top: '70%',\n    left: WIDTH_LEFT_PANEL + '%',\n    width: 100 - WIDTH_LEFT_PANEL + '%',\n    height: '26%',\n    padding: DEFAULT_PADDING,\n    scrollable: true,\n    scrollbar: {\n      ch: ' ',\n      inverse: false\n    },\n    keys: true,\n    autoCommandKeys: true,\n    tags: true,\n    border: {\n      type: 'line'\n    },\n    style: {\n      fg: 'white',\n      border: {\n        fg: 'white'\n      },\n      scrollbar: {\n        bg: 'blue',\n        fg: 'black'\n      }\n    }\n  });\n\n  this.metricsBox = blessed.list({\n    label: ' Custom Metrics ',\n    top: '70%',\n    left: '0%',\n    width: WIDTH_LEFT_PANEL + '%',\n    height: '26%',\n    padding: DEFAULT_PADDING,\n    scrollbar: {\n      ch: ' ',\n      inverse: false\n    },\n    keys: true,\n    autoCommandKeys: true,\n    tags: true,\n    border: {\n      type: 'line'\n    },\n    style: {\n      fg: 'white',\n      border: {\n        fg: 'white'\n      },\n      scrollbar: {\n        bg: 'blue',\n        fg: 'black'\n      }\n    }\n  });\n\n  this.box4 = blessed.text({\n    content: ' left/right: switch boards | up/down/mouse: scroll | Ctrl-C: exit{|} {cyan-fg}{bold}To go further check out https://pm2.io/{/}  ',\n    left: '0%',\n    top: '95%',\n    width: '100%',\n    height: '6%',\n    valign: 'middle',\n    tags: true,\n    style: {\n      fg: 'white'\n    }\n  });\n\n  this.list.focus();\n\n  this.screen.append(this.list);\n  this.screen.append(this.logBox);\n  this.screen.append(this.metadataBox);\n  this.screen.append(this.metricsBox);\n  this.screen.append(this.box4);\n\n  this.list.setLabel(' Process List ');\n\n  this.screen.render();\n\n  var that = this;\n\n  var i = 0;\n  var boards = ['list', 'logBox', 'metricsBox', 'metadataBox'];\n  this.screen.key(['left', 'right'], function(ch, key) {\n    (key.name === 'left') ? i-- : i++;\n    if (i == 4)\n      i = 0;\n    if (i == -1)\n      i = 3;\n    that[boards[i]].focus();\n    that[boards[i]].style.border.fg = 'blue';\n    if (key.name === 'left') {\n      if (i == 3)\n        that[boards[0]].style.border.fg = 'white';\n      else\n        that[boards[i + 1]].style.border.fg = 'white';\n    }\n    else {\n       if (i == 0)\n        that[boards[3]].style.border.fg = 'white';\n      else\n        that[boards[i - 1]].style.border.fg = 'white';\n    }\n  });\n\n  this.screen.key(['escape', 'q', 'C-c'], function(ch, key) {\n    this.screen.destroy();\n    process.exit(0);\n  });\n\n  // async refresh of the ui\n  setInterval(function () {\n    that.screen.render();\n  }, 300);\n\n  return this;\n}\n\n/**\n * Refresh dashboard\n * @method refresh\n * @param {} processes\n * @return this\n */\nDashboard.refresh = function(processes) {\n  debug('Monit refresh');\n\n  if(!processes) {\n    this.list.setItem(0, 'No process available');\n    return;\n  }\n\n  if (processes.length != this.list.items.length) {\n    this.list.clearItems();\n  }\n\n  // Total of processes memory\n  var mem = 0;\n  processes.forEach(function(proc) {\n    mem += proc.monit.memory;\n  })\n\n  // Sort process list\n  processes.sort(function(a, b) {\n    if (a.pm2_env.name < b.pm2_env.name)\n      return -1;\n    if (a.pm2_env.name > b.pm2_env.name)\n      return 1;\n    return 0;\n  });\n\n  // Loop to get process infos\n  for (var i = 0; i < processes.length; i++) {\n    // Percent of memory use by one process in all pm2 processes\n    var memPercent = (processes[i].monit.memory / mem) * 100;\n\n    // Status of process\n    var status = processes[i].pm2_env.status == 'online' ? '{green-fg}' : '{red-fg}';\n    status = status + '{bold}' + processes[i].pm2_env.status + '{/}';\n\n    var name = processes[i].pm2_env.name || p.basename(processes[i].pm2_env.pm_exec_path);\n\n    // Line of list\n    var item = printf('[%2s] %s {|} Mem: {bold}{%s-fg}%3d{/} MB    CPU: {bold}{%s-fg}%2d{/} %s  %s',\n                       processes[i].pm2_env.pm_id,\n                       name,\n                       gradient(memPercent, [255, 0, 0], [0, 255, 0]),\n                       (processes[i].monit.memory / 1048576).toFixed(2),\n                       gradient(processes[i].monit.cpu, [255, 0, 0], [0, 255, 0]),\n                       processes[i].monit.cpu,\n                       \"%\",\n                       status);\n\n    // Check if item exist\n    if (this.list.getItem(i)) {\n      this.list.setItem(i, item);\n    }\n    else {\n      this.list.pushItem(item);\n    }\n\n    var proc = processes[this.list.selected];\n    // render the logBox\n    let process_id = proc.pm_id\n    let logs = this.logLines[process_id];\n    if(typeof(logs) !== \"undefined\"){\n      this.logBox.setItems(logs)\n      if (!this.logBox.focused) {\n          this.logBox.setScrollPerc(100);\n      }\n    }else{\n      this.logBox.clearItems();\n    }\n    this.logBox.setLabel(`  ${proc.pm2_env.name} Logs  `)\n\n    this.metadataBox.setLine(0, 'App Name              ' + '{bold}' + proc.pm2_env.name + '{/}');\n    this.metadataBox.setLine(1, 'Namespace             ' + '{bold}' + proc.pm2_env.namespace + '{/}');\n    this.metadataBox.setLine(2, 'Version               ' + '{bold}' + proc.pm2_env.version + '{/}');\n    this.metadataBox.setLine(3, 'Restarts              ' + proc.pm2_env.restart_time);\n    this.metadataBox.setLine(4, 'Uptime                ' + ((proc.pm2_env.pm_uptime && proc.pm2_env.status == 'online') ? timeSince(proc.pm2_env.pm_uptime) : 0));\n    this.metadataBox.setLine(5, 'Script path           ' + proc.pm2_env.pm_exec_path);\n    this.metadataBox.setLine(6, 'Script args           ' + (proc.pm2_env.args ? (typeof proc.pm2_env.args == 'string' ? JSON.parse(proc.pm2_env.args.replace(/'/g, '\"')):proc.pm2_env.args).join(' ') : 'N/A'));\n    this.metadataBox.setLine(7, 'Interpreter           ' + proc.pm2_env.exec_interpreter);\n    this.metadataBox.setLine(8, 'Interpreter args      ' + (proc.pm2_env.node_args.length != 0 ? proc.pm2_env.node_args : 'N/A'));\n    this.metadataBox.setLine(9, 'Exec mode             ' + (proc.pm2_env.exec_mode == 'fork_mode' ? '{bold}fork{/}' : '{blue-fg}{bold}cluster{/}'));\n    this.metadataBox.setLine(10, 'Node.js version       ' + proc.pm2_env.node_version);\n    this.metadataBox.setLine(11, 'watch & reload        ' + (proc.pm2_env.watch ? '{green-fg}{bold}✔{/}' : '{red-fg}{bold}✘{/}'));\n    this.metadataBox.setLine(12, 'Unstable restarts     ' + proc.pm2_env.unstable_restarts);\n\n    this.metadataBox.setLine(13, 'Comment               ' + ((proc.pm2_env.versioning) ? proc.pm2_env.versioning.comment : 'N/A'));\n    this.metadataBox.setLine(14, 'Revision              ' + ((proc.pm2_env.versioning) ? proc.pm2_env.versioning.revision : 'N/A'));\n    this.metadataBox.setLine(15, 'Branch                ' + ((proc.pm2_env.versioning) ? proc.pm2_env.versioning.branch : 'N/A'));\n    this.metadataBox.setLine(16, 'Remote url            ' + ((proc.pm2_env.versioning) ? proc.pm2_env.versioning.url : 'N/A'));\n    this.metadataBox.deleteLine(17)\n    this.metadataBox.setLine(17, 'Last update           ' + ((proc.pm2_env.versioning) ? proc.pm2_env.versioning.update_time : 'N/A'));\n\n    if (Object.keys(proc.pm2_env.axm_monitor).length != this.metricsBox.items.length) {\n      this.metricsBox.clearItems();\n    }\n    var j = 0;\n    for (var key in proc.pm2_env.axm_monitor) {\n      var metric_name = proc.pm2_env.axm_monitor[key].hasOwnProperty('value') ? proc.pm2_env.axm_monitor[key].value : proc.pm2_env.axm_monitor[key]\n      var metric_unit = proc.pm2_env.axm_monitor[key].hasOwnProperty('unit') ? proc.pm2_env.axm_monitor[key].unit : null\n      var probe = `{bold}${key}{/} {|} ${metric_name}${metric_unit == null ? '' : ' ' + metric_unit}`\n\n      if (this.metricsBox.getItem(j)) {\n        this.metricsBox.setItem(j, probe);\n      }\n      else {\n        this.metricsBox.pushItem(probe);\n      }\n      j++;\n    }\n\n    this.screen.render();\n  }\n\n  return this;\n}\n\n/**\n * Put Log\n * @method log\n * @param {} data\n * @return this\n */\nDashboard.log = function(type, data) {\n  var that = this;\n\n  if(typeof(this.logLines[data.process.pm_id]) == \"undefined\"){\n    this.logLines[data.process.pm_id]=[];\n  }\n  // Logs colors\n  switch (type) {\n    case 'PM2':\n      var color = '{blue-fg}';\n      break;\n    case 'out':\n      var color = '{green-fg}';\n      break;\n    case 'err':\n      var color = '{red-fg}';\n      break;\n    default:\n      var color = '{white-fg}';\n  }\n\n  var logs = data.data.split('\\n')\n\n  logs.forEach((log) => {\n    if (log.length > 0) {\n      this.logLines[data.process.pm_id].push(color + data.process.name + '{/} > ' + log)\n\n\n      //removing logs if longer than limit\n      let count = 0;\n      let max_count = 0;\n      let leading_process_id = -1;\n\n      for(var process_id in this.logLines){\n        count += this.logLines[process_id].length;\n        if( this.logLines[process_id].length > max_count){\n          leading_process_id = process_id;\n          max_count = this.logLines[process_id].length;\n        }\n      }\n\n      if (count > 200) {\n        this.logLines[leading_process_id].shift()\n      }\n    }\n  })\n\n  return this;\n}\n\nmodule.exports = Dashboard;\n\nfunction timeSince(date) {\n\n  var seconds = Math.floor((new Date() - date) / 1000);\n\n  var interval = Math.floor(seconds / 31536000);\n\n  if (interval > 1) {\n    return interval + 'Y';\n  }\n  interval = Math.floor(seconds / 2592000);\n  if (interval > 1) {\n    return interval + 'M';\n  }\n  interval = Math.floor(seconds / 86400);\n  if (interval > 1) {\n    return interval + 'D';\n  }\n  interval = Math.floor(seconds / 3600);\n  if (interval > 1) {\n    return interval + 'h';\n  }\n  interval = Math.floor(seconds / 60);\n  if (interval > 1) {\n    return interval + 'm';\n  }\n  return Math.floor(seconds) + 's';\n}\n\n/* Args :\n *  p : Percent 0 - 100\n *  rgb_ : Array of rgb [255, 255, 255]\n * Return :\n *  Hexa #FFFFFF\n */\nfunction gradient(p, rgb_beginning, rgb_end) {\n\n    var w = (p / 100) * 2 - 1;\n\n    var w1 = (w + 1) / 2.0;\n    var w2 = 1 - w1;\n\n    var rgb = [parseInt(rgb_beginning[0] * w1 + rgb_end[0] * w2),\n        parseInt(rgb_beginning[1] * w1 + rgb_end[1] * w2),\n            parseInt(rgb_beginning[2] * w1 + rgb_end[2] * w2)];\n\n    return \"#\" + ((1 << 24) + (rgb[0] << 16) + (rgb[1] << 8) + rgb[2]).toString(16).slice(1);\n}\n"
  },
  {
    "path": "lib/API/Deploy.js",
    "content": "/**\n * Copyright 2013-2022 the PM2 project authors. All rights reserved.\n * Use of this source code is governed by a license that\n * can be found in the LICENSE file.\n */\n\nvar fs      = require('fs');\nvar cst     = require('../../constants.js');\nvar Utility = require('../Utility.js');\nvar Common  = require('../Common.js');\n\nfunction deployHelper() {\n  console.log('');\n  console.log('-----> Helper: Deployment with PM2');\n  console.log('');\n  console.log('  Generate a sample ecosystem.config.js with the command');\n  console.log('  $ pm2 ecosystem');\n  console.log('  Then edit the file depending on your needs');\n  console.log('');\n  console.log('  Commands:');\n  console.log('    setup                run remote setup commands');\n  console.log('    update               update deploy to the latest release');\n  console.log('    revert [n]           revert to [n]th last deployment or 1');\n  console.log('    curr[ent]            output current release commit');\n  console.log('    prev[ious]           output previous release commit');\n  console.log('    exec|run <cmd>       execute the given <cmd>');\n  console.log('    list                 list previous deploy commits');\n  console.log('    [ref]                deploy to [ref], the \"ref\" setting, or latest tag');\n  console.log('');\n  console.log('');\n  console.log('  Basic Examples:');\n  console.log('');\n  console.log('    First initialize remote production host:');\n  console.log('    $ pm2 deploy ecosystem.config.js production setup');\n  console.log('');\n  console.log('    Then deploy new code:');\n  console.log('    $ pm2 deploy ecosystem.config.js production');\n  console.log('');\n  console.log('    If I want to revert to the previous commit:');\n  console.log('    $ pm2 deploy ecosystem.config.js production revert 1');\n  console.log('');\n  console.log('    Execute a command on remote server:');\n  console.log('    $ pm2 deploy ecosystem.config.js production exec \"pm2 restart all\"');\n  console.log('');\n  console.log('    PM2 will look by default to the ecosystem.config.js file so you dont need to give the file name:');\n  console.log('    $ pm2 deploy production');\n  console.log('    Else you have to tell PM2 the name of your ecosystem file');\n  console.log('');\n  console.log('    More examples in https://github.com/Unitech/pm2');\n  console.log('');\n};\n\nmodule.exports = function(CLI) {\n  CLI.prototype.deploy = function(file, commands, cb) {\n    var that = this;\n\n    if (file == 'help') {\n      deployHelper();\n      return cb ? cb() : that.exitCli(cst.SUCCESS_EXIT);\n    }\n\n    var args = commands.rawArgs;\n    var env;\n\n    args.splice(0, args.indexOf('deploy') + 1);\n\n    // Find ecosystem file by default\n    if (!Common.isConfigFile(file)) {\n      env = args[0];\n      var defaultConfigNames = [ ...Common.getConfigFileCandidates('ecosystem'), 'ecosystem.json5', 'package.json'];\n\n      file = Utility.whichFileExists(defaultConfigNames);\n\n      if (!file) {\n        Common.printError('Not any default deployment file exists.'+\n          ' Allowed default config file names are: ' + defaultConfigNames.join(', '));\n        return cb ? cb('Not any default ecosystem file present') : that.exitCli(cst.ERROR_EXIT);\n      }\n    }\n    else\n      env = args[1];\n\n    var json_conf = null;\n\n    try {\n      json_conf = Common.parseConfig(fs.readFileSync(file), file);\n    } catch (e) {\n      Common.printError(e);\n      return cb ? cb(e) : that.exitCli(cst.ERROR_EXIT);\n    }\n\n    if (!env) {\n      deployHelper();\n      return cb ? cb() : that.exitCli(cst.SUCCESS_EXIT);\n    }\n\n    if (!json_conf.deploy || !json_conf.deploy[env]) {\n      Common.printError('%s environment is not defined in %s file', env, file);\n      return cb ? cb('%s environment is not defined in %s file') : that.exitCli(cst.ERROR_EXIT);\n    }\n\n    if (!json_conf.deploy[env]['post-deploy']) {\n      json_conf.deploy[env]['post-deploy'] = 'pm2 startOrRestart ' + file + ' --env ' + env;\n    }\n\n    require('pm2-deploy').deployForEnv(json_conf.deploy, env, args, function(err, data) {\n      if (err) {\n        Common.printError('Deploy failed');\n        Common.printError(err.message || err);\n        return cb ? cb(err) : that.exitCli(cst.ERROR_EXIT);\n      }\n      Common.printOut('--> Success');\n      return cb ? cb(null, data) : that.exitCli(cst.SUCCESS_EXIT);\n    });\n  };\n\n};\n"
  },
  {
    "path": "lib/API/Extra.js",
    "content": "\n/***************************\n *\n * Extra methods\n *\n **************************/\n\nvar cst         = require('../../constants.js');\nvar Common      = require('../Common.js');\nvar UX          = require('./UX');\nvar chalk       = require('ansis');\nvar path        = require('path');\nvar fs          = require('fs');\nvar fmt         = require('../tools/fmt.js');\nvar dayjs      = require('dayjs');\nvar pkg         = require('../../package.json');\nconst copyDirSync = require('../tools/copydirSync.js')\n\nmodule.exports = function(CLI) {\n  /**\n   * Get version of the daemonized PM2\n   * @method getVersion\n   * @callback cb\n   */\n  CLI.prototype.getVersion = function(cb) {\n    var that = this;\n\n    that.Client.executeRemote('getVersion', {}, function(err) {\n      return cb ? cb.apply(null, arguments) : that.exitCli(cst.SUCCESS_EXIT);\n    });\n  };\n\n  /**\n   * Install pm2-sysmonit\n   */\n  CLI.prototype.launchSysMonitoring = function(cb) {\n    if ((this.pm2_configuration && this.pm2_configuration.sysmonit != 'true') ||\n        process.env.TRAVIS ||\n        global.it === 'function' ||\n        cst.IS_WINDOWS === true)\n      return cb ? cb(null) : null\n\n    var filepath\n\n    try {\n      filepath = path.dirname(require.resolve('pm2-sysmonit'))\n    } catch(e) {\n      return cb ? cb(null) : null\n    }\n\n    this.start({\n      script: filepath\n    }, {\n      started_as_module : true\n    }, (err, res) => {\n      if (err) {\n        Common.printError(cst.PREFIX_MSG_ERR + 'Error while trying to serve : ' + err.message || err);\n        return cb ? cb(err) : this.speedList(cst.ERROR_EXIT);\n      }\n      return cb ? cb(null) : this.speedList();\n    });\n  };\n\n  /**\n   * Show application environment\n   * @method env\n   * @callback cb\n   */\n  CLI.prototype.env = function(app_id, cb) {\n    var procs = []\n    var printed = 0\n\n    this.Client.executeRemote('getMonitorData', {}, (err, list) => {\n      list.forEach(l => {\n        if (app_id == l.pm_id) {\n          printed++\n          var env = Common.safeExtend({}, l.pm2_env)\n          Object.keys(env).forEach(key => {\n            console.log(`${key}: ${chalk.green(env[key])}`)\n          })\n        }\n      })\n\n      if (printed == 0) {\n        Common.err(`Modules with id ${app_id} not found`)\n        return cb ? cb.apply(null, arguments) : this.exitCli(cst.ERROR_EXIT);\n      }\n      return cb ? cb.apply(null, arguments) : this.exitCli(cst.SUCCESS_EXIT);\n    })\n  };\n\n  /**\n   * Get version of the daemonized PM2\n   * @method getVersion\n   * @callback cb\n   */\n  CLI.prototype.report = function() {\n    var that = this;\n\n    var Log = require('./Log');\n\n    that.Client.executeRemote('getReport', {}, function(err, report) {\n\n      console.log()\n      console.log()\n      console.log()\n      console.log('```')\n      fmt.title('PM2 report')\n      fmt.field('Date', new Date());\n      fmt.sep();\n\n      if (report && !err) {\n        fmt.title(chalk.bold.blue('Daemon'));\n        fmt.field('pm2d version', report.pm2_version);\n        fmt.field('node version', report.node_version);\n        fmt.field('node path', report.node_path);\n        fmt.field('argv', report.argv);\n        fmt.field('argv0', report.argv0);\n        fmt.field('user', report.user);\n        fmt.field('uid', report.uid);\n        fmt.field('gid', report.gid);\n        fmt.field('uptime', dayjs(new Date()).diff(report.started_at, 'minute') + 'min');\n      }\n\n      fmt.sep();\n      fmt.title(chalk.bold.blue('CLI'));\n      fmt.field('local pm2', pkg.version);\n      fmt.field('node version', process.versions.node);\n      fmt.field('node path', process.env['_'] || 'not found');\n      fmt.field('argv', process.argv);\n      fmt.field('argv0', process.argv0);\n      fmt.field('user', process.env.USER || process.env.LNAME || process.env.USERNAME);\n      if (cst.IS_WINDOWS === false && process.geteuid)\n        fmt.field('uid', process.geteuid());\n      if (cst.IS_WINDOWS === false && process.getegid)\n        fmt.field('gid', process.getegid());\n\n      var os = require('os');\n\n      fmt.sep();\n      fmt.title(chalk.bold.blue('System info'));\n      fmt.field('arch', os.arch());\n      fmt.field('platform', os.platform());\n      fmt.field('type', os.type());\n      fmt.field('cpus', os.cpus()[0].model);\n      fmt.field('cpus nb', Object.keys(os.cpus()).length);\n      fmt.field('freemem', os.freemem());\n      fmt.field('totalmem', os.totalmem());\n      fmt.field('home', os.homedir());\n\n      that.Client.executeRemote('getMonitorData', {}, function(err, list) {\n\n        fmt.sep();\n        fmt.title(chalk.bold.blue('PM2 list'));\n        UX.list(list, that.gl_interact_infos);\n\n        fmt.sep();\n        fmt.title(chalk.bold.blue('Daemon logs'));\n        Log.tail([{\n          path     : cst.PM2_LOG_FILE_PATH,\n          app_name : 'PM2',\n          type     : 'PM2'\n        }], 20, false, function() {\n          console.log('```')\n          console.log()\n          console.log()\n\n          console.log(chalk.bold.green('Please copy/paste the above report in your issue on https://github.com/Unitech/pm2/issues'));\n\n          console.log()\n          console.log()\n          that.exitCli(cst.SUCCESS_EXIT);\n        });\n      });\n    });\n  };\n\n  CLI.prototype.getPID = function(app_name, cb) {\n    var that = this;\n\n    if (typeof(app_name) === 'function') {\n      cb = app_name;\n      app_name = null;\n    }\n\n    this.Client.executeRemote('getMonitorData', {}, function(err, list) {\n      if (err) {\n        Common.printError(cst.PREFIX_MSG_ERR + err);\n        return cb ? cb(Common.retErr(err)) : that.exitCli(cst.ERROR_EXIT);\n      }\n\n      var pids = [];\n\n      list.forEach(function(app) {\n        if (!app_name || app_name == app.name)\n          pids.push(app.pid);\n      })\n\n      if (!cb) {\n        Common.printOut(pids.join(\"\\n\"))\n        return that.exitCli(cst.SUCCESS_EXIT);\n      }\n      return cb(null, pids);\n    })\n  }\n\n  /**\n   * Create PM2 memory snapshot\n   * @method getVersion\n   * @callback cb\n   */\n  CLI.prototype.profile = function(type, time, cb) {\n    var that = this;\n    var dayjs = require('dayjs');\n    var cmd\n\n    if (type == 'cpu') {\n      cmd = {\n        ext: '.cpuprofile',\n        action: 'profileCPU'\n      }\n    }\n    if (type == 'mem') {\n      cmd = {\n        ext: '.heapprofile',\n        action: 'profileMEM'\n      }\n    }\n\n    var file = path.join(process.cwd(), dayjs().format('dd-HH:mm:ss') + cmd.ext);\n    time = time || 10000\n\n    console.log(`Starting ${cmd.action} profiling for ${time}ms...`)\n    that.Client.executeRemote(cmd.action, {\n      pwd : file,\n      timeout: time\n    }, function(err) {\n      if (err) {\n        console.error(err);\n        return that.exitCli(1);\n      }\n      console.log(`Profile done in ${file}`)\n      return cb ? cb.apply(null, arguments) : that.exitCli(cst.SUCCESS_EXIT);\n    });\n  };\n\n\n  function basicMDHighlight(lines) {\n    console.log('\\n\\n+-------------------------------------+')\n    console.log(chalk.bold('README.md content:'))\n    lines = lines.split('\\n')\n    var isInner = false\n    lines.forEach(l => {\n      if (l.startsWith('#'))\n        console.log(chalk.bold.green(l))\n      else if (isInner || l.startsWith('```')) {\n        if (isInner && l.startsWith('```'))\n          isInner = false\n        else if (isInner == false)\n          isInner = true\n        console.log(chalk.gray(l))\n      }\n      else if (l.startsWith('`'))\n        console.log(chalk.gray(l))\n      else\n        console.log(l)\n    })\n    console.log('+-------------------------------------+')\n  }\n  /**\n   * pm2 create command\n   * create boilerplate of application for fast try\n   * @method boilerplate\n   */\n  CLI.prototype.boilerplate = function(cb) {\n    var i = 0\n    var projects = []\n    var enquirer = require('enquirer')\n    const forEach = require('async/forEach')\n\n    fs.readdir(path.join(__dirname, '../templates/sample-apps'), (err, items) => {\n      forEach(items, (app, next) => {\n        var fp = path.join(__dirname, '../templates/sample-apps', app)\n        fs.readFile(path.join(fp, 'package.json'), (err, dt) => {\n          var meta = JSON.parse(dt)\n          meta.fullpath = fp\n          meta.folder_name = app\n          projects.push(meta)\n          next()\n        })\n      }, () => {\n        const prompt = new enquirer.Select({\n          name: 'boilerplate',\n          message: 'Select a boilerplate',\n          choices: projects.map((p, i) => {\n            return {\n              message: `${chalk.bold.blue(p.name)} ${p.description}`,\n              value: `${i}`\n            }\n          })\n        });\n\n        prompt.run()\n          .then(answer => {\n            var p = projects[parseInt(answer)]\n            basicMDHighlight(fs.readFileSync(path.join(p.fullpath, 'README.md')).toString())\n            console.log(chalk.bold(`>> Project copied inside folder ./${p.folder_name}/\\n`))\n            copyDirSync(p.fullpath, path.join(process.cwd(), p.folder_name));\n            this.start(path.join(p.fullpath, 'ecosystem.config.js'), {\n              cwd: p.fullpath\n            }, () => {\n              return cb ? cb.apply(null, arguments) : this.speedList(cst.SUCCESS_EXIT);\n            })\n          })\n          .catch(e => {\n            return cb ? cb.apply(null, arguments) : this.speedList(cst.SUCCESS_EXIT);\n          });\n\n      })\n    })\n  }\n\n  /**\n   * Description\n   * @method sendLineToStdin\n   */\n  CLI.prototype.sendLineToStdin = function(pm_id, line, separator, cb) {\n    var that = this;\n\n    if (!cb && typeof(separator) == 'function') {\n      cb = separator;\n      separator = null;\n    }\n\n    var packet = {\n      pm_id : pm_id,\n      line : line + (separator || '\\n')\n    };\n\n    that.Client.executeRemote('sendLineToStdin', packet, function(err, res) {\n      if (err) {\n        Common.printError(cst.PREFIX_MSG_ERR + err);\n        return cb ? cb(Common.retErr(err)) : that.exitCli(cst.ERROR_EXIT);\n      }\n      return cb ? cb(null, res) : that.speedList();\n    });\n  };\n\n  /**\n   * Description\n   * @method attachToProcess\n   */\n  CLI.prototype.attach = function(pm_id, separator, cb) {\n    var that = this;\n    var readline = require('readline');\n\n    if (isNaN(pm_id)) {\n      Common.printError('pm_id must be a process number (not a process name)');\n      return cb ? cb(Common.retErr('pm_id must be number')) : that.exitCli(cst.ERROR_EXIT);\n    }\n\n    if (typeof(separator) == 'function') {\n      cb = separator;\n      separator = null;\n    }\n\n    var rl = readline.createInterface({\n      input: process.stdin,\n      output: process.stdout\n    });\n\n    rl.on('close', function() {\n      return cb ? cb() : that.exitCli(cst.SUCCESS_EXIT);\n    });\n\n    that.Client.launchBus(function(err, bus, socket) {\n      if (err) {\n        Common.printError(err);\n        return cb ? cb(Common.retErr(err)) : that.exitCli(cst.ERROR_EXIT);\n      }\n\n      bus.on('log:*', function(type, packet) {\n        if (packet.process.pm_id !== parseInt(pm_id))\n          return;\n        process.stdout.write(packet.data);\n      });\n    });\n\n    rl.on('line', function(line) {\n      that.sendLineToStdin(pm_id, line, separator, function() {});\n    });\n  };\n\n  /**\n   * Description\n   * @method sendDataToProcessId\n   */\n  CLI.prototype.sendDataToProcessId = function(proc_id, packet, cb) {\n    var that = this;\n\n    if (typeof proc_id === 'object' && typeof packet === 'function') {\n      // the proc_id is packet.\n      cb = packet;\n      packet = proc_id;\n    } else {\n      packet.id = proc_id;\n    }\n\n    that.Client.executeRemote('sendDataToProcessId', packet, function(err, res) {\n      if (err) {\n        Common.printError(err);\n        return cb ? cb(Common.retErr(err)) : that.exitCli(cst.ERROR_EXIT);\n      }\n      Common.printOut('successfully sent data to process');\n      return cb ? cb(null, res) : that.speedList();\n    });\n  };\n\n  /**\n   * Used for custom actions, allows to trigger function inside an app\n   * To expose a function you need to use keymetrics/pmx\n   *\n   * @method msgProcess\n   * @param {Object} opts\n   * @param {String} id           process id\n   * @param {String} action_name  function name to trigger\n   * @param {Object} [opts.opts]  object passed as first arg of the function\n   * @param {String} [uuid]       optional unique identifier when logs are emitted\n   *\n   */\n  CLI.prototype.msgProcess = function(opts, cb) {\n    var that = this;\n\n    that.Client.executeRemote('msgProcess', opts, cb);\n  };\n\n  /**\n   * Trigger a PMX custom action in target application\n   * Custom actions allows to interact with an application\n   *\n   * @method trigger\n   * @param  {String|Number} pm_id       process id or application name\n   * @param  {String}        action_name name of the custom action to trigger\n   * @param  {Mixed}         params      parameter to pass to target action\n   * @param  {Function}      cb          callback\n   */\n  CLI.prototype.trigger = function(pm_id, action_name, params, cb) {\n    if (typeof(params) === 'function') {\n      cb = params;\n      params = null;\n    }\n    var cmd = {\n      msg : action_name\n    };\n    var counter = 0;\n    var process_wait_count = 0;\n    var that = this;\n    var results = [];\n\n    if (params)\n      cmd.opts = params;\n    if (isNaN(pm_id))\n      cmd.name = pm_id;\n    else\n      cmd.id = pm_id;\n\n    this.launchBus(function(err, bus) {\n      bus.on('axm:reply', function(ret) {\n        if (ret.process.name == pm_id || ret.process.pm_id == pm_id || ret.process.namespace == pm_id || pm_id == 'all') {\n          results.push(ret);\n          Common.printOut('[%s:%s:%s]=%j', ret.process.name, ret.process.pm_id, ret.process.namespace, ret.data.return);\n          if (++counter == process_wait_count)\n            return cb ? cb(null, results) : that.exitCli(cst.SUCCESS_EXIT);\n        }\n      });\n\n      that.msgProcess(cmd, function(err, data) {\n        if (err) {\n          Common.printError(err);\n          return cb ? cb(Common.retErr(err)) : that.exitCli(cst.ERROR_EXIT);\n        }\n\n        if (data.process_count == 0) {\n          Common.printError('Not any process has received a command (offline or unexistent)');\n          return cb ? cb(Common.retErr('Unknown process')) : that.exitCli(cst.ERROR_EXIT);\n        }\n\n        process_wait_count = data.process_count;\n        Common.printOut(chalk.bold('%s processes have received command %s'),\n                        data.process_count, action_name);\n      });\n    });\n  };\n\n  /**\n   * Description\n   * @method sendSignalToProcessName\n   * @param {} signal\n   * @param {} process_name\n   * @return\n   */\n  CLI.prototype.sendSignalToProcessName = function(signal, process_name, cb) {\n    var that = this;\n\n    that.Client.executeRemote('sendSignalToProcessName', {\n      signal : signal,\n      process_name : process_name\n    }, function(err, list) {\n      if (err) {\n        Common.printError(err);\n        return cb ? cb(Common.retErr(err)) : that.exitCli(cst.ERROR_EXIT);\n      }\n      Common.printOut('successfully sent signal %s to process name %s', signal, process_name);\n      return cb ? cb(null, list) : that.speedList();\n    });\n  };\n\n  /**\n   * Description\n   * @method sendSignalToProcessId\n   * @param {} signal\n   * @param {} process_id\n   * @return\n   */\n  CLI.prototype.sendSignalToProcessId = function(signal, process_id, cb) {\n    var that = this;\n\n    that.Client.executeRemote('sendSignalToProcessId', {\n      signal : signal,\n      process_id : process_id\n    }, function(err, list) {\n      if (err) {\n        Common.printError(err);\n        return cb ? cb(Common.retErr(err)) : that.exitCli(cst.ERROR_EXIT);\n      }\n      Common.printOut('successfully sent signal %s to process id %s', signal, process_id);\n      return cb ? cb(null, list) : that.speedList();\n    });\n  };\n\n  /**\n   * API method to launch a process that will serve directory over http\n   */\n  CLI.prototype.autoinstall = function (cb) {\n    var filepath = path.resolve(path.dirname(module.filename), '../Sysinfo/ServiceDetection/ServiceDetection.js');\n\n    this.start(filepath, (err, res) => {\n      if (err) {\n        Common.printError(cst.PREFIX_MSG_ERR + 'Error while trying to serve : ' + err.message || err);\n        return cb ? cb(err) : this.speedList(cst.ERROR_EXIT);\n      }\n      return cb ? cb(null) : this.speedList();\n    });\n  }\n\n  /**\n   * API method to launch a process that will serve directory over http\n   *\n   * @param {Object} opts options\n   * @param {String} opts.path path to be served\n   * @param {Number} opts.port port on which http will bind\n   * @param {Boolean} opts.spa single page app served\n   * @param {String} opts.basicAuthUsername basic auth username\n   * @param {String} opts.basicAuthPassword basic auth password\n   * @param {Object} commander commander object\n   * @param {Function} cb optional callback\n   */\n  CLI.prototype.serve = function (target_path, port, opts, commander, cb) {\n    var that = this;\n    var servePort = process.env.PM2_SERVE_PORT || port || 8080;\n    var servePath = path.resolve(process.env.PM2_SERVE_PATH || target_path || '.');\n\n    var filepath = path.resolve(path.dirname(module.filename), './Serve.js');\n\n    if (typeof commander.name === 'string')\n      opts.name = commander.name\n    else\n      opts.name = 'static-page-server-' + servePort\n    if (!opts.env)\n      opts.env = {};\n    opts.env.PM2_SERVE_PORT = servePort;\n    opts.env.PM2_SERVE_PATH = servePath;\n    opts.env.PM2_SERVE_SPA = opts.spa;\n    if (opts.basicAuthUsername && opts.basicAuthPassword) {\n      opts.env.PM2_SERVE_BASIC_AUTH = 'true';\n      opts.env.PM2_SERVE_BASIC_AUTH_USERNAME = opts.basicAuthUsername;\n      opts.env.PM2_SERVE_BASIC_AUTH_PASSWORD = opts.basicAuthPassword;\n    }\n    if (opts.monitor) {\n      opts.env.PM2_SERVE_MONITOR = opts.monitor\n    }\n    opts.cwd = servePath;\n\n    this.start(filepath, opts,  function (err, res) {\n      if (err) {\n        Common.printError(cst.PREFIX_MSG_ERR + 'Error while trying to serve : ' + err.message || err);\n        return cb ? cb(err) : that.speedList(cst.ERROR_EXIT);\n      }\n      Common.printOut(cst.PREFIX_MSG + 'Serving ' + servePath + ' on port ' + servePort);\n      return cb ? cb(null, res) : that.speedList();\n    });\n  }\n\n  /**\n   * Ping daemon - if PM2 daemon not launched, it will launch it\n   * @method ping\n   */\n  CLI.prototype.ping = function(cb) {\n    var that = this;\n\n    that.Client.executeRemote('ping', {}, function(err, res) {\n      if (err) {\n        Common.printError(err);\n        return cb ? cb(new Error(err)) : that.exitCli(cst.ERROR_EXIT);\n      }\n      Common.printOut(res);\n      return cb ? cb(null, res) : that.exitCli(cst.SUCCESS_EXIT);\n    });\n  };\n\n\n  /**\n   * Execute remote command\n   */\n  CLI.prototype.remote = function(command, opts, cb) {\n    var that = this;\n\n    that[command](opts.name, function(err_cmd, ret) {\n      if (err_cmd)\n        console.error(err_cmd);\n      console.log('Command %s finished', command);\n      return cb(err_cmd, ret);\n    });\n  };\n\n  /**\n   * This remote method allows to pass multiple arguments\n   * to PM2\n   * It is used for the new scoped PM2 action system\n   */\n  CLI.prototype.remoteV2 = function(command, opts, cb) {\n    var that = this;\n\n    if (that[command].length == 1)\n      return that[command](cb);\n\n    opts.args.push(cb);\n    return that[command].apply(this, opts.args);\n  };\n\n\n  /**\n   * Description\n   * @method generateSample\n   * @param {} name\n   * @return\n   */\n  CLI.prototype.generateSample = function(mode) {\n    var that = this;\n    var templatePath;\n\n    if (mode == 'simple')\n      templatePath = path.join(cst.TEMPLATE_FOLDER, cst.APP_CONF_TPL_SIMPLE);\n    else\n      templatePath = path.join(cst.TEMPLATE_FOLDER, cst.APP_CONF_TPL);\n\n    var sample = fs.readFileSync(templatePath);\n    var dt     = sample.toString();\n    var f_name = 'ecosystem.config.js';\n\t\tvar pwd = process.env.PWD || process.cwd();\n\n    try {\n      fs.writeFileSync(path.join(pwd, f_name), dt);\n    } catch (e) {\n      console.error(e.stack || e);\n      return that.exitCli(cst.ERROR_EXIT);\n    }\n    Common.printOut('File %s generated', path.join(pwd, f_name));\n    that.exitCli(cst.SUCCESS_EXIT);\n  };\n\n  /**\n   * Description\n   * @method dashboard\n   * @return\n   */\n  CLI.prototype.dashboard = function(cb) {\n    var that = this;\n\n    var Dashboard = require('./Dashboard');\n\n    if (cb)\n      return cb(new Error('Dashboard cant be called programmatically'));\n\n    Dashboard.init();\n\n    this.Client.launchBus(function (err, bus) {\n      if (err) {\n        console.error('Error launchBus: ' + err);\n        that.exitCli(cst.ERROR_EXIT);\n      }\n      bus.on('log:*', function(type, data) {\n        Dashboard.log(type, data)\n      })\n    });\n\n    process.on('SIGINT', function() {\n      this.Client.disconnectBus(function() {\n        process.exit(cst.SUCCESS_EXIT);\n      });\n    });\n\n    function refreshDashboard() {\n      that.Client.executeRemote('getMonitorData', {}, function(err, list) {\n        if (err) {\n          console.error('Error retrieving process list: ' + err);\n          that.exitCli(cst.ERROR_EXIT);\n        }\n\n        Dashboard.refresh(list);\n\n        setTimeout(function() {\n          refreshDashboard();\n        }, 800);\n      });\n    }\n\n    refreshDashboard();\n  };\n\n  CLI.prototype.monit = function(cb) {\n    var that = this;\n\n    var Monit = require('./Monit.js');\n\n    if (cb) return cb(new Error('Monit cant be called programmatically'));\n\n    Monit.init();\n\n    function launchMonitor() {\n      that.Client.executeRemote('getMonitorData', {}, function(err, list) {\n        if (err) {\n          console.error('Error retrieving process list: ' + err);\n          that.exitCli(conf.ERROR_EXIT);\n        }\n\n        Monit.refresh(list);\n\n        setTimeout(function() {\n          launchMonitor();\n        }, 400);\n      });\n    }\n\n    launchMonitor();\n  };\n\n  CLI.prototype.inspect = function(app_name, cb) {\n    const that = this;\n    this.trigger(app_name, 'internal:inspect', function (err, res) {\n\n      if(res && res[0]) {\n        if (res[0].data.return === '') {\n          Common.printOut(`Inspect disabled on ${app_name}`);\n        } else {\n          Common.printOut(`Inspect enabled on ${app_name} => go to chrome : chrome://inspect !!!`);\n        }\n      } else {\n        Common.printOut(`Unable to activate inspect mode on ${app_name} !!!`);\n      }\n\n      that.exitCli(cst.SUCCESS_EXIT);\n    });\n  };\n};\n"
  },
  {
    "path": "lib/API/ExtraMgmt/Docker.js",
    "content": "\nconst util = require('util')\nconst spawn = require('child_process').spawn\nconst DockerMgmt = {}\n\nmodule.exports = DockerMgmt\n\nfunction execDocker(cmd, cb) {\n  var i = spawn('docker', cmd, {\n    stdio : 'inherit',\n    env: process.env,\n\t\tshell : true\n  })\n\n  i.on('close', cb)\n}\n\nDockerMgmt.processCommand = function(PM2, start_id, select_id, action, cb) {\n  PM2.Client.executeRemote('getSystemData', {}, (err, sys_infos) => {\n    if (sys_infos.containers && sys_infos.containers.length == 0)\n      return cb(new Error(`Process ${select_id} not found`))\n    var container = sys_infos.containers[select_id - start_id - 1]\n    if (action == 'stopProcessId')\n      execDocker(['stop', container.id], cb)\n    if (action == 'deleteProcessId')\n      execDocker(['rm', container.id], cb)\n    if (action == 'restartProcessId')\n      execDocker(['restart', container.id], cb)\n  })\n}\n"
  },
  {
    "path": "lib/API/Log.js",
    "content": "/**\n * Copyright 2013-2022 the PM2 project authors. All rights reserved.\n * Use of this source code is governed by a license that\n * can be found in the LICENSE file.\n */\nvar fs     = require('fs'),\n    util   = require('util'),\n    chalk  = require('ansis'),\n    forEachLimit  = require('async/forEachLimit'),\n    dayjs = require('dayjs');\n\nvar Log = module.exports = {};\n\nvar DEFAULT_PADDING = '          ';\n\n/**\n * Tail logs from file stream.\n * @param {Object} apps_list\n * @param {Number} lines\n * @param {Boolean} raw\n * @param {Function} callback\n * @return\n */\n\nLog.tail = function(apps_list, lines, raw, callback) {\n  var that = this;\n\n  if (lines === 0 || apps_list.length === 0)\n    return callback && callback();\n\n  var count = 0;\n\n  var getLastLines = function (filename, lines, callback) {\n    var chunk = '';\n    var size = Math.max(0, fs.statSync(filename).size - (lines * 200));\n\n    var fd = fs.createReadStream(filename, {start : size});\n    fd.on('data', function(data) { chunk += data.toString(); });\n    fd.on('end', function() {\n      chunk = chunk.split('\\n').slice(-(lines+1));\n      chunk.pop();\n      callback(chunk);\n    });\n  };\n\n  apps_list.sort(function(a, b) {\n    return (fs.existsSync(a.path) ? fs.statSync(a.path).mtime.valueOf() : 0) -\n      (fs.existsSync(b.path) ? fs.statSync(b.path).mtime.valueOf() : 0);\n  });\n\n  forEachLimit(apps_list, 1, function(app, next) {\n    if (!fs.existsSync(app.path || ''))\n      return next();\n\n    getLastLines(app.path, lines, function(output) {\n      console.log(chalk.gray('%s last %d lines:'), app.path, lines);\n      output.forEach(function(out) {\n        if (raw)\n          return app.type === 'err' ? console.error(out) : console.log(out);\n        if (app.type === 'out')\n          process.stdout.write(chalk.green(pad(DEFAULT_PADDING, app.app_name)  + ' | '));\n        else if (app.type === 'err')\n          process.stdout.write(chalk.red(pad(DEFAULT_PADDING, app.app_name)  + ' | '));\n        else\n          process.stdout.write(chalk.blue(pad(DEFAULT_PADDING, 'PM2') + ' | '));\n        console.log(out);\n      });\n      if (output.length)\n        process.stdout.write('\\n');\n      next();\n    });\n  }, function() {\n    callback && callback();\n  });\n};\n\n/**\n * Stream logs in realtime from the bus eventemitter.\n * @param {String} id\n * @param {Boolean} raw\n * @return\n */\n\nLog.stream = function(Client, id, raw, timestamp, exclusive, highlight) {\n  var that = this;\n\n  Client.launchBus(function(err, bus, socket) {\n\n    socket.on('reconnect attempt', function() {\n      if (global._auto_exit === true) {\n        if (timestamp)\n          process.stdout.write(chalk['dim'](chalk.gray(dayjs().format(timestamp) + ' ')));\n        process.stdout.write(chalk.blue(pad(DEFAULT_PADDING, 'PM2') + ' | ') + '[[[ Target PM2 killed. ]]]');\n        process.exit(0);\n      }\n    });\n\n    var min_padding = 3\n\n    bus.on('log:*', function(type, packet) {\n        var isMatchingProcess = id === 'all'\n            || packet.process.name == id\n            || packet.process.pm_id == id\n            || packet.process.namespace == id;\n\n      if (!isMatchingProcess)\n        return;\n\n      if ((type === 'out' && exclusive === 'err')\n         || (type === 'err' && exclusive === 'out')\n         || (type === 'PM2' && exclusive !== false))\n        return;\n\n      var lines;\n\n      if (typeof(packet.data) === 'string')\n        lines = (packet.data || '').split('\\n');\n      else\n        return;\n\n      lines.forEach(function(line) {\n        if (!line || line.length === 0) return;\n\n        if (raw)\n          return type === 'err' ? process.stderr.write(util.format(line) + '\\n') : process.stdout.write(util.format(line) + '\\n');\n\n        if (timestamp)\n          process.stdout.write(chalk['dim'](chalk.gray(dayjs().format(timestamp) + ' ')));\n\n        var name = packet.process.pm_id + '|' + packet.process.name;\n\n        if (name.length > min_padding)\n          min_padding = name.length + 1\n\n        if (type === 'out')\n          process.stdout.write(chalk.green(pad(' '.repeat(min_padding), name)  + ' | '));\n        else if (type === 'err')\n          process.stdout.write(chalk.red(pad(' '.repeat(min_padding), name)  + ' | '));\n        else if (!raw && (id === 'all' || id === 'PM2'))\n          process.stdout.write(chalk.blue(pad(' '.repeat(min_padding), 'PM2') + ' | '));\n        if (highlight)\n          process.stdout.write(util.format(line).replace(highlight, chalk.bgBlackBright(highlight)) + '\\n');\n        else\n          process.stdout.write(util.format(line)+ '\\n');\n      });\n    });\n  });\n};\n\nLog.devStream = function(Client, id, raw, timestamp, exclusive) {\n  var that = this;\n\n  Client.launchBus(function(err, bus) {\n\n    setTimeout(function() {\n      bus.on('process:event', function(packet) {\n        if (packet.event == 'online')\n          console.log(chalk.green('[rundev] App %s restarted'), packet.process.name);\n      });\n    }, 1000);\n\n    var min_padding = 3\n\n    bus.on('log:*', function(type, packet) {\n      if (id !== 'all'\n          && packet.process.name != id\n          && packet.process.pm_id != id)\n        return;\n\n      if ((type === 'out' && exclusive === 'err')\n          || (type === 'err' && exclusive === 'out')\n          || (type === 'PM2' && exclusive !== false))\n        return;\n\n      if (type === 'PM2')\n        return;\n\n      var name = packet.process.pm_id + '|' + packet.process.name;\n\n      var lines;\n\n      if (typeof(packet.data) === 'string')\n        lines = (packet.data || '').split('\\n');\n      else\n        return;\n\n      lines.forEach(function(line) {\n        if (!line || line.length === 0) return;\n\n        if (raw)\n          return process.stdout.write(util.format(line) + '\\n');\n\n        if (timestamp)\n          process.stdout.write(chalk['dim'](chalk.gray(dayjs().format(timestamp) + ' ')));\n\n        var name = packet.process.name + '-' + packet.process.pm_id;\n\n        if (name.length > min_padding)\n          min_padding = name.length + 1\n\n        if (type === 'out')\n          process.stdout.write(chalk.green(pad(' '.repeat(min_padding), name)  + ' | '));\n        else if (type === 'err')\n          process.stdout.write(chalk.red(pad(' '.repeat(min_padding), name)  + ' | '));\n        else if (!raw && (id === 'all' || id === 'PM2'))\n          process.stdout.write(chalk.blue(pad(' '.repeat(min_padding), 'PM2') + ' | '));\n        process.stdout.write(util.format(line) + '\\n');\n      });\n    });\n  });\n};\n\nLog.jsonStream = function(Client, id) {\n  var that = this;\n\n  Client.launchBus(function(err, bus) {\n    if (err) console.error(err);\n\n    bus.on('process:event', function(packet) {\n      process.stdout.write(JSON.stringify({\n        timestamp : dayjs(packet.at),\n        type      : 'process_event',\n        status    : packet.event,\n        app_name  : packet.process.name\n      }));\n      process.stdout.write('\\n');\n    });\n\n    bus.on('log:*', function(type, packet) {\n      if (id !== 'all'\n          && packet.process.name != id\n          && packet.process.pm_id != id)\n        return;\n\n      if (type === 'PM2')\n        return;\n\n      if (typeof(packet.data) == 'string')\n        packet.data = packet.data.replace(/(\\r\\n|\\n|\\r)/gm,'');\n\n      process.stdout.write(JSON.stringify({\n        message : packet.data,\n        timestamp : dayjs(packet.at),\n        type : type,\n        process_id : packet.process.pm_id,\n        app_name : packet.process.name\n      }));\n      process.stdout.write('\\n');\n    });\n  });\n};\n\nLog.formatStream = function(Client, id, raw, timestamp, exclusive, highlight) {\n  var that = this;\n\n  Client.launchBus(function(err, bus) {\n\n    bus.on('log:*', function(type, packet) {\n      if (id !== 'all'\n          && packet.process.name != id\n          && packet.process.pm_id != id)\n        return;\n\n      if ((type === 'out' && exclusive === 'err')\n          || (type === 'err' && exclusive === 'out')\n          || (type === 'PM2' && exclusive !== false))\n        return;\n\n      if (type === 'PM2' && raw)\n        return;\n\n      var name = packet.process.name + '-' + packet.process.pm_id;\n\n      var lines;\n\n      if (typeof(packet.data) === 'string')\n        lines = (packet.data || '').split('\\n');\n      else\n        return;\n\n      lines.forEach(function(line) {\n        if (!line || line.length === 0) return;\n\n        if (!raw) {\n          if (timestamp)\n            process.stdout.write('timestamp=' + dayjs().format(timestamp) + ' ');\n          if (packet.process.name === 'PM2')\n            process.stdout.write('app=pm2 ');\n          if (packet.process.name !== 'PM2')\n            process.stdout.write('app=' + packet.process.name + ' id=' + packet.process.pm_id + ' ');\n          if (type === 'out')\n            process.stdout.write('type=out ');\n          else if (type === 'err')\n            process.stdout.write('type=error ');\n        }\n\n        process.stdout.write('message=');\n        if (highlight)\n          process.stdout.write(util.format(line).replace(highlight, chalk.bgBlackBright(highlight)) + '\\n');\n        else\n          process.stdout.write(util.format(line) + '\\n');\n      });\n    });\n  });\n};\n\nfunction pad(pad, str, padLeft) {\n  if (typeof str === 'undefined')\n    return pad;\n  if (padLeft) {\n    return (pad + str).slice(-pad.length);\n  } else {\n    return (str + pad).substring(0, pad.length);\n  }\n}\n"
  },
  {
    "path": "lib/API/LogManagement.js",
    "content": "var chalk  = require('ansis');\nvar util   = require('util');\nvar fs     = require('fs');\nvar exec   = require('child_process').exec;\nvar path   = require('path');\n\nvar Log    = require('./Log');\nvar cst    = require('../../constants.js');\nvar Common = require('../Common.js');\n\nmodule.exports = function(CLI) {\n\n  /**\n   * Description\n   * @method flush\n   * @return\n   */\n  CLI.prototype.flush = function(api, cb) {\n    var that = this;\n\n    if (!api) {\n      Common.printOut(cst.PREFIX_MSG + 'Flushing ' + cst.PM2_LOG_FILE_PATH);\n      fs.closeSync(fs.openSync(cst.PM2_LOG_FILE_PATH, 'w'));\n    }\n\n    that.Client.executeRemote('getMonitorData', {}, function(err, list) {\n      if (err) {\n        Common.printError(err);\n        return cb ? cb(Common.retErr(err)) : that.exitCli(cst.ERROR_EXIT);\n      }\n      list.forEach(function(l) {\n        if (typeof api == 'undefined') {\n          Common.printOut(cst.PREFIX_MSG + 'Flushing:');\n          Common.printOut(cst.PREFIX_MSG + l.pm2_env.pm_out_log_path);\n          Common.printOut(cst.PREFIX_MSG + l.pm2_env.pm_err_log_path);\n\n          if (l.pm2_env.pm_log_path) {\n            Common.printOut(cst.PREFIX_MSG + l.pm2_env.pm_log_path);\n            fs.closeSync(fs.openSync(l.pm2_env.pm_log_path, 'w'));\n          }\n          fs.closeSync(fs.openSync(l.pm2_env.pm_out_log_path, 'w'));\n          fs.closeSync(fs.openSync(l.pm2_env.pm_err_log_path, 'w'));\n        }\n        else if (l.pm2_env.pm_id == api || l.pm2_env.name === api) {\n          Common.printOut(cst.PREFIX_MSG + 'Flushing:');\n\n          if (l.pm2_env.pm_log_path && fs.existsSync(l.pm2_env.pm_log_path)) {\n            Common.printOut(cst.PREFIX_MSG + l.pm2_env.pm_log_path);\n            fs.closeSync(fs.openSync(l.pm2_env.pm_log_path, 'w'));\n          }\n\n          if (l.pm2_env.pm_out_log_path && fs.existsSync(l.pm2_env.pm_out_log_path)) {\n            Common.printOut(cst.PREFIX_MSG + l.pm2_env.pm_out_log_path);\n            fs.closeSync(fs.openSync(l.pm2_env.pm_out_log_path, 'w'));\n          }\n\n          if (l.pm2_env.pm_err_log_path && fs.existsSync(l.pm2_env.pm_err_log_path)) {\n            Common.printOut(cst.PREFIX_MSG + l.pm2_env.pm_err_log_path);\n            fs.closeSync(fs.openSync(l.pm2_env.pm_err_log_path, 'w'));\n          }\n        }\n      });\n\n      Common.printOut(cst.PREFIX_MSG + 'Logs flushed');\n      return cb ? cb(null, list) : that.exitCli(cst.SUCCESS_EXIT);\n    });\n  };\n\n  CLI.prototype.logrotate = function(opts, cb) {\n    var that = this;\n\n    if (process.getuid() != 0) {\n      return exec('whoami', function(err, stdout, stderr) {\n        Common.printError(cst.PREFIX_MSG + 'You have to run this command as root. Execute the following command:');\n        Common.printError(cst.PREFIX_MSG + chalk.gray('      sudo env PATH=$PATH:' + path.dirname(process.execPath) + ' pm2 logrotate -u ' + stdout.trim()));\n\n        cb ? cb(Common.retErr('You have to run this with elevated rights')) : that.exitCli(cst.ERROR_EXIT);\n      });\n    }\n\n    if (!fs.existsSync('/etc/logrotate.d')) {\n      Common.printError(cst.PREFIX_MSG + '/etc/logrotate.d does not exist we can not copy the default configuration.');\n      return cb ? cb(Common.retErr('/etc/logrotate.d does not exist')) : that.exitCli(cst.ERROR_EXIT);\n    }\n\n    var templatePath = path.join(cst.TEMPLATE_FOLDER, cst.LOGROTATE_SCRIPT);\n    Common.printOut(cst.PREFIX_MSG + 'Getting logrorate template ' + templatePath);\n    var script = fs.readFileSync(templatePath, {encoding: 'utf8'});\n\n    var user = opts.user || 'root';\n\n    script = script.replace(/%HOME_PATH%/g, cst.PM2_ROOT_PATH)\n      .replace(/%USER%/g, user);\n\n    try {\n      fs.writeFileSync('/etc/logrotate.d/pm2-'+user, script);\n    } catch (e) {\n      console.error(e.stack || e);\n    }\n\n    Common.printOut(cst.PREFIX_MSG + 'Logrotate configuration added to /etc/logrotate.d/pm2');\n    return cb ? cb(null, {success:true}) : that.exitCli(cst.SUCCESS_EXIT);\n  };\n\n  /**\n   * Description\n   * @method reloadLogs\n   * @return\n   */\n  CLI.prototype.reloadLogs = function(cb) {\n    var that = this;\n\n    Common.printOut('Reloading all logs...');\n    that.Client.executeRemote('reloadLogs', {}, function(err, logs) {\n      if (err) {\n        Common.printError(err);\n        return cb ? cb(Common.retErr(err)) : that.exitCli(cst.ERROR_EXIT);\n      }\n      Common.printOut('All logs reloaded');\n      return cb ? cb(null, logs) : that.exitCli(cst.SUCCESS_EXIT);\n    });\n  };\n\n  /**\n   * Description\n   * @method streamLogs\n   * @param {String} id\n   * @param {Number} lines\n   * @param {Boolean} raw\n   * @return\n   */\n  CLI.prototype.streamLogs = function(id, lines, raw, timestamp, exclusive, highlight) {\n    var that = this;\n    var files_list = [];\n\n    // If no argument is given, we stream logs for all running apps\n    id = id || 'all';\n    lines = lines !== undefined ? lines : 20;\n    lines = lines < 0 ? -(lines) : lines;\n\n    // Avoid duplicates and check if path is different from '/dev/null'\n    var pushIfUnique = function(entry) {\n      var exists = false;\n\n      if (entry.path.toLowerCase\n          && entry.path.toLowerCase() !== '/dev/null') {\n\n        files_list.some(function(file) {\n          if (file.path === entry.path)\n            exists = true;\n          return exists;\n        });\n\n        if (exists)\n          return;\n\n        files_list.push(entry);\n      }\n    }\n\n    // Get the list of all running apps\n    that.Client.executeRemote('getMonitorData', {}, function(err, list) {\n      var regexList = [];\n      var namespaceList = [];\n\n      if (err) {\n        Common.printError(err);\n        that.exitCli(cst.ERROR_EXIT);\n      }\n\n      if (lines === 0)\n        return Log.stream(that.Client, id, raw, timestamp, exclusive, highlight);\n\n      Common.printOut(chalk.bold.gray(util.format.call(this, '[TAILING] Tailing last %d lines for [%s] process%s (change the value with --lines option)', lines, id, id === 'all' ? 'es' : '')));\n\n      // Populate the array `files_list` with the paths of all files we need to tail\n      list.forEach(function(proc) {\n        if (proc.pm2_env && (id === 'all' ||\n                             proc.pm2_env.name == id ||\n                             proc.pm2_env.pm_id == id)) {\n          if (proc.pm2_env.pm_out_log_path && exclusive !== 'err')\n            pushIfUnique({\n              path     : proc.pm2_env.pm_out_log_path,\n              app_name :proc.pm2_env.pm_id + '|' + proc.pm2_env.name,\n              type     : 'out'});\n          if (proc.pm2_env.pm_err_log_path && exclusive !== 'out')\n            pushIfUnique({\n              path     : proc.pm2_env.pm_err_log_path,\n              app_name : proc.pm2_env.pm_id + '|' + proc.pm2_env.name,\n              type     : 'err'\n            });\n        } else if(proc.pm2_env && proc.pm2_env.namespace == id) {\n          if(namespaceList.indexOf(proc.pm2_env.name) === -1) {\n            namespaceList.push(proc.pm2_env.name)\n          }\n          if (proc.pm2_env.pm_out_log_path && exclusive !== 'err')\n            pushIfUnique({\n              path     : proc.pm2_env.pm_out_log_path,\n              app_name :proc.pm2_env.pm_id + '|' + proc.pm2_env.name,\n              type     : 'out'});\n          if (proc.pm2_env.pm_err_log_path && exclusive !== 'out')\n            pushIfUnique({\n              path     : proc.pm2_env.pm_err_log_path,\n              app_name : proc.pm2_env.pm_id + '|' + proc.pm2_env.name,\n              type     : 'err'\n            });\n        }\n        // Populate the array `files_list` with the paths of all files we need to tail, when log in put is a regex\n        else if(proc.pm2_env && (isNaN(id) && id[0] === '/' && id[id.length - 1] === '/')) {\n          var regex = new RegExp(id.replace(/\\//g, ''));\n          if(regex.test(proc.pm2_env.name)) {\n            if(regexList.indexOf(proc.pm2_env.name) === -1) {\n              regexList.push(proc.pm2_env.name);\n            }\n            if (proc.pm2_env.pm_out_log_path && exclusive !== 'err')\n              pushIfUnique({\n                path     : proc.pm2_env.pm_out_log_path,\n                app_name : proc.pm2_env.pm_id + '|' + proc.pm2_env.name,\n                type     : 'out'});\n            if (proc.pm2_env.pm_err_log_path && exclusive !== 'out')\n              pushIfUnique({\n                path     : proc.pm2_env.pm_err_log_path,\n                app_name : proc.pm2_env.pm_id + '|' + proc.pm2_env.name,\n                type     : 'err'\n              });\n          }\n        }\n      });\n\n    //for fixing issue https://github.com/Unitech/pm2/issues/3506\n     /* if (files_list && files_list.length == 0) {\n        Common.printError(cst.PREFIX_MSG_ERR + 'No file to stream for app [%s], exiting.', id);\n        return process.exit(cst.ERROR_EXIT);\n      }*/\n\n      if (!raw && (id === 'all' || id === 'PM2') && exclusive === false) {\n        Log.tail([{\n          path     : cst.PM2_LOG_FILE_PATH,\n          app_name : 'PM2',\n          type     : 'PM2'\n        }], lines, raw, function() {\n          Log.tail(files_list, lines, raw, function() {\n            Log.stream(that.Client, id, raw, timestamp, exclusive, highlight);\n          });\n        });\n      }\n      else {\n        Log.tail(files_list, lines, raw, function() {\n          if(regexList.length > 0) {\n            regexList.forEach(function(id) {\n                Log.stream(that.Client, id, raw, timestamp, exclusive, highlight);\n            })\n          }\n          else if(namespaceList.length > 0) {\n            namespaceList.forEach(function(id) {\n                Log.stream(that.Client, id, raw, timestamp, exclusive, highlight);\n            })\n          }\n          else {\n            Log.stream(that.Client, id, raw, timestamp, exclusive, highlight);\n          }\n        });\n      }\n    });\n  };\n\n  /**\n   * Description\n   * @method printLogs\n   * @param {String} id\n   * @param {Number} lines\n   * @param {Boolean} raw\n   * @return\n   */\n  CLI.prototype.printLogs = function(id, lines, raw, timestamp, exclusive) {\n    var that = this;\n    var files_list = [];\n\n    // If no argument is given, we stream logs for all running apps\n    id = id || 'all';\n    lines = lines !== undefined ? lines : 20;\n    lines = lines < 0 ? -(lines) : lines;\n\n    // Avoid duplicates and check if path is different from '/dev/null'\n    var pushIfUnique = function(entry) {\n      var exists = false;\n\n      if (entry.path.toLowerCase\n          && entry.path.toLowerCase() !== '/dev/null') {\n\n        files_list.some(function(file) {\n          if (file.path === entry.path)\n            exists = true;\n          return exists;\n        });\n\n        if (exists)\n          return;\n\n        files_list.push(entry);\n      }\n    }\n\n    // Get the list of all running apps\n    that.Client.executeRemote('getMonitorData', {}, function(err, list) {\n      if (err) {\n        Common.printError(err);\n        that.exitCli(cst.ERROR_EXIT);\n      }\n\n      if (lines <= 0) {\n        return that.exitCli(cst.SUCCESS_EXIT)\n      }\n\n      Common.printOut(chalk.bold.gray(util.format.call(this, '[TAILING] Tailing last %d lines for [%s] process%s (change the value with --lines option)', lines, id, id === 'all' ? 'es' : '')));\n\n      // Populate the array `files_list` with the paths of all files we need to tail\n      list.forEach(function(proc) {\n        if (proc.pm2_env && (id === 'all' ||\n                             proc.pm2_env.name == id ||\n                             proc.pm2_env.pm_id == id)) {\n          if (proc.pm2_env.pm_out_log_path && exclusive !== 'err')\n            pushIfUnique({\n              path     : proc.pm2_env.pm_out_log_path,\n              app_name :proc.pm2_env.pm_id + '|' + proc.pm2_env.name,\n              type     : 'out'});\n          if (proc.pm2_env.pm_err_log_path && exclusive !== 'out')\n            pushIfUnique({\n              path     : proc.pm2_env.pm_err_log_path,\n              app_name : proc.pm2_env.pm_id + '|' + proc.pm2_env.name,\n              type     : 'err'\n            });\n        }\n        // Populate the array `files_list` with the paths of all files we need to tail, when log in put is a regex\n        else if(proc.pm2_env && (isNaN(id) && id[0] === '/' && id[id.length - 1] === '/')) {\n          var regex = new RegExp(id.replace(/\\//g, ''));\n          if(regex.test(proc.pm2_env.name)) {\n            if (proc.pm2_env.pm_out_log_path && exclusive !== 'err')\n              pushIfUnique({\n                path     : proc.pm2_env.pm_out_log_path,\n                app_name : proc.pm2_env.pm_id + '|' + proc.pm2_env.name,\n                type     : 'out'});\n            if (proc.pm2_env.pm_err_log_path && exclusive !== 'out')\n              pushIfUnique({\n                path     : proc.pm2_env.pm_err_log_path,\n                app_name : proc.pm2_env.pm_id + '|' + proc.pm2_env.name,\n                type     : 'err'\n              });\n          }\n        }\n      });\n\n      if (!raw && (id === 'all' || id === 'PM2') && exclusive === false) {\n        Log.tail([{\n          path     : cst.PM2_LOG_FILE_PATH,\n          app_name : 'PM2',\n          type     : 'PM2'\n        }], lines, raw, function() {\n          Log.tail(files_list, lines, raw, function() {\n            that.exitCli(cst.SUCCESS_EXIT);\n          });\n        });\n      }\n      else {\n        Log.tail(files_list, lines, raw, function() {\n          that.exitCli(cst.SUCCESS_EXIT);\n        });\n      }\n    });\n  };\n};\n"
  },
  {
    "path": "lib/API/Modules/LOCAL.js",
    "content": "\nvar path          = require('path');\nvar fs            = require('fs');\nvar os            = require('os');\nvar spawn         = require('child_process').spawn;\nvar chalk         = require('ansis');\nvar parallel      = require('async/parallel');\n\nvar Configuration = require('../../Configuration.js');\nvar cst           = require('../../../constants.js');\nvar Common        = require('../../Common');\nvar Utility       = require('../../Utility.js');\nvar readline = require('readline')\n\nvar INTERNAL_MODULES = {\n  'deep-monitoring': {\n    dependencies: [{name: 'v8-profiler-node8'}, {name: 'gc-stats'}, {name: 'event-loop-inspector'}]\n  },\n  'gc-stats': {name: 'gc-stats'},\n  'event-loop-inspector': {name: 'event-loop-inspector'},\n  'v8-profiler': {name: 'v8-profiler-node8'},\n  'profiler': {name: 'v8-profiler-node8'},\n  'typescript': {dependencies: [{name: 'typescript'}, {name: 'ts-node@latest'}]},\n  'livescript': {name: 'livescript'},\n  'coffee-script': {name: 'coffee-script', message: 'Coffeescript v1 support'},\n  'coffeescript': {name: 'coffeescript', message: 'Coffeescript v2 support'}\n};\n\nmodule.exports = {\n  install,\n  INTERNAL_MODULES,\n  installMultipleModules\n}\n\n\nfunction install(module, cb, verbose) {\n  if (!module || !module.name || module.name.length === 0) {\n    return cb(new Error('No module name !'));\n  }\n\n  if (typeof verbose === 'undefined') {\n    verbose = true;\n  }\n\n  installLangModule(module.name, function (err) {\n    var display = module.message || module.name;\n    if (err) {\n      if (verbose) { Common.printError(cst.PREFIX_MSG_MOD_ERR + chalk.bold.green(display + ' installation has FAILED (checkout previous logs)')); }\n      return cb(err);\n    }\n\n    if (verbose) { Common.printOut(cst.PREFIX_MSG + chalk.bold.green(display + ' ENABLED')); }\n    return cb();\n  });\n}\n\nfunction installMultipleModules(modules, cb, post_install) {\n  var functionList = [];\n  for (var i = 0; i < modules.length; i++) {\n    functionList.push((function (index) {\n      return function (callback) {\n        var module = modules[index];\n        if (typeof modules[index] === 'string') {\n          module = {name: modules[index]};\n        }\n        install(module, function ($post_install, err, $index, $modules) {\n          try {\n            var install_instance = spawn(post_install[modules[index]], {\n              stdio : 'inherit',\n              windowsHide: true,\n              env: process.env,\n              shell : true,\n              cwd : process.cwd()\n            });\n            Common.printOut(cst.PREFIX_MSG_MOD + 'Running configuraton script.');\n          }\n          catch(e)\n          {\n            Common.printOut(cst.PREFIX_MSG_MOD + 'No configuraton script found.');\n          }\n          callback(null, {  module: module, err: err });\n        }, false);\n      };\n    })(i));\n  }\n\n  parallel(functionList, function (err, results) {\n    for (var i = 0; i < results.length; i++) {\n      var display = results[i].module.message || results[i].module.name;\n      if (results[i].err) {\n        err = results[i].err;\n        Common.printError(cst.PREFIX_MSG_MOD_ERR + chalk.bold.green(display + ' installation has FAILED (checkout previous logs)'));\n      } else {\n        Common.printOut(cst.PREFIX_MSG + chalk.bold.green(display + ' ENABLED'));\n      }\n    }\n\n    if(cb) cb(err);\n  });\n};\n\nfunction installLangModule(module_name, cb) {\n  var node_module_path = path.resolve(path.join(__dirname, '../../../'));\n  Common.printOut(cst.PREFIX_MSG_MOD + 'Calling ' + chalk.bold.red('[NPM]') + ' to install ' + module_name + ' ...');\n\n  var install_instance = spawn(cst.IS_WINDOWS ? 'npm.cmd' : 'npm', ['install', module_name, '--loglevel=error'], {\n    stdio : 'inherit',\n    env: process.env,\n\t\tshell : true,\n    cwd : node_module_path\n  });\n\n  install_instance.on('close', function(code) {\n    if (code > 0)\n      return cb(new Error('Module install failed'));\n    return cb(null);\n  });\n\n  install_instance.on('error', function (err) {\n    console.error(err.stack || err);\n  });\n};\n"
  },
  {
    "path": "lib/API/Modules/Modularizer.js",
    "content": "/**\n * Copyright 2013-2022 the PM2 project authors. All rights reserved.\n * Use of this source code is governed by a license that\n * can be found in the LICENSE file.\n */\nvar path          = require('path');\nvar eachLimit     = require('async/eachLimit');\nvar forEachLimit  = require('async/forEachLimit');\n\nvar Configuration = require('../../Configuration.js');\nvar cst = require('../../../constants.js');\nvar Common = require('../../Common');\nvar NPM = require('./NPM.js')\nvar TAR = require('./TAR.js')\nvar LOCAL = require('./LOCAL.js')\n\nvar Modularizer = module.exports = {};\n\n/**\n * PM2 Module System.\n */\nModularizer.install = function (CLI, module_name, opts, cb) {\n  module_name = module_name.replace(/[;`|]/g, \"\");\n  if (typeof(opts) == 'function') {\n    cb = opts;\n    opts = {};\n  }\n\n  if (LOCAL.INTERNAL_MODULES.hasOwnProperty(module_name)) {\n    Common.logMod(`Adding dependency ${module_name} to PM2 Runtime`);\n    var currentModule = LOCAL.INTERNAL_MODULES[module_name];\n    if (currentModule && currentModule.hasOwnProperty('dependencies')) {\n      LOCAL.installMultipleModules(currentModule.dependencies, cb);\n    } else {\n      LOCAL.install(currentModule, cb);\n    }\n  }\n  else if (module_name == '.') {\n    Common.logMod(`Installing local NPM module`);\n    return NPM.localStart(CLI, opts, cb)\n  }\n  else if (opts.tarball || /\\.tar\\.gz$/i.test(module_name)) {\n    Common.logMod(`Installing TAR module`);\n    TAR.install(CLI, module_name, opts, cb)\n  }\n  else {\n    Common.logMod(`Installing NPM ${module_name} module`);\n    NPM.install(CLI, module_name, opts, cb)\n  }\n};\n\n/**\n * Launch All Modules\n * Used PM2 at startup\n */\nModularizer.launchModules = function(CLI, cb) {\n  var modules = Modularizer.listModules();\n\n  if (!modules) return cb();\n\n  // 1#\n  function launchNPMModules(cb) {\n    if (!modules.npm_modules) return launchTARModules(cb)\n\n    eachLimit(Object.keys(modules.npm_modules), 1, function(module_name, next) {\n      NPM.start(CLI, modules, module_name, next)\n    }, function() {\n      launchTARModules(cb)\n    });\n  }\n\n  // 2#\n  function launchTARModules(cb) {\n    if (!modules.tar_modules) return cb()\n\n    eachLimit(Object.keys(modules.tar_modules), 1, function(module_name, next) {\n      TAR.start(CLI, module_name, next)\n    }, function() {\n      return cb ? cb(null) : false;\n    });\n  }\n\n  launchNPMModules(cb)\n}\n\nModularizer.package = function(CLI, module_path, cb) {\n  var fullpath = process.cwd()\n  if (module_path)\n    fullpath = require('path').resolve(module_path)\n  TAR.packager(fullpath, process.cwd(), cb)\n}\n\n/**\n * Uninstall module\n */\nModularizer.uninstall = function(CLI, module_name, cb) {\n  Common.printOut(cst.PREFIX_MSG_MOD + 'Uninstalling module ' + module_name);\n  var modules_list = Modularizer.listModules();\n\n  if (module_name == 'all') {\n    if (!modules_list) return cb();\n\n    return forEachLimit(Object.keys(modules_list.npm_modules), 1, function(module_name, next) {\n      NPM.uninstall(CLI, module_name, next)\n    }, () => {\n      forEachLimit(Object.keys(modules_list.tar_modules), 1, function(module_name, next) {\n        TAR.uninstall(CLI, module_name, next)\n      }, cb)\n    });\n  }\n\n  if (modules_list.npm_modules[module_name]) {\n    NPM.uninstall(CLI, module_name, cb)\n  } else if (modules_list.tar_modules[module_name]) {\n    TAR.uninstall(CLI, module_name, cb)\n  }\n  else {\n    Common.errMod('Unknown module')\n    CLI.exitCli(1)\n  }\n};\n\n/**\n * List modules based on modules present in ~/.pm2/modules/ folder\n */\nModularizer.listModules = function() {\n  return {\n    npm_modules: Configuration.getSync(cst.MODULE_CONF_PREFIX) || {},\n    tar_modules: Configuration.getSync(cst.MODULE_CONF_PREFIX_TAR) || {}\n  }\n};\n\nModularizer.getAdditionalConf = function(app_name) {\n  return NPM.getModuleConf(app_name)\n};\n\nModularizer.publish = function(PM2, folder, opts, cb) {\n  if (opts.npm == true) {\n    NPM.publish(opts, cb)\n  }\n  else {\n    TAR.publish(PM2, folder, cb)\n  }\n};\n\nModularizer.generateSample = function(app_name, cb) {\n  NPM.generateSample(app_name, cb)\n};\n"
  },
  {
    "path": "lib/API/Modules/NPM.js",
    "content": "const path          = require('path');\nconst fs            = require('fs');\nconst os            = require('os');\nconst spawn         = require('child_process').spawn;\nconst chalk         = require('ansis');\n\nconst readline = require('readline')\nconst which = require('../../tools/which.js')\nconst sexec = require('../../tools/sexec.js')\nconst copydirSync = require('../../tools/copydirSync.js')\nconst deleteFolderRecursive = require('../../tools/deleteFolderRecursive.js')\n\nvar Configuration = require('../../Configuration.js');\nvar cst           = require('../../../constants.js');\nvar Common        = require('../../Common');\nvar Utility       = require('../../Utility.js');\n\nmodule.exports = {\n  install,\n  uninstall,\n  start,\n  publish,\n  generateSample,\n  localStart,\n  getModuleConf\n}\n\n/**\n * PM2 Module System.\n * Features:\n * - Installed modules are listed separately from user applications\n * - Always ON, a module is always up along PM2, to stop it, you need to uninstall it\n * - Install a runnable module from NPM/Github/HTTP (require a package.json only)\n * - Some modules add internal PM2 depencencies (like typescript, profiling...)\n * - Internally it uses NPM install (https://docs.npmjs.com/cli/install)\n * - Auto discover script to launch (first it checks the apps field, then bin and finally main attr)\n * - Generate sample module via pm2 module:generate <module_name>\n */\n\nfunction localStart(PM2, opts, cb) {\n  var proc_path = '',\n      cmd  = '',\n      conf = {};\n\n  Common.printOut(cst.PREFIX_MSG_MOD + 'Installing local module in DEVELOPMENT MODE with WATCH auto restart');\n  proc_path = process.cwd();\n\n  cmd = path.join(proc_path, cst.DEFAULT_MODULE_JSON);\n\n  Common.extend(opts, {\n    cmd : cmd,\n    development_mode : true,\n    proc_path : proc_path\n  });\n\n  return StartModule(PM2, opts, function(err, dt) {\n    if (err) return cb(err);\n    Common.printOut(cst.PREFIX_MSG_MOD + 'Module successfully installed and launched');\n    return cb(null, dt);\n  });\n}\n\nfunction generateSample(app_name, cb) {\n  var rl = readline.createInterface({\n    input: process.stdin,\n    output: process.stdout\n  });\n\n  function samplize(module_name) {\n    var cmd1 = 'git clone https://github.com/pm2-hive/sample-module.git ' + module_name + '; cd ' + module_name + '; rm -rf .git';\n    var cmd2 = 'cd ' + module_name + ' ; sed -i \"s:sample-module:'+ module_name  +':g\" package.json';\n    var cmd3 = 'cd ' + module_name + ' ; npm install';\n\n    Common.printOut(cst.PREFIX_MSG_MOD + 'Getting sample app');\n\n    sexec(cmd1, function(err) {\n      if (err) Common.printError(cst.PREFIX_MSG_MOD_ERR + err.message);\n      sexec(cmd2, function(err) {\n        console.log('');\n        sexec(cmd3, function(err) {\n          console.log('');\n          Common.printOut(cst.PREFIX_MSG_MOD + 'Module sample created in folder: ', path.join(process.cwd(), module_name));\n          console.log('');\n          Common.printOut('Start module in development mode:');\n          Common.printOut('$ cd ' + module_name + '/');\n          Common.printOut('$ pm2 install . ');\n          console.log('');\n\n          Common.printOut('Module Log: ');\n          Common.printOut('$ pm2 logs ' + module_name);\n          console.log('');\n          Common.printOut('Uninstall module: ');\n          Common.printOut('$ pm2 uninstall ' + module_name);\n          console.log('');\n          Common.printOut('Force restart: ');\n          Common.printOut('$ pm2 restart ' + module_name);\n          return cb ?  cb() : false;\n        });\n      });\n    });\n  }\n\n  if (app_name) return samplize(app_name);\n\n  rl.question(cst.PREFIX_MSG_MOD + \"Module name: \", function(module_name) {\n    samplize(module_name);\n  });\n}\n\nfunction publish(opts, cb) {\n  var rl = readline.createInterface({\n    input: process.stdin,\n    output: process.stdout\n  });\n\n  var semver = require('semver');\n\n  var package_file = path.join(process.cwd(), 'package.json');\n\n  var package_json = require(package_file);\n\n  package_json.version = semver.inc(package_json.version, 'minor');\n  Common.printOut(cst.PREFIX_MSG_MOD + 'Incrementing module to: %s@%s',\n                  package_json.name,\n                  package_json.version);\n\n\n  rl.question(\"Write & Publish? [Y/N]\", function(answer) {\n    if (answer != \"Y\")\n      return cb();\n\n\n    fs.writeFile(package_file, JSON.stringify(package_json, null, 2), function(err, data) {\n      if (err) return cb(err);\n\n      Common.printOut(cst.PREFIX_MSG_MOD + 'Publishing module - %s@%s',\n                      package_json.name,\n                      package_json.version);\n\n      sexec('npm publish', function(code) {\n        Common.printOut(cst.PREFIX_MSG_MOD + 'Module - %s@%s successfully published',\n                        package_json.name,\n                        package_json.version);\n\n        Common.printOut(cst.PREFIX_MSG_MOD + 'Pushing module on Git');\n        sexec('git add . ; git commit -m \"' + package_json.version + '\"; git push origin master', function(code) {\n\n          Common.printOut(cst.PREFIX_MSG_MOD + 'Installable with pm2 install %s', package_json.name);\n          return cb(null, package_json);\n        });\n      });\n    });\n\n  });\n}\n\nfunction moduleExistInLocalDB(CLI, module_name, cb) {\n  var modules = Configuration.getSync(cst.MODULE_CONF_PREFIX);\n  if (!modules) return cb(false);\n  var module_name_only = Utility.getCanonicModuleName(module_name)\n  modules = Object.keys(modules);\n  return cb(modules.indexOf(module_name_only) > -1 ? true : false);\n};\n\nfunction install(CLI, module_name, opts, cb) {\n  moduleExistInLocalDB(CLI, module_name, function (exists) {\n    if (exists) {\n      Common.logMod('Module already installed. Updating.');\n\n      Rollback.backup(module_name);\n\n      return uninstall(CLI, module_name, function () {\n        return continueInstall(CLI, module_name, opts, cb);\n      });\n    }\n    return continueInstall(CLI, module_name, opts, cb);\n  })\n}\n\n// Builtin Node Switch\nfunction getNPMCommandLine(module_name, install_path) {\n  if (which('bun')) {\n    return spawn.bind(this, 'bun', ['install', module_name, '--loglevel=error', '--cwd', `\"${install_path}\"` ], {\n      stdio : 'inherit',\n      env: process.env,\n      windowsHide: true,\n      shell : true\n    })\n  }\n  else if (which('npm')) {\n    return spawn.bind(this, cst.IS_WINDOWS ? 'npm.cmd' : 'npm', ['install', module_name, '--loglevel=error', '--prefix', `\"${install_path}\"` ], {\n      stdio : 'inherit',\n      env: process.env,\n      windowsHide: true,\n      shell : true\n    })\n  }\n  else {\n    return spawn.bind(this, cst.BUILTIN_NODE_PATH, [cst.BUILTIN_NPM_PATH, 'install', module_name, '--loglevel=error', '--prefix', `\"${install_path}\"`], {\n      stdio : 'inherit',\n      env: process.env,\n      windowsHide: true,\n      shell : true\n    })\n  }\n}\n\nfunction continueInstall(CLI, module_name, opts, cb) {\n  Common.printOut(cst.PREFIX_MSG_MOD + 'Calling ' + chalk.bold.red('[NPM]') + ' to install ' + module_name + ' ...');\n\n  var canonic_module_name = Utility.getCanonicModuleName(module_name);\n  var install_path = path.join(cst.DEFAULT_MODULE_PATH, canonic_module_name);\n\n  require('mkdirp')(install_path)\n    .then(function() {\n      process.chdir(os.homedir());\n\n      var install_instance = getNPMCommandLine(module_name, install_path)();\n\n      install_instance.on('close', finalizeInstall);\n\n      install_instance.on('error', function (err) {\n        console.error(err.stack || err);\n      });\n    });\n\n  function finalizeInstall(code) {\n    if (code != 0) {\n      // If install has failed, revert to previous module version\n      return Rollback.revert(CLI, module_name, function() {\n        return cb(new Error('Installation failed via NPM, module has been restored to prev version'));\n      });\n    }\n\n    Common.printOut(cst.PREFIX_MSG_MOD + 'Module downloaded');\n\n    var proc_path = path.join(install_path, 'node_modules', canonic_module_name);\n    var package_json_path = path.join(proc_path, 'package.json');\n\n    // Append default configuration to module configuration\n    try {\n      var conf = JSON.parse(fs.readFileSync(package_json_path).toString()).config;\n\n      if (conf) {\n        Object.keys(conf).forEach(function(key) {\n          Configuration.setSyncIfNotExist(canonic_module_name + ':' + key, conf[key]);\n        });\n      }\n    } catch(e) {\n      Common.printError(e);\n    }\n\n    opts = Common.extend(opts, {\n      cmd : package_json_path,\n      development_mode : false,\n      proc_path : proc_path\n    });\n\n    Configuration.set(cst.MODULE_CONF_PREFIX + ':' + canonic_module_name, {\n      uid : opts.uid,\n      gid : opts.gid\n    }, function(err, data) {\n      if (err) return cb(err);\n\n      StartModule(CLI, opts, function(err, dt) {\n        if (err) return cb(err);\n\n        if (process.env.PM2_PROGRAMMATIC === 'true')\n          return cb(null, dt);\n\n        CLI.conf(canonic_module_name, function() {\n          Common.printOut(cst.PREFIX_MSG_MOD + 'Module successfully installed and launched');\n          Common.printOut(cst.PREFIX_MSG_MOD + 'Checkout module options: `$ pm2 conf`');\n          return cb(null, dt);\n        });\n      });\n    });\n  }\n}\n\nfunction start(PM2, modules, module_name, cb) {\n  Common.printOut(cst.PREFIX_MSG_MOD + 'Starting NPM module ' + module_name);\n\n  var install_path = path.join(cst.DEFAULT_MODULE_PATH, module_name);\n  var proc_path = path.join(install_path, 'node_modules', module_name);\n  var package_json_path = path.join(proc_path, 'package.json');\n\n  var opts = {};\n\n  // Merge with embedded configuration inside module_conf (uid, gid)\n  Common.extend(opts, modules[module_name]);\n\n  // Merge meta data to start module properly\n  Common.extend(opts, {\n    // package.json path\n    cmd : package_json_path,\n    // starting mode\n    development_mode : false,\n    // process cwd\n    proc_path : proc_path\n  });\n\n  StartModule(PM2, opts, function(err, dt) {\n    if (err) console.error(err);\n    return cb();\n  })\n}\n\nfunction uninstall(CLI, module_name, cb) {\n  var module_name_only = Utility.getCanonicModuleName(module_name)\n  var proc_path = path.join(cst.DEFAULT_MODULE_PATH, module_name_only);\n  Configuration.unsetSync(cst.MODULE_CONF_PREFIX + ':' + module_name_only);\n\n  CLI.deleteModule(module_name_only, function(err, data) {\n    console.log('Deleting', proc_path)\n    if (module_name != '.' && proc_path.includes('modules') === true) {\n      deleteFolderRecursive(proc_path)\n    }\n\n    if (err) {\n      Common.printError(err);\n      return cb(err);\n    }\n\n    return cb(null, data);\n  });\n}\n\nfunction getModuleConf(app_name) {\n  if (!app_name) throw new Error('No app_name defined');\n\n  var module_conf = Configuration.getAllSync();\n\n  var additional_env = {};\n\n  if (!module_conf[app_name]) {\n    additional_env = {};\n    additional_env[app_name] = {};\n  }\n  else {\n    additional_env = Common.clone(module_conf[app_name]);\n    additional_env[app_name] = JSON.stringify(module_conf[app_name]);\n  }\n  return additional_env;\n}\n\nfunction StartModule(CLI, opts, cb) {\n  if (!opts.cmd && !opts.package) throw new Error('module package.json not defined');\n  if (!opts.development_mode) opts.development_mode = false;\n\n  var package_json = require(opts.cmd || opts.package);\n\n  /**\n   * Script file detection\n   * 1- *apps* field (default pm2 json configuration)\n   * 2- *bin* field\n   * 3- *main* field\n   */\n  if (!package_json.apps && !package_json.pm2) {\n    package_json.apps = {};\n\n    if (package_json.bin) {\n      var bin = Object.keys(package_json.bin)[0];\n      package_json.apps.script = package_json.bin[bin];\n    }\n    else if (package_json.main) {\n      package_json.apps.script = package_json.main;\n    }\n  }\n\n  Common.extend(opts, {\n    cwd               : opts.proc_path,\n    watch             : opts.development_mode,\n    force_name        : package_json.name,\n    started_as_module : true\n  });\n\n  // Start the module\n  CLI.start(package_json, opts, function(err, data) {\n    if (err) return cb(err);\n\n    if (opts.safe) {\n      Common.printOut(cst.PREFIX_MSG_MOD + 'Monitoring module behavior for potential issue (5secs...)');\n\n      var time = typeof(opts.safe) == 'boolean' ? 3000 : parseInt(opts.safe);\n      return setTimeout(function() {\n        CLI.describe(package_json.name, function(err, apps) {\n          if (err || apps[0].pm2_env.restart_time > 2) {\n            return Rollback.revert(CLI, package_json.name, function() {\n              return cb(new Error('New Module is instable, restored to previous version'));\n            });\n          }\n          return cb(null, data);\n        });\n      }, time);\n    }\n\n    return cb(null, data);\n  });\n};\n\n\n\nvar Rollback = {\n  revert : function(CLI, module_name, cb) {\n    var canonic_module_name = Utility.getCanonicModuleName(module_name);\n    var backup_path = path.join(require('os').tmpdir(), canonic_module_name);\n    var module_path = path.join(cst.DEFAULT_MODULE_PATH, canonic_module_name);\n\n    try {\n      fs.statSync(backup_path)\n    } catch(e) {\n      return cb(new Error('no backup found'));\n    }\n\n    Common.printOut(cst.PREFIX_MSG_MOD + chalk.bold.red('[[[[[ Module installation failure! ]]]]]'));\n    Common.printOut(cst.PREFIX_MSG_MOD + chalk.bold.red('[RESTORING TO PREVIOUS VERSION]'));\n\n    CLI.deleteModule(canonic_module_name, function() {\n      // Delete failing module\n\n      if (module_name.includes('modules') === true)\n        deleteFolderRecursive(module_path)\n      // Restore working version\n      copydirSync(backup_path, path.join(cst.DEFAULT_MODULE_PATH, canonic_module_name));\n\n      var proc_path = path.join(module_path, 'node_modules', canonic_module_name);\n      var package_json_path = path.join(proc_path, 'package.json');\n\n      // Start module\n      StartModule(CLI, {\n        cmd : package_json_path,\n        development_mode : false,\n        proc_path : proc_path\n      }, cb);\n    });\n  },\n  backup : function(module_name) {\n    // Backup current module\n    var tmpdir = require('os').tmpdir();\n    var canonic_module_name = Utility.getCanonicModuleName(module_name);\n    var module_path = path.join(cst.DEFAULT_MODULE_PATH, canonic_module_name);\n    copydirSync(module_path, path.join(tmpdir, canonic_module_name));\n  }\n}\n"
  },
  {
    "path": "lib/API/Modules/TAR.js",
    "content": "\nvar Configuration = require('../../Configuration.js');\nvar cst = require('../../../constants.js');\nvar Common = require('../../Common');\nvar forEachLimit  = require('async/forEachLimit');\nconst sexec = require('../../tools/sexec.js');\nconst deleteFolderRecursive = require('../../tools/deleteFolderRecursive.js');\n\nvar path = require('path');\nvar fs = require('fs');\nvar os = require('os');\nvar spawn = require('child_process').spawn;\nvar exec = require('child_process').exec;\nvar execSync = require('child_process').execSync;\n\nmodule.exports = {\n  install,\n  uninstall,\n  start,\n  publish,\n  packager\n}\n\n/**\n * Module management to manage tarball packages\n *\n * pm2 install http.tar.gz\n * pm2 uninstall http\n *\n * - the first and only folder in the tarball must be called module (tar zcvf http module/)\n * - a package.json must be present with attribute \"name\", \"version\" and \"pm2\" to declare apps to run\n */\n\nfunction install(PM2, module_filepath, opts, cb) {\n  // Remote file retrieval\n  if (module_filepath.includes('http') === true) {\n    var target_file = module_filepath.split('/').pop()\n    var target_filepath = path.join(os.tmpdir(), target_file)\n\n    opts.install_url = module_filepath\n\n    return retrieveRemote(module_filepath, target_filepath, (err) => {\n      if (err) {\n        Common.errMod(err)\n        process.exit(1)\n      }\n      installLocal(PM2, target_filepath, opts, cb)\n    })\n  }\n\n  // Local install\n  installLocal(PM2, module_filepath, opts, cb)\n}\n\nfunction retrieveRemote(url, dest, cb) {\n  Common.logMod(`Retrieving remote package ${url}...`)\n\n  var wget = spawn('wget', [url, '-O', dest, '-q'], {\n    stdio : 'inherit',\n    env: process.env,\n    windowsHide: true,\n\t\tshell : true\n  })\n\n  wget.on('error', (err) => {\n    console.error(err.stack || err)\n  })\n\n  wget.on('close', (code) => {\n    if (code !== 0)\n      return cb(new Error('Could not download'))\n    return cb(null)\n  })\n}\n\nfunction installLocal(PM2, module_filepath, opts, cb) {\n  Common.logMod(`Installing package ${module_filepath}`)\n\n  // Get module name by unpacking the module/package.json only and read the name attribute\n  getModuleName(module_filepath, function(err, module_name) {\n    if (err) return cb(err)\n\n    Common.logMod(`Module name is ${module_name}`)\n\n    Common.logMod(`Depackaging module...`)\n\n    var install_path = path.join(cst.DEFAULT_MODULE_PATH, module_name);\n\n    require('mkdirp').sync(install_path)\n\n    var install_instance = spawn('tar', ['zxf', module_filepath, '-C', install_path, '--strip-components 1'], {\n      stdio : 'inherit',\n      env: process.env,\n\t\t  shell : true\n    })\n\n    install_instance.on('close', function(code) {\n      Common.logMod(`Module depackaged in ${install_path}`)\n      if (code == 0)\n        return runInstall(PM2, install_path, module_name, opts, cb)\n      return PM2.exitCli(1)\n    });\n\n    install_instance.on('error', function (err) {\n      console.error(err.stack || err);\n    });\n  })\n}\n\nfunction deleteModulePath(module_name) {\n  var sanitized = module_name.replace(/\\./g, '')\n  deleteFolderRecursive(path.join(cst.DEFAULT_MODULE_PATH, module_name));\n}\n\nfunction runInstall(PM2, target_path, module_name, opts, cb) {\n  var config_file = path.join(target_path, 'package.json')\n  var conf\n\n  try {\n    conf = require(config_file)\n    module_name = conf.name\n  } catch(e) {\n    Common.errMod(new Error('Cannot find package.json file with name attribute at least'));\n  }\n\n  // Force with the name in the package.json\n  opts.started_as_module = true\n  opts.cwd = target_path\n\n  if (needPrefix(conf))\n    opts.name_prefix = module_name\n\n  if (opts.install) {\n    Common.logMod(`Running YARN install...`)\n\n    sexec(`cd ${target_path} ; yarn install`, {silent: false}, function(code) {\n      // Start apps under \"apps\" or \"pm2\" attribute\n      Common.logMod(`Starting ${target_path}`)\n      PM2.start(conf, opts, function(err, data) {\n        if (err) return cb(err)\n\n        Configuration.setSync(`${cst.MODULE_CONF_PREFIX_TAR}:${module_name}`, {\n          source: 'tarball',\n          install_url: opts.install_url,\n          installed_at: Date.now()\n        })\n\n        Common.logMod(`Module INSTALLED and STARTED`)\n        return cb(null, 'Module installed & Started')\n      })\n    })\n  }\n  else {\n    PM2.start(conf, opts, function(err, data) {\n      if (err) return cb(err)\n\n      Configuration.setSync(`${cst.MODULE_CONF_PREFIX_TAR}:${module_name}`, {\n        source: 'tarball',\n        install_url: opts.install_url,\n        installed_at: Date.now()\n      })\n\n      Common.logMod(`Module INSTALLED and STARTED`)\n      return cb(null, 'Module installed & Started')\n    })\n  }\n}\n\nfunction start(PM2, module_name, cb) {\n  var module_path = path.join(cst.DEFAULT_MODULE_PATH, module_name);\n  Common.printOut(cst.PREFIX_MSG_MOD + 'Starting TAR module ' + module_name);\n  var package_json_path = path.join(module_path, 'package.json');\n  var module_conf = Configuration.getSync(`${cst.MODULE_CONF_PREFIX_TAR}:${module_name}`)\n\n  try {\n    var conf = require(package_json_path)\n  } catch(e) {\n    Common.printError(`Could not find package.json as ${package_json_path}`)\n    return cb()\n  }\n\n  var opts = {};\n\n  opts.started_as_module = true\n  opts.cwd = module_path\n\n  if (module_conf.install_url)\n    opts.install_url = module_conf.install_url\n\n  if (needPrefix(conf))\n    opts.name_prefix = module_name\n\n  PM2.start(conf, opts, function(err, data) {\n    if (err) {\n      Common.printError(`Could not start ${module_name} ${module_path}`)\n      return cb()\n    }\n\n    Common.printOut(`${cst.PREFIX_MSG_MOD} Module ${module_name} STARTED`)\n    return cb();\n  })\n}\n\n/**\n * Retrieve from module package.json the name of each application\n * delete process and delete folder\n */\nfunction uninstall(PM2, module_name, cb) {\n  var module_path = path.join(cst.DEFAULT_MODULE_PATH, module_name);\n\n  Common.logMod(`Removing ${module_name} from auto startup`)\n\n  try {\n    var pkg = require(path.join(module_path, 'package.json'))\n  } catch(e) {\n    Common.errMod('Could not retrieve module package.json');\n    return cb(e)\n  }\n\n  var apps = pkg.apps || pkg.pm2\n  apps = [].concat(apps);\n\n  /**\n   * Some time a module can have multiple processes\n   */\n  forEachLimit(apps, 1, (app, next) => {\n    var app_name\n\n    if (!app.name) {\n      Common.renderApplicationName(app)\n    }\n\n    if (apps.length > 1)\n      app_name = `${module_name}:${app.name}`\n    else if (apps.length == 1 && pkg.name != apps[0].name)\n      app_name = `${module_name}:${app.name}`\n    else\n      app_name = app.name\n\n    PM2._operate('deleteProcessId', app_name, () => {\n      deleteModulePath(module_name)\n      next()\n    })\n  }, () => {\n    Configuration.unsetSync(`${cst.MODULE_CONF_PREFIX_TAR}:${module_name}`)\n    cb(null)\n  })\n}\n\n\n/**\n * Uncompress only module/package.json and retrieve the \"name\" attribute in the package.json\n */\nfunction getModuleName(module_filepath, cb) {\n  var tmp_folder = path.join(os.tmpdir(), cst.MODULE_BASEFOLDER)\n\n  var install_instance = spawn('tar', ['zxf', module_filepath, '-C', os.tmpdir(), `${cst.MODULE_BASEFOLDER}/package.json`], {\n    stdio : 'inherit',\n    env: process.env,\n\t\tshell : true\n  })\n\n  install_instance.on('close', function(code) {\n    try {\n      var pkg = JSON.parse(fs.readFileSync(path.join(tmp_folder, `package.json`)))\n      return cb(null, pkg.name)\n    } catch(e) {\n      return cb(e)\n    }\n  });\n}\n\nfunction packager(module_path, target_path, cb) {\n  var base_folder = path.dirname(module_path)\n  var module_folder_name = path.basename(module_path)\n  var pkg = require(path.join(module_path, 'package.json'))\n  var pkg_name = `${module_folder_name}-v${pkg.version.replace(/\\./g, '-')}.tar.gz`\n  var target_fullpath = path.join(target_path, pkg_name)\n\n  var cmd = `tar zcf ${target_fullpath} -C ${base_folder} --transform 's,${module_folder_name},module,' ${module_folder_name}`\n\n  Common.logMod(`Gziping ${module_path} to ${target_fullpath}`)\n\n  var tar = exec(cmd, (err, sto, ste) => {\n    if (err) {\n      console.error(sto.toString().trim())\n      console.error(ste.toString().trim())\n    }\n  })\n\n  tar.on('close', function (code) {\n    cb(code == 0 ? null : code, {\n      package_name: pkg_name,\n      path: target_fullpath\n    })\n  })\n}\n\nfunction publish(PM2, folder, cb) {\n  var target_folder = folder ? path.resolve(folder) : process.cwd()\n\n  try {\n    var pkg = JSON.parse(fs.readFileSync(path.join(target_folder, 'package.json')).toString())\n  } catch(e) {\n    Common.errMod(`${process.cwd()} module does not contain any package.json`)\n    process.exit(1)\n  }\n\n  if (!pkg.name) throw new Error('Attribute name should be present')\n  if (!pkg.version) throw new Error('Attribute version should be present')\n  if (!pkg.pm2 && !pkg.apps) throw new Error('Attribute apps should be present')\n\n  var current_path = target_folder\n  var module_name = path.basename(current_path)\n  var target_path = os.tmpdir()\n\n  Common.logMod(`Starting publishing procedure for ${module_name}@${pkg.version}`)\n\n  packager(current_path, target_path, (err, res) => {\n    if (err) {\n      Common.errMod('Can\\'t package, exiting')\n      process.exit(1)\n    }\n\n    Common.logMod(`Package [${pkg.name}] created in path ${res.path}`)\n\n    var data = {\n      module_data: {\n        file: res.path,\n        content_type: 'content/gzip'\n      },\n      id: pkg.name,\n      name: pkg.name,\n      version: pkg.version\n    };\n\n    var uri = `${PM2.pm2_configuration.registry}/api/v1/modules`\n    Common.logMod(`Sending Package to remote ${pkg.name} ${uri}`)\n\n    require('needle')\n      .post(uri, data, { multipart: true }, function(err, res, body) {\n        if (err) {\n          Common.errMod(err)\n          process.exit(1)\n        }\n        if (res.statusCode !== 200) {\n          Common.errMod(`${pkg.name}-${pkg.version}: ${res.body.msg}`)\n          process.exit(1)\n        }\n        Common.logMod(`Module ${module_name} published under version ${pkg.version}`)\n        process.exit(0)\n      })\n  })\n}\n\nfunction needPrefix(conf) {\n  if ((conf.apps && conf.apps.length > 1) ||\n      (conf.pm2 && conf.pm2.length > 1) ||\n      (conf.apps.length == 1 && conf.name != conf.apps[0].name))\n    return true\n  return false\n}\n"
  },
  {
    "path": "lib/API/Modules/flagExt.js",
    "content": "var fs          = require('fs');\nvar conf         = require('../../../constants.js');\n\nfunction  find_extensions(folder, ext, ret)\n{\n    try {\n      fs.accessSync(folder, fs.constants.R_OK);\n    } catch (err) {\n      return;\n    }\n    if(fs.statSync(folder).isDirectory() && folder.indexOf('node_modules') == -1 && (fs.statSync(folder)[\"mode\"] & 4))\n    {\n      fs.readdirSync(folder).forEach(file => {\n          var tmp;\n          if(Number.parseInt(folder.lastIndexOf('/') + 1) === folder.length)\n            tmp = folder + file;\n          else\n            tmp = folder + '/' + file;\n          if(fs.statSync(tmp).isDirectory())\n            find_extensions(tmp, ext, ret);\n          else\n          {\n          var p = true;\n          for(var i = 0; i < ext.length;i++)\n            if(ext[i].test(file))\n              p = false;\n          if(p)\n            ret.push(folder +  '/' + file);\n          }\n      });\n  }\n}\n\nmodule.exports.make_available_extension = function  make_available_extension(opts, ret)\n{\n  if(typeof opts == 'object'  && typeof ret == 'object')\n  {\n    var mas = opts.ext.split(',');\n    for(var i = 0;i < mas.length;i++)\n      mas[i] = '.' + mas[i];\n    var res = [];\n    for(var i = 0;i < mas.length;i++)\n      res[i] = new RegExp(mas[i] + '$');\n    find_extensions(process.cwd(), res, ret);\n  }\n}\n"
  },
  {
    "path": "lib/API/Modules/index.js",
    "content": "\n/***************************\n *\n * Module methods\n *\n **************************/\n\nvar cst          = require('../../../constants.js');\nvar Common       = require('../../Common.js');\nvar chalk        = require('ansis');\nvar forEachLimit = require('async/forEachLimit');\n\nvar Modularizer = require('./Modularizer.js');\n\nmodule.exports = function(CLI) {\n  /**\n   * Install / Update a module\n   */\n  CLI.prototype.install = function(module_name, opts, cb) {\n    var that = this;\n\n    if (typeof(opts) == 'function') {\n      cb = opts;\n      opts = {};\n    }\n\n    Modularizer.install(this, module_name, opts, function(err, data) {\n      if (err) {\n        Common.printError(cst.PREFIX_MSG_ERR + (err.message || err));\n        return cb ? cb(Common.retErr(err)) : that.speedList(cst.ERROR_EXIT);\n      }\n      return cb ? cb(null, data) : that.speedList(cst.SUCCESS_EXIT);\n    });\n  };\n\n  /**\n   * Uninstall a module\n   */\n  CLI.prototype.uninstall = function(module_name, cb) {\n    var that = this;\n\n    Modularizer.uninstall(this, module_name, function(err, data) {\n      if (err)\n        return cb ? cb(Common.retErr(err)) : that.speedList(cst.ERROR_EXIT);\n      return cb ? cb(null, data) : that.speedList(cst.SUCCESS_EXIT);\n    });\n  };\n\n  CLI.prototype.launchAll = function(CLI, cb) {\n    Modularizer.launchModules(CLI, cb);\n  };\n\n  CLI.prototype.package = function(module_path, cb) {\n    Modularizer.package(this, module_path, (err, res) => {\n      if (err) {\n        Common.errMod(err)\n        return cb ? cb(err) : this.exitCli(1)\n      }\n      Common.logMod(`Module packaged in ${res.path}`)\n      return cb ? cb(err) : this.exitCli(0)\n    })\n  };\n\n  /**\n   * Publish module on NPM + Git push\n   */\n  CLI.prototype.publish = function(folder, opts, cb) {\n    var that = this;\n\n    Modularizer.publish(this, folder, opts, function(err, data) {\n      if (err)\n        return cb ? cb(Common.retErr(err)) : that.speedList(cst.ERROR_EXIT);\n      return cb ? cb(null, data) : that.speedList(cst.SUCCESS_EXIT);\n    });\n  };\n\n  /**\n   * Publish module on NPM + Git push\n   */\n  CLI.prototype.generateModuleSample = function(app_name, cb) {\n    var that = this;\n\n    Modularizer.generateSample(app_name, function(err, data) {\n      if (err)\n        return cb ? cb(Common.retErr(err)) : that.exitCli(cst.ERROR_EXIT);\n      return cb ? cb(null, data) : that.exitCli(cst.SUCCESS_EXIT);\n    });\n  };\n\n  /**\n   * Special delete method\n   */\n  CLI.prototype.deleteModule = function(module_name, cb) {\n    var that = this;\n\n    var found_proc = [];\n\n    this.Client.getAllProcess(function(err, procs) {\n      if (err) {\n        Common.printError('Error retrieving process list: ' + err);\n        return cb(Common.retErr(err));\n      }\n\n      procs.forEach(function(proc) {\n        if (proc.pm2_env.name == module_name && proc.pm2_env.pmx_module) {\n          found_proc.push(proc.pm_id);\n        }\n      });\n\n      if (found_proc.length == 0)\n        return cb();\n\n      that._operate('deleteProcessId', found_proc[0], function(err) {\n        if (err) return cb(Common.retErr(err));\n        Common.printOut('In memory process deleted');\n        return cb();\n      });\n    });\n  };\n};\n"
  },
  {
    "path": "lib/API/Monit.js",
    "content": "/**\n * Copyright 2013-2022 the PM2 project authors. All rights reserved.\n * Use of this source code is governed by a license that\n * can be found in the LICENSE file.\n */\n// pm2-htop\n// Library who interacts with PM2 to display processes resources in htop way\n// by Strzelewicz Alexandre\n\nvar multimeter = require('pm2-multimeter');\nvar os         = require('os');\nvar p          = require('path');\nvar chalk      = require('ansis');\n\nvar UX      = require('./UX');\n\nvar debug = require('debug')('pm2:monit');\n\n// Cst for light programs\nconst RATIO_T1   = Math.floor(os.totalmem() / 500);\n// Cst for medium programs\nconst RATIO_T2   = Math.floor(os.totalmem() / 50);\n// Cst for heavy programs\nconst RATIO_T3   = Math.floor(os.totalmem() / 5);\n// Cst for heavy programs\nconst RATIO_T4   = Math.floor(os.totalmem());\n\nvar Monit = {};\n\n//helper to get bars.length (num bars printed)\nObject.size = function(obj) {\n    var size = 0, key;\n    for (key in obj) {\n        if (obj.hasOwnProperty(key)) size++;\n    }\n    return size;\n};\n\n/**\n * Reset the monitor through charm, basically \\033c\n * @param  String msg optional message to show\n * @return Monit\n */\nMonit.reset = function(msg) {\n\n  this.multi.charm.reset();\n\n  this.multi.write('\\x1B[32m⌬ PM2 \\x1B[39mmonitoring\\x1B[96m (To go further check out https://app.pm2.io) \\x1B[39m\\n\\n');\n\n  if(msg) {\n    this.multi.write(msg);\n  }\n\n  this.bars = {};\n\n  return this;\n}\n\n/**\n * Synchronous Monitor init method\n * @method init\n * @return Monit\n */\nMonit.init = function() {\n\n  this.multi = multimeter(process);\n\n  this.multi.on('^C', this.stop);\n\n  this.reset();\n\n  return this;\n}\n\n/**\n * Stops monitor\n * @method stop\n */\nMonit.stop = function() {\n  this.multi.charm.destroy();\n  process.exit(0);\n}\n\n\n/**\n * Refresh monitor\n * @method refresh\n * @param {} processes\n * @return this\n */\nMonit.refresh = function(processes) {\n  debug('Monit refresh');\n\n  if(!processes) {\n    processes = [];\n  }\n\n  var num = processes.length;\n  this.num_bars = Object.size(this.bars);\n\n  if(num !== this.num_bars) {\n    debug('Monit addProcesses - actual: %s, new: %s', this.num_bars, num);\n    return this.addProcesses(processes);\n  } else {\n\n    if(num === 0) {\n      return;\n    }\n\n    debug('Monit refresh');\n    var proc;\n\n    for(var i = 0; i < num; i++) {\n      proc = processes[i];\n\n      //this is to avoid a print issue when the process is restarted for example\n      //we might also check for the pid but restarted|restarting will be rendered bad\n      if(this.bars[proc.pm_id] && proc.pm2_env.status !== this.bars[proc.pm_id].status) {\n        debug('bars for %s does not exist', proc.pm_id);\n        this.addProcesses(processes);\n        break;\n      }\n\n      this.updateBars(proc);\n\n    }\n  }\n\n  return this;\n}\n\nMonit.addProcess = function(proc, i) {\n  if(proc.pm_id in this.bars) {\n    return ;\n  }\n\n  if (proc.monit.error)\n    throw new Error(JSON.stringify(proc.monit.error));\n\n  var process_name = proc.pm2_env.name || p.basename(proc.pm2_env.pm_exec_path);\n  var status = proc.pm2_env.status == 'online' ? chalk.green.bold('●') : chalk.red.bold('●');\n\n  this.multi.write(' ' + status + ' ' + chalk.green.bold(process_name));\n  this.multi.write('\\n');\n  this.multi.write('[' + proc.pm2_env.pm_id + '] [' + proc.pm2_env.exec_mode + ']\\n');\n\n  var bar_cpu = this.multi(40, (i * 2) + 3 + i, {\n    width: 30,\n    solid: {\n      text: '|',\n      foreground: 'white',\n      background: 'blue'\n    },\n    empty: {\n      text: ' '\n    }\n  });\n\n  var bar_memory = this.multi(40, (i * 2) + 4 + i, {\n    width: 30,\n    solid: {\n      text: '|',\n      foreground: 'white',\n      background: 'red'\n    },\n    empty: {\n      text: ' '\n    }\n  });\n\n  this.bars[proc.pm_id] = {\n    memory: bar_memory,\n    cpu: bar_cpu,\n    status: proc.pm2_env.status\n  };\n\n  this.updateBars(proc);\n\n  this.multi.write('\\n');\n\n  return this;\n}\n\nMonit.addProcesses = function(processes) {\n\n  if(!processes) {\n    processes = [];\n  }\n\n  this.reset();\n\n  var num = processes.length;\n\n  if(num > 0) {\n    for(var i = 0; i < num; i++) {\n      this.addProcess(processes[i], i);\n    }\n  } else {\n    this.reset('No processes to monit');\n  }\n\n}\n\n// Draw memory bars\n/**\n * Description\n * @method drawRatio\n * @param {} bar_memory\n * @param {} memory\n * @return\n */\nMonit.drawRatio = function(bar_memory, memory) {\n  var scale = 0;\n\n  if (memory < RATIO_T1) scale = RATIO_T1;\n  else if (memory < RATIO_T2) scale = RATIO_T2;\n  else if (memory < RATIO_T3) scale = RATIO_T3;\n  else scale = RATIO_T4;\n\n  bar_memory.ratio(memory,\n\t\t   scale,\n\t\t   UX.helpers.bytesToSize(memory, 3));\n};\n\n/**\n * Updates bars informations\n * @param  {} proc       proc object\n * @return  this\n */\nMonit.updateBars = function(proc) {\n  if (this.bars[proc.pm_id]) {\n    if (proc.pm2_env.status !== 'online' || proc.pm2_env.status !== this.bars[proc.pm_id].status) {\n      this.bars[proc.pm_id].cpu.percent(0, chalk.red(proc.pm2_env.status));\n      this.drawRatio(this.bars[proc.pm_id].memory, 0, chalk.red(proc.pm2_env.status));\n    } else if (!proc.monit) {\n      this.bars[proc.pm_id].cpu.percent(0, chalk.red('No data'));\n      this.drawRatio(this.bars[proc.pm_id].memory, 0, chalk.red('No data'));\n    } else {\n      this.bars[proc.pm_id].cpu.percent(proc.monit.cpu);\n      this.drawRatio(this.bars[proc.pm_id].memory, proc.monit.memory);\n    }\n  }\n\n  return this;\n}\n\nmodule.exports = Monit;\n"
  },
  {
    "path": "lib/API/Serve.js",
    "content": "/**\n * Copyright 2013-2022 the PM2 project authors. All rights reserved.\n * Use of this source code is governed by a license that\n * can be found in the LICENSE file.\n */\n'use strict';\n\nvar fs = require('fs');\nvar http = require('http');\nvar url = require('url');\nvar path = require('path');\nvar debug = require('debug')('pm2:serve');\n\n// var probe = require('@pm2/io');\n// var errorMeter = probe.meter({\n//   name      : '404/sec',\n//   samples   : 1,\n//   timeframe : 60\n// })\n/**\n * list of supported content types.\n */\nvar contentTypes = {\n  '3gp': 'video/3gpp',\n  'a': 'application/octet-stream',\n  'ai': 'application/postscript',\n  'aif': 'audio/x-aiff',\n  'aiff': 'audio/x-aiff',\n  'asc': 'application/pgp-signature',\n  'asf': 'video/x-ms-asf',\n  'asm': 'text/x-asm',\n  'asx': 'video/x-ms-asf',\n  'atom': 'application/atom+xml',\n  'au': 'audio/basic',\n  'avi': 'video/x-msvideo',\n  'bat': 'application/x-msdownload',\n  'bin': 'application/octet-stream',\n  'bmp': 'image/bmp',\n  'bz2': 'application/x-bzip2',\n  'c': 'text/x-c',\n  'cab': 'application/vnd.ms-cab-compressed',\n  'cc': 'text/x-c',\n  'chm': 'application/vnd.ms-htmlhelp',\n  'class': 'application/octet-stream',\n  'com': 'application/x-msdownload',\n  'conf': 'text/plain',\n  'cpp': 'text/x-c',\n  'crt': 'application/x-x509-ca-cert',\n  'css': 'text/css',\n  'csv': 'text/csv',\n  'cxx': 'text/x-c',\n  'deb': 'application/x-debian-package',\n  'der': 'application/x-x509-ca-cert',\n  'diff': 'text/x-diff',\n  'djv': 'image/vnd.djvu',\n  'djvu': 'image/vnd.djvu',\n  'dll': 'application/x-msdownload',\n  'dmg': 'application/octet-stream',\n  'doc': 'application/msword',\n  'dot': 'application/msword',\n  'dtd': 'application/xml-dtd',\n  'dvi': 'application/x-dvi',\n  'ear': 'application/java-archive',\n  'eml': 'message/rfc822',\n  'eps': 'application/postscript',\n  'exe': 'application/x-msdownload',\n  'f': 'text/x-fortran',\n  'f77': 'text/x-fortran',\n  'f90': 'text/x-fortran',\n  'flv': 'video/x-flv',\n  'for': 'text/x-fortran',\n  'gem': 'application/octet-stream',\n  'gemspec': 'text/x-script.ruby',\n  'gif': 'image/gif',\n  'gz': 'application/x-gzip',\n  'h': 'text/x-c',\n  'hh': 'text/x-c',\n  'htm': 'text/html',\n  'html': 'text/html',\n  'ico': 'image/vnd.microsoft.icon',\n  'ics': 'text/calendar',\n  'ifb': 'text/calendar',\n  'iso': 'application/octet-stream',\n  'jar': 'application/java-archive',\n  'java': 'text/x-java-source',\n  'jnlp': 'application/x-java-jnlp-file',\n  'jpeg': 'image/jpeg',\n  'jpg': 'image/jpeg',\n  'js': 'application/javascript',\n  'json': 'application/json',\n  'log': 'text/plain',\n  'm3u': 'audio/x-mpegurl',\n  'm4v': 'video/mp4',\n  'man': 'text/troff',\n  'mathml': 'application/mathml+xml',\n  'mbox': 'application/mbox',\n  'mdoc': 'text/troff',\n  'me': 'text/troff',\n  'mid': 'audio/midi',\n  'midi': 'audio/midi',\n  'mime': 'message/rfc822',\n  'mml': 'application/mathml+xml',\n  'mng': 'video/x-mng',\n  'mov': 'video/quicktime',\n  'mp3': 'audio/mpeg',\n  'mp4': 'video/mp4',\n  'mp4v': 'video/mp4',\n  'mpeg': 'video/mpeg',\n  'mpg': 'video/mpeg',\n  'ms': 'text/troff',\n  'msi': 'application/x-msdownload',\n  'odp': 'application/vnd.oasis.opendocument.presentation',\n  'ods': 'application/vnd.oasis.opendocument.spreadsheet',\n  'odt': 'application/vnd.oasis.opendocument.text',\n  'ogg': 'application/ogg',\n  'p': 'text/x-pascal',\n  'pas': 'text/x-pascal',\n  'pbm': 'image/x-portable-bitmap',\n  'pdf': 'application/pdf',\n  'pem': 'application/x-x509-ca-cert',\n  'pgm': 'image/x-portable-graymap',\n  'pgp': 'application/pgp-encrypted',\n  'pkg': 'application/octet-stream',\n  'pl': 'text/x-script.perl',\n  'pm': 'text/x-script.perl-module',\n  'png': 'image/png',\n  'pnm': 'image/x-portable-anymap',\n  'ppm': 'image/x-portable-pixmap',\n  'pps': 'application/vnd.ms-powerpoint',\n  'ppt': 'application/vnd.ms-powerpoint',\n  'ps': 'application/postscript',\n  'psd': 'image/vnd.adobe.photoshop',\n  'py': 'text/x-script.python',\n  'qt': 'video/quicktime',\n  'ra': 'audio/x-pn-realaudio',\n  'rake': 'text/x-script.ruby',\n  'ram': 'audio/x-pn-realaudio',\n  'rar': 'application/x-rar-compressed',\n  'rb': 'text/x-script.ruby',\n  'rdf': 'application/rdf+xml',\n  'roff': 'text/troff',\n  'rpm': 'application/x-redhat-package-manager',\n  'rss': 'application/rss+xml',\n  'rtf': 'application/rtf',\n  'ru': 'text/x-script.ruby',\n  's': 'text/x-asm',\n  'sgm': 'text/sgml',\n  'sgml': 'text/sgml',\n  'sh': 'application/x-sh',\n  'sig': 'application/pgp-signature',\n  'snd': 'audio/basic',\n  'so': 'application/octet-stream',\n  'svg': 'image/svg+xml',\n  'svgz': 'image/svg+xml',\n  'swf': 'application/x-shockwave-flash',\n  't': 'text/troff',\n  'tar': 'application/x-tar',\n  'tbz': 'application/x-bzip-compressed-tar',\n  'tcl': 'application/x-tcl',\n  'tex': 'application/x-tex',\n  'texi': 'application/x-texinfo',\n  'texinfo': 'application/x-texinfo',\n  'text': 'text/plain',\n  'tif': 'image/tiff',\n  'tiff': 'image/tiff',\n  'torrent': 'application/x-bittorrent',\n  'tr': 'text/troff',\n  'txt': 'text/plain',\n  'vcf': 'text/x-vcard',\n  'vcs': 'text/x-vcalendar',\n  'vrml': 'model/vrml',\n  'war': 'application/java-archive',\n  'wav': 'audio/x-wav',\n  'webp': 'image/webp',\n  'wma': 'audio/x-ms-wma',\n  'wmv': 'video/x-ms-wmv',\n  'wmx': 'video/x-ms-wmx',\n  'wrl': 'model/vrml',\n  'wsdl': 'application/wsdl+xml',\n  'xbm': 'image/x-xbitmap',\n  'xhtml': 'application/xhtml+xml',\n  'xls': 'application/vnd.ms-excel',\n  'xml': 'application/xml',\n  'xpm': 'image/x-xpixmap',\n  'xsl': 'application/xml',\n  'xslt': 'application/xslt+xml',\n  'yaml': 'text/yaml',\n  'yml': 'text/yaml',\n  'zip': 'application/zip',\n  'woff': 'application/font-woff',\n  'woff2': 'application/font-woff',\n  'otf': 'application/font-sfnt',\n  'otc': 'application/font-sfnt',\n  'ttf': 'application/font-sfnt'\n};\n\nvar options = {\n  port: process.env.PM2_SERVE_PORT || process.argv[3] || 8080,\n  host: process.env.PM2_SERVE_HOST || process.argv[4] || '0.0.0.0',\n  path: path.resolve(process.env.PM2_SERVE_PATH || process.argv[2] || '.'),\n  spa: process.env.PM2_SERVE_SPA === 'true',\n  homepage: process.env.PM2_SERVE_HOMEPAGE || '/index.html',\n  basic_auth: process.env.PM2_SERVE_BASIC_AUTH === 'true' ? {\n    username: process.env.PM2_SERVE_BASIC_AUTH_USERNAME,\n    password: process.env.PM2_SERVE_BASIC_AUTH_PASSWORD\n  } : null,\n  monitor: process.env.PM2_SERVE_MONITOR\n};\n\nif (typeof options.port === 'string') {\n  options.port = parseInt(options.port) || 8080\n}\n\nif (typeof options.monitor === 'string' && options.monitor !== '') {\n  try {\n    let fileContent = fs.readFileSync(path.join(process.env.PM2_HOME, 'agent.json5')).toString()\n    // Handle old configuration with json5\n    fileContent = fileContent.replace(/\\s(\\w+):/g, '\"$1\":')\n    // parse\n    let conf = JSON.parse(fileContent)\n    options.monitorBucket = conf.public_key\n  } catch (e) {\n    console.log('Interaction file does not exist')\n  }\n}\n\n// start an HTTP server\nhttp.createServer(function (request, response) {\n  if (options.basic_auth) {\n    if (!request.headers.authorization || request.headers.authorization.indexOf('Basic ') === -1) {\n      return sendBasicAuthResponse(response)\n    }\n\n    var user = parseBasicAuth(request.headers.authorization)\n    if (user.username !== options.basic_auth.username || user.password !== options.basic_auth.password) {\n      return sendBasicAuthResponse(response)\n    }\n  }\n\n  serveFile(request.url, request, response);\n\n}).listen(options.port, options.host, function (err) {\n  if (err) {\n    console.error(err);\n    process.exit(1);\n  }\n  console.log('Exposing %s directory on %s:%d', options.path, options.host, options.port);\n});\n\nfunction serveFile(uri, request, response) {\n  var file = decodeURIComponent(url.parse(uri || request.url).pathname);\n\n  if (file === '/' || file === '') {\n    file = options.homepage;\n    request.wantHomepage = true;\n  }\n  var filePath = path.resolve(options.path + file);\n\n  // since we call filesystem directly so we need to verify that the\n  // url doesn't go outside the serve path\n  if (filePath.indexOf(options.path) !== 0) {\n    response.writeHead(403, { 'Content-Type': 'text/html' });\n    return response.end('403 Forbidden');\n  }\n\n  var contentType = contentTypes[filePath.split('.').pop().toLowerCase()] || 'text/plain';\n\n  fs.readFile(filePath, function (error, content) {\n    if (error) {\n      if ((!options.spa || file === options.homepage)) {\n        console.error('[%s] Error while serving %s with content-type %s : %s',\n                      new Date(), filePath, contentType, error.message || error);\n      }\n      //errorMeter.mark();\n      if (error.code === 'ENOENT') {\n        if (options.spa && !request.wantHomepage) {\n          request.wantHomepage = true;\n          return serveFile(`/${path.basename(file)}`, request, response);\n        } else if (options.spa && file !== options.homepage) {\n          return serveFile(options.homepage, request, response);\n        }\n        fs.readFile(options.path + '/404.html', function (err, content) {\n          content = err ? '404 Not Found' : content;\n          response.writeHead(404, { 'Content-Type': 'text/html' });\n          return response.end(content, 'utf-8');\n        });\n        return;\n      }\n      response.writeHead(500);\n      return response.end('Sorry, check with the site admin for error: ' + error.code + ' ..\\n');\n    }\n\n    // Add CORS headers to allow browsers to fetch data directly\n    response.writeHead(200, {\n      'Content-Type': contentType,\n      'Access-Control-Allow-Origin': '*',\n      'Access-Control-Allow-Methods': 'GET'\n    });\n    if (options.monitorBucket && contentType === 'text/html') {\n      content = content.toString().replace('</body>', `\n<script>\n;(function (b,e,n,o,i,t) {\n  b[o]=b[o]||function(f){(b[o].c=b[o].c||[]).push(f)};\n  t=e.createElement(i);e=e.getElementsByTagName(i)[0];\n  t.async=1;t.src=n;e.parentNode.insertBefore(t,e);\n}(window,document,'https://apm.pm2.io/pm2-io-apm-browser.v1.js','pm2Ready','script'))\n\npm2Ready(function(apm) {\n  apm.setBucket('${options.monitorBucket}')\n  apm.setApplication('${options.monitor}')\n  apm.reportTimings()\n  apm.reportIssues()\n})\n</script>\n</body>\n`);\n    }\n    response.end(content, 'utf-8');\n    debug('[%s] Serving %s with content-type %s', Date.now(), filePath, contentType);\n  });\n}\n\nfunction parseBasicAuth(auth) {\n  // auth is like `Basic Y2hhcmxlczoxMjM0NQ==`\n  var tmp = auth.split(' ');\n\n  var buf = Buffer.from(tmp[1], 'base64');\n  var plain = buf.toString();\n\n  var creds = plain.split(':');\n  return {\n    username: creds[0],\n    password: creds[1]\n  }\n}\n\nfunction sendBasicAuthResponse(response) {\n  response.writeHead(401, {\n    'Content-Type': 'text/html',\n    'WWW-Authenticate': 'Basic realm=\"Authentication service\"'\n  });\n  return response.end('401 Unauthorized');\n}\n"
  },
  {
    "path": "lib/API/Startup.js",
    "content": "/**\n * Copyright 2013-2022 the PM2 project authors. All rights reserved.\n * Use of this source code is governed by a license that\n * can be found in the LICENSE file.\n */\nvar chalk        = require('ansis');\nvar path         = require('path');\nvar fs           = require('fs');\nvar forEachLimit = require('async/forEachLimit');\nvar eachLimit    = require('async/eachLimit');\nvar Common       = require('../Common.js');\nvar cst          = require('../../constants.js');\nvar util \t       = require('util');\nvar tmpPath      = require('os').tmpdir;\nvar which        = require('../tools/which.js');\nvar sexec = require('../tools/sexec')\nmodule.exports = function(CLI) {\n  /**\n   * If command is launched without root right\n   * Display helper\n   */\n  function isNotRoot(startup_mode, platform, opts, cb) {\n    Common.printOut(`${cst.PREFIX_MSG}To ${startup_mode} the Startup Script, copy/paste the following command:`);\n\n    let pm2_bin_path = require.main.filename\n\n    if (pm2_bin_path.includes('/lib/binaries/CLI.js') === true) {\n      pm2_bin_path = pm2_bin_path.replace('/lib/binaries/CLI.js', '/bin/pm2')\n    }\n\n    if (opts.user) {\n      console.log('sudo env PATH=$PATH:' + path.dirname(process.execPath) + ' pm2 ' + opts.args[1].name() + ' ' + platform + ' -u ' + opts.user + ' --hp ' + process.env.HOME);\n      return cb(new Error('You have to run this with elevated rights'));\n    }\n    return sexec('whoami', {silent: true}, function(err, stdout, stderr) {\n      console.log('sudo env PATH=$PATH:' + path.dirname(process.execPath) + ' ' + pm2_bin_path + ' ' + opts.args[1].name() + ' ' + platform + ' -u ' + stdout.trim() + ' --hp ' + process.env.HOME);\n      return cb(new Error('You have to run this with elevated rights'));\n    });\n  }\n\n  /**\n   * Detect running init system\n   */\n  function detectInitSystem() {\n    var hash_map = {\n      'systemctl'  : 'systemd',\n      'update-rc.d': 'upstart',\n      'chkconfig'  : 'systemv',\n      'rc-update'  : 'openrc',\n      'launchctl'  : 'launchd',\n      'sysrc'      : 'rcd',\n      'rcctl'      : 'rcd-openbsd',\n      'svcadm'     : 'smf'\n    };\n    var init_systems = Object.keys(hash_map);\n\n    for (var i = 0; i < init_systems.length; i++) {\n      if (which(init_systems[i]) != null) {\n        break;\n      }\n    }\n\n    if (i >= init_systems.length) {\n      Common.printError(cst.PREFIX_MSG_ERR + 'Init system not found');\n      return null;\n    }\n    Common.printOut(cst.PREFIX_MSG + 'Init System found: ' + chalk.bold(hash_map[init_systems[i]]));\n    return hash_map[init_systems[i]];\n  }\n\n  CLI.prototype.uninstallStartup = function(platform, opts, cb) {\n    var commands;\n    var that = this;\n    var actual_platform = detectInitSystem();\n    var user = opts.user || process.env.USER || process.env.LOGNAME; // Use LOGNAME on Solaris-like systems\n    var service_name = (opts.serviceName || 'pm2-' + user);\n    var openrc_service_name = 'pm2';\n    var launchd_service_name = (opts.serviceName || 'pm2.' + user);\n\n    if (!platform)\n      platform = actual_platform;\n    else if (actual_platform && actual_platform !== platform) {\n      Common.printOut('-----------------------------------------------------------')\n      Common.printOut(' PM2 detected ' + actual_platform + ' but you precised ' + platform)\n      Common.printOut(' Please verify that your choice is indeed your init system')\n      Common.printOut(' If you arent sure, just run : pm2 startup')\n      Common.printOut('-----------------------------------------------------------')\n    }\n    if (platform === null)\n      throw new Error('Init system not found')\n\n    if (!cb) {\n      cb = function(err, data) {\n        if (err)\n          return that.exitCli(cst.ERROR_EXIT);\n        return that.exitCli(cst.SUCCESS_EXIT);\n      }\n    }\n\n    if (process.getuid() != 0) {\n      return isNotRoot('unsetup', platform, opts, cb);\n    }\n\n    if (fs.existsSync('/etc/init.d/pm2-init.sh')) {\n      platform = 'oldsystem';\n    }\n\n    switch(platform) {\n    case 'systemd':\n      commands = [\n        'systemctl stop ' + service_name,\n        'systemctl disable ' + service_name,\n        'rm /etc/systemd/system/' + service_name + '.service'\n      ];\n      break;\n    case 'systemv':\n      commands = [\n        'chkconfig ' + service_name + ' off',\n        'rm /etc/init.d/' + service_name\n      ];\n      break;\n    case 'oldsystem':\n      Common.printOut(cst.PREFIX_MSG + 'Disabling and deleting old startup system');\n      commands = [\n        'update-rc.d pm2-init.sh disable',\n        'update-rc.d -f pm2-init.sh remove',\n        'rm /etc/init.d/pm2-init.sh'\n      ];\n      break;\n    case 'openrc':\n      service_name = openrc_service_name;\n      commands = [\n        '/etc/init.d/' + service_name + ' stop',\n        'rc-update delete ' + service_name + ' default',\n        'rm /etc/init.d/' + service_name\n      ];\n      break;\n    case 'upstart':\n      commands = [\n        'update-rc.d ' + service_name + ' disable',\n        'update-rc.d -f ' + service_name + ' remove',\n        'rm /etc/init.d/' + service_name\n      ];\n      break;\n    case 'launchd':\n      var destination = path.join(process.env.HOME, 'Library/LaunchAgents/' + launchd_service_name + '.plist');\n      commands = [\n        'launchctl remove ' + launchd_service_name + ' || true',\n        'rm ' + destination\n      ];\n      break;\n    case 'rcd':\n      service_name = (opts.serviceName || 'pm2_' + user);\n      commands = [\n        '/usr/local/etc/rc.d/' + service_name + ' stop',\n        'sysrc -x ' + service_name  + '_enable',\n        'rm /usr/local/etc/rc.d/' + service_name\n      ];\n      break;\n    case 'rcd-openbsd':\n      service_name = (opts.serviceName || 'pm2_' + user);\n      var destination = path.join('/etc/rc.d', service_name);\n      commands = [\n        'rcctl stop ' + service_name,\n        'rcctl disable ' + service_name,\n        'rm ' + destination\n      ];\n      break;\n    case 'smf':\n      service_name = (opts.serviceName || 'pm2_' + user);\n      commands = [\n        'svcadm disable ' + service_name,\n        'svccfg delete -f ' + service_name\n      ]\n    };\n\n    sexec(commands.join('&& '), function(code, stdout, stderr) {\n      Common.printOut(stdout);\n      Common.printOut(stderr);\n      if (code == 0) {\n        Common.printOut(cst.PREFIX_MSG + chalk.bold('Init file disabled.'));\n      } else {\n        Common.printOut(cst.ERROR_MSG + chalk.bold('Return code : ' + code));\n      }\n\n      cb(null, {\n        commands : commands,\n        platform : platform\n      });\n    });\n  };\n\n  /**\n   * Startup script generation\n   * @method startup\n   * @param {string} platform type (centos|redhat|amazon|gentoo|systemd|smf)\n   */\n  CLI.prototype.startup = function(platform, opts, cb) {\n    var that = this;\n    var actual_platform = detectInitSystem();\n    var user = (opts.user || process.env.USER || process.env.LOGNAME); // Use LOGNAME on Solaris-like systems\n    var service_name = (opts.serviceName || 'pm2-' + user);\n    var openrc_service_name = 'pm2';\n    var launchd_service_name = (opts.serviceName || 'pm2.' + user);\n\n    if (!platform)\n      platform = actual_platform;\n    else if (actual_platform && actual_platform !== platform) {\n      Common.printOut('-----------------------------------------------------------')\n      Common.printOut(' PM2 detected ' + actual_platform + ' but you precised ' + platform)\n      Common.printOut(' Please verify that your choice is indeed your init system')\n      Common.printOut(' If you arent sure, just run : pm2 startup')\n      Common.printOut('-----------------------------------------------------------')\n    }\n    if (platform == null)\n      throw new Error('Init system not found');\n\n    if (!cb) {\n      cb = function(err, data) {\n        if (err)\n          return that.exitCli(cst.ERROR_EXIT);\n        return that.exitCli(cst.SUCCESS_EXIT);\n      }\n    }\n\n    if (process.getuid() != 0) {\n      return isNotRoot('setup', platform, opts, cb);\n    }\n\n    var destination;\n    var commands;\n    var template;\n\n    function getTemplate(type) {\n      return fs.readFileSync(path.join(__dirname, '..', 'templates/init-scripts', type + '.tpl'), {encoding: 'utf8'});\n    }\n\n    switch(platform) {\n    case 'ubuntu':\n    case 'centos':\n    case 'arch':\n    case 'oracle':\n    case 'systemd':\n      if (opts.waitIp)\n        template = getTemplate('systemd-online');\n      else\n        template = getTemplate('systemd');\n      destination = '/etc/systemd/system/' + service_name + '.service';\n      commands = [\n        'systemctl enable ' + service_name\n      ];\n      break;\n    case 'ubuntu14':\n    case 'ubuntu12':\n    case 'upstart':\n      template = getTemplate('upstart');\n      destination = '/etc/init.d/' + service_name;\n      commands = [\n        'chmod +x ' + destination,\n        'mkdir -p /var/lock/subsys',\n        'touch /var/lock/subsys/' + service_name,\n        'update-rc.d ' + service_name + ' defaults'\n      ];\n      break;\n    case 'systemv':\n    case 'amazon':\n    case 'centos6':\n      template = getTemplate('upstart');\n      destination = '/etc/init.d/' + service_name;\n      commands = [\n        'chmod +x ' + destination,\n        'mkdir -p /var/lock/subsys',\n        'touch /var/lock/subsys/' + service_name,\n        'chkconfig --add ' + service_name,\n        'chkconfig ' + service_name + ' on',\n        'initctl list'\n      ];\n      break;\n    case 'macos':\n    case 'darwin':\n    case 'launchd':\n      template = getTemplate('launchd');\n      destination = path.join(process.env.HOME, 'Library/LaunchAgents/' + launchd_service_name + '.plist');\n      commands = [\n        'mkdir -p ' + path.join(process.env.HOME, 'Library/LaunchAgents'),\n        'launchctl load -w ' + destination\n      ]\n      break;\n    case 'freebsd':\n    case 'rcd':\n      template = getTemplate('rcd');\n      service_name = (opts.serviceName || 'pm2_' + user);\n      destination = '/usr/local/etc/rc.d/' + service_name;\n      commands = [\n        'chmod 755 ' + destination,\n        'sysrc ' + service_name + '_enable=YES'\n      ];\n      break;\n    case 'openbsd':\n    case 'rcd-openbsd':\n      template = getTemplate('rcd-openbsd');\n      service_name = (opts.serviceName || 'pm2_' + user);\n      destination = path.join('/etc/rc.d/', service_name);\n      commands = [\n        'chmod 755 ' + destination,\n        'rcctl enable ' + service_name,\n        'rcctl start ' + service_name\n      ];\n      break;\n    case 'openrc':\n      template = getTemplate('openrc');\n      service_name = openrc_service_name;\n      destination = '/etc/init.d/' + service_name;\n      commands = [\n        'chmod +x ' + destination,\n        'rc-update add ' + service_name + ' default'\n      ];\n      break;\n    case 'smf':\n    case 'sunos':\n    case 'solaris':\n      template = getTemplate('smf');\n      service_name = (opts.serviceName || 'pm2_' + user);\n      destination = path.join(tmpPath(), service_name + '.xml');\n      commands = [\n        'svccfg import ' + destination,\n        'svcadm enable ' + service_name\n      ];\n      break;\n    default:\n      throw new Error('Unknown platform / init system name');\n    }\n\n    /**\n     * 4# Replace template variable value\n     */\n    var envPath\n\n    if (cst.HAS_NODE_EMBEDDED == true)\n      envPath = util.format('%s:%s', process.env.PATH || '', path.dirname(process.execPath))\n    else if (new RegExp(path.dirname(process.execPath)).test(process.env.PATH))\n      envPath = process.env.PATH\n    else\n      envPath = util.format('%s:%s', process.env.PATH || '', path.dirname(process.execPath))\n\n    let pm2_bin_path = require.main.filename\n\n    if (pm2_bin_path.includes('/lib/binaries/CLI.js') === true) {\n      pm2_bin_path = pm2_bin_path.replace('/lib/binaries/CLI.js', '/bin/pm2')\n    }\n\n    template = template.replace(/%PM2_PATH%/g, pm2_bin_path)\n      .replace(/%NODE_PATH%/g, envPath)\n      .replace(/%USER%/g, user)\n      .replace(/%HOME_PATH%/g, opts.hp ? path.resolve(opts.hp, '.pm2') : cst.PM2_ROOT_PATH)\n      .replace(/%SERVICE_NAME%/g, service_name);\n\n    Common.printOut(chalk.bold('Platform'), platform);\n    Common.printOut(chalk.bold('Template'));\n    Common.printOut(template);\n    Common.printOut(chalk.bold('Target path'));\n    Common.printOut(destination);\n    Common.printOut(chalk.bold('Command list'));\n    Common.printOut(commands);\n\n    Common.printOut(cst.PREFIX_MSG + 'Writing init configuration in ' + destination);\n    try {\n      fs.writeFileSync(destination, template);\n    } catch (e) {\n      console.error(cst.PREFIX_MSG_ERR + 'Failure when trying to write startup script');\n      console.error(e.message || e);\n      return cb(e);\n    }\n\n    Common.printOut(cst.PREFIX_MSG + 'Making script booting at startup...');\n\n    forEachLimit(commands, 1, function(command, next) {\n      Common.printOut(cst.PREFIX_MSG + '[-] Executing: %s...', chalk.bold(command));\n\n      sexec(command, function(code, stdout, stderr) {\n        if (code === 0) {\n          Common.printOut(cst.PREFIX_MSG + chalk.bold('[v] Command successfully executed.'));\n          return next();\n        } else {\n          Common.printOut(chalk.red('[ERROR] Exit code : ' + code))\n          return next(new Error(command + ' failed, see error above.'));\n        }\n      })\n\n    }, function(err) {\n      if (err) {\n        console.error(cst.PREFIX_MSG_ERR + (err.message || err));\n        return cb(err);\n      }\n      Common.printOut(chalk.bold.blue('+---------------------------------------+'));\n      Common.printOut(chalk.bold.blue((cst.PREFIX_MSG + 'Freeze a process list on reboot via:' )));\n      Common.printOut(chalk.bold('$ pm2 save'));\n      Common.printOut('');\n      Common.printOut(chalk.bold.blue(cst.PREFIX_MSG + 'Remove init script via:'));\n      Common.printOut(chalk.bold('$ pm2 unstartup ' + platform));\n\n      return cb(null, {\n        destination  : destination,\n        template : template\n      });\n    });\n  };\n\n  /**\n   * DISABLED FEATURE\n   * KEEPING METHOD FOR BACKWARD COMPAT\n   */\n  CLI.prototype.autodump = function(cb) {\n    return cb()\n  }\n\n  /**\n   * Dump current processes managed by pm2 into DUMP_FILE_PATH file\n   * @method dump\n   * @param {} cb\n   * @return\n   */\n  CLI.prototype.dump = function(force, cb) {\n    var env_arr = [];\n    var that = this;\n\n    if (typeof(force) === 'function') {\n      cb = force\n      force = false\n    }\n\n    if (!cb)\n      Common.printOut(cst.PREFIX_MSG + 'Saving current process list...');\n\n    that.Client.executeRemote('getMonitorData', {}, function(err, list) {\n      if (err) {\n        Common.printError('Error retrieving process list: ' + err);\n        return cb ? cb(Common.retErr(err)) : that.exitCli(cst.ERROR_EXIT);\n      }\n\n      /**\n       * Description\n       * @method fin\n       * @param {} err\n       * @return\n       */\n      function fin(err) {\n\n        // try to fix issues with empty dump file\n        // like #3485\n        if (!force && env_arr.length === 0 && !process.env.FORCE) {\n\n          // fix : if no dump file, no process, only module and after pm2 update\n          if (!fs.existsSync(cst.DUMP_FILE_PATH)) {\n            that.clearDump(function(){});\n          }\n\n          // if no process in list don't modify dump file\n          // process list should not be empty\n          if (cb) {\n            return cb(new Error('Process list empty, cannot save empty list'));\n          } else  {\n            Common.printOut(cst.PREFIX_MSG_WARNING + 'PM2 is not managing any process, skipping save...');\n            Common.printOut(cst.PREFIX_MSG_WARNING + 'To force saving use: pm2 save --force');\n            that.exitCli(cst.SUCCESS_EXIT);\n            return;\n          }\n        }\n\n        // Back up dump file\n        try {\n          if (fs.existsSync(cst.DUMP_FILE_PATH)) {\n            fs.writeFileSync(cst.DUMP_BACKUP_FILE_PATH, fs.readFileSync(cst.DUMP_FILE_PATH));\n          }\n        } catch (e) {\n          console.error(e.stack || e);\n          Common.printOut(cst.PREFIX_MSG_ERR + 'Failed to back up dump file in %s', cst.DUMP_BACKUP_FILE_PATH);\n        }\n\n        // Overwrite dump file, delete if broken and exit\n        try {\n          fs.writeFileSync(cst.DUMP_FILE_PATH, JSON.stringify(env_arr, '', 2));\n        } catch (e) {\n          console.error(e.stack || e);\n          try {\n            // try to backup file\n            if (fs.existsSync(cst.DUMP_BACKUP_FILE_PATH)) {\n              fs.writeFileSync(cst.DUMP_FILE_PATH, fs.readFileSync(cst.DUMP_BACKUP_FILE_PATH));\n            }\n          } catch (e) {\n            // don't keep broken file\n            fs.unlinkSync(cst.DUMP_FILE_PATH);\n            console.error(e.stack || e);\n          }\n          Common.printOut(cst.PREFIX_MSG_ERR + 'Failed to save dump file in %s', cst.DUMP_FILE_PATH);\n          return that.exitCli(cst.ERROR_EXIT);\n        }\n        if (cb) return cb(null, {success:true});\n\n        Common.printOut(cst.PREFIX_MSG + 'Successfully saved in %s', cst.DUMP_FILE_PATH);\n        return that.exitCli(cst.SUCCESS_EXIT);\n      }\n\n      (function ex(apps) {\n        if (!apps[0]) return fin(null);\n        delete apps[0].pm2_env.instances;\n        delete apps[0].pm2_env.pm_id;\n        delete apps[0].pm2_env.prev_restart_delay;\n        if (!apps[0].pm2_env.pmx_module)\n          env_arr.push(apps[0].pm2_env);\n        apps.shift();\n        return ex(apps);\n      })(list);\n    });\n  };\n\n  /**\n   * Remove DUMP_FILE_PATH file and DUMP_BACKUP_FILE_PATH file\n   * @method dump\n   * @param {} cb\n   * @return\n   */\n  CLI.prototype.clearDump = function(cb) {\n    fs.writeFileSync(cst.DUMP_FILE_PATH, JSON.stringify([]));\n\n    if(cb && typeof cb === 'function') return cb();\n\n    Common.printOut(cst.PREFIX_MSG + 'Successfully created %s', cst.DUMP_FILE_PATH);\n    return this.exitCli(cst.SUCCESS_EXIT);\n  };\n\n  /**\n   * Resurrect processes\n   * @method resurrect\n   * @param {} cb\n   * @return\n   */\n  CLI.prototype.resurrect = function(cb) {\n    var apps = {};\n    var that = this;\n\n    var processes;\n\n    function readDumpFile(dumpFilePath) {\n      Common.printOut(cst.PREFIX_MSG + 'Restoring processes located in %s', dumpFilePath);\n      try {\n        var apps = fs.readFileSync(dumpFilePath);\n      } catch (e) {\n        Common.printError(cst.PREFIX_MSG_ERR + 'Failed to read dump file in %s', dumpFilePath);\n        throw e;\n      }\n\n      return apps;\n    }\n\n    function parseDumpFile(dumpFilePath, apps) {\n      try {\n        var processes = Common.parseConfig(apps, 'none');\n      } catch (e) {\n        Common.printError(cst.PREFIX_MSG_ERR + 'Failed to parse dump file in %s', dumpFilePath);\n        try {\n          fs.unlinkSync(dumpFilePath);\n        } catch (e) {\n          console.error(e.stack || e);\n        }\n        throw e;\n      }\n\n      return processes;\n    }\n\n    // Read dump file, fall back to backup, delete if broken\n    try {\n      apps = readDumpFile(cst.DUMP_FILE_PATH);\n      processes = parseDumpFile(cst.DUMP_FILE_PATH, apps);\n    } catch(e) {\n      try {\n        apps = readDumpFile(cst.DUMP_BACKUP_FILE_PATH);\n        processes = parseDumpFile(cst.DUMP_BACKUP_FILE_PATH, apps);\n      } catch(e) {\n        Common.printError(cst.PREFIX_MSG_ERR + 'No processes saved; DUMP file doesn\\'t exist');\n        // if (cb) return cb(Common.retErr(e));\n        // else return that.exitCli(cst.ERROR_EXIT);\n        return that.speedList();\n      }\n    }\n\n    that.Client.executeRemote('getMonitorData', {}, function(err, list) {\n      if (err) {\n        Common.printError(err);\n        return that.exitCli(1);\n      }\n\n      var current = [];\n      var target = [];\n\n      list.forEach(function(app) {\n        if (!current[app.name])\n          current[app.name] = 0;\n        current[app.name]++;\n      });\n\n      processes.forEach(function(app) {\n        if (!target[app.name])\n          target[app.name] = 0;\n        target[app.name]++;\n      });\n\n      var tostart = Object.keys(target).filter(function(i) {\n        return Object.keys(current).indexOf(i) < 0;\n      })\n\n      eachLimit(processes, cst.CONCURRENT_ACTIONS, function(app, next) {\n        if (tostart.indexOf(app.name) == -1)\n          return next();\n        that.Client.executeRemote('prepare', app, function(err, dt) {\n          if (err)\n            Common.printError(err);\n          else\n            Common.printOut(cst.PREFIX_MSG + 'Process %s restored', app.pm_exec_path);\n          next();\n        });\n      }, function(err) {\n        return cb ? cb(null, apps) : that.speedList();\n      });\n    });\n  };\n\n}\n"
  },
  {
    "path": "lib/API/UX/helpers.js",
    "content": "const chalk = require('ansis')\nconst Helpers = {}\n\n/**\n * Converts Byte to Human readable size\n * @method bytesToSize\n * @param {} bytes\n * @param {} precision\n * @return\n */\nHelpers.bytesToSize = function(bytes, precision) {\n  var kilobyte = 1024\n  var megabyte = kilobyte * 1024\n  var gigabyte = megabyte * 1024\n  var terabyte = gigabyte * 1024\n\n  if ((bytes >= 0) && (bytes < kilobyte)) {\n    return bytes + 'b '\n  } else if ((bytes >= kilobyte) && (bytes < megabyte)) {\n    return (bytes / kilobyte).toFixed(precision) + 'kb '\n  } else if ((bytes >= megabyte) && (bytes < gigabyte)) {\n    return (bytes / megabyte).toFixed(precision) + 'mb '\n  } else if ((bytes >= gigabyte) && (bytes < terabyte)) {\n    return (bytes / gigabyte).toFixed(precision) + 'gb '\n  } else if (bytes >= terabyte) {\n    return (bytes / terabyte).toFixed(precision) + 'tb '\n  } else {\n    return bytes + 'b '\n  }\n}\n\n\n/**\n * Color Process state\n * @method colorStatus\n * @param {} status\n * @return\n */\nHelpers.colorStatus = function(status) {\n  switch (status) {\n\n  case 'online':\n    return chalk.green.bold('online')\n    break\n  case 'running':\n    return chalk.green.bold('online')\n    break\n  case 'restarting':\n    return chalk.yellow.bold('restart')\n    break\n  case 'created':\n    return chalk.yellow.bold('created')\n    break\n  case 'launching':\n    return chalk.blue.bold('launching')\n    break\n  default:\n    return chalk.red.bold(status)\n  }\n}\n\n/**\n * Safe Push\n */\nHelpers.safe_push = function() {\n  var argv = arguments\n  var table = argv[0]\n\n  for (var i = 1; i < argv.length; ++i) {\n    var elem = argv[i]\n    if (elem[Object.keys(elem)[0]] === undefined\n        || elem[Object.keys(elem)[0]] === null) {\n      elem[Object.keys(elem)[0]] = 'N/A'\n    }\n    else if (Array.isArray(elem[Object.keys(elem)[0]])) {\n      elem[Object.keys(elem)[0]].forEach(function(curr, j) {\n        if (curr === undefined || curr === null)\n          elem[Object.keys(elem)[0]][j] = 'N/A'\n      })\n    }\n    table.push(elem)\n  }\n}\n\n/**\n * Description\n * @method timeSince\n * @param {} date\n * @return BinaryExpression\n */\nHelpers.timeSince = function(date) {\n  var seconds = Math.floor((new Date() - date) / 1000)\n\n  var interval = Math.floor(seconds / 31536000)\n\n  if (interval > 1) {\n    return interval + 'Y'\n  }\n  interval = Math.floor(seconds / 2592000)\n  if (interval > 1) {\n    return interval + 'M'\n  }\n  interval = Math.floor(seconds / 86400)\n  if (interval > 1) {\n    return interval + 'D'\n  }\n  interval = Math.floor(seconds / 3600)\n  if (interval > 1) {\n    return interval + 'h'\n  }\n  interval = Math.floor(seconds / 60)\n  if (interval > 1) {\n    return interval + 'm'\n  }\n  return Math.floor(seconds) + 's'\n}\n\n/**\n * Colorize Metrics\n *\n * @param {Number} value current value\n * @param {Number} warn value threshold\n * @param {Number} alert value threshold\n * @param {String} prefix value prefix\n * @return {String} value\n */\nHelpers.colorizedMetric = function(value, warn, alert, prefix) {\n  var inverted = false\n  if (alert < warn)\n    inverted = true\n\n  if (!prefix) prefix = ''\n  if (isNaN(value) === true)\n    return 'N/A'\n  if (value == 0)\n    return 0 + prefix\n  if (inverted == true) {\n    if (value > warn)\n      return chalk.green(value + prefix)\n    if (value <= warn && value >= alert)\n      return chalk.bold.yellow(value + prefix)\n    return chalk.bold.red(value + prefix)\n  }\n  if (value < warn)\n    return chalk.green(value + prefix)\n  if (value >= warn && value <= alert)\n    return chalk.bold.yellow(value + prefix)\n  return chalk.bold.red(value + prefix)\n}\n\n/**\n * Get nested property\n *\n * @param {String} propertyName\n * @param {Object} obj\n * @returns {String} property value\n */\nHelpers.getNestedProperty = function(propertyName, obj) {\n  var parts = propertyName.split('.'),\n      length = parts.length,\n      property = obj || {}\n\n  for (var i = 0; i < length; i++ ) {\n    property = property[parts[i]]\n  }\n\n  return property\n}\n\nHelpers.openEditor = function (file, opts, cb) {\n  var spawn = require('child_process').spawn\n\n  if (typeof opts === 'function') {\n    cb = opts\n    opts = {}\n  }\n\n  if (!opts) opts = {}\n\n  var ed = /^win/.test(process.platform) ? 'notepad' : 'vim'\n  var editor = opts.editor || process.env.VISUAL || process.env.EDITOR || ed\n  var args = editor.split(/\\s+/)\n  var bin = args.shift()\n\n  var ps = spawn(bin, args.concat([ file ]), {\n    windowsHide: true,\n    stdio: 'inherit'\n  })\n\n  ps.on('exit', function (code, sig) {\n    if (typeof cb === 'function') cb(code, sig)\n  })\n}\n\n\nHelpers.dispKeys = function(kv, target_module) {\n  Object.keys(kv).forEach(function(key) {\n\n    if (target_module != null && target_module != key)\n      return false\n\n    if (typeof(kv[key]) == 'object') {\n      var obj = {}\n\n      console.log(chalk.bold('Module: ') + chalk.bold.blue(key))\n      Object.keys(kv[key]).forEach(function(sub_key) {\n        console.log(`$ pm2 set ${key}:${sub_key} ${kv[key][sub_key]}`)\n      })\n    }\n  })\n}\n\nmodule.exports = Helpers\n"
  },
  {
    "path": "lib/API/UX/index.js",
    "content": "\nconst UX = {\n  helpers: require('./helpers.js'),\n  describe: require('./pm2-describe.js'),\n  list: require('./pm2-ls.js'),\n  list_min: require('./pm2-ls-minimal.js')\n}\n\nmodule.exports = UX\n"
  },
  {
    "path": "lib/API/UX/pm2-describe.js",
    "content": "const Table = require('cli-tableau')\nconst chalk = require('ansis')\nconst UxHelpers = require('./helpers.js')\nconst Common = require('../../Common.js')\n\nvar postModuleInfos = function(module_name, human_info) {\n  var table = new Table({\n    style : {'padding-left' : 1, head : ['cyan', 'bold'], compact : true}\n  })\n\n  var disp = {}\n\n  human_info.unshift(['Module name', module_name])\n  human_info.forEach(function(info) {\n    var obj = {}\n    obj[chalk.bold.cyan(info[0])] = info[1]\n    table.push(obj)\n  })\n\n  console.log()\n  console.log(chalk.bold.inverse(' Module %s infos '), module_name)\n  console.log(table.toString())\n}\n\n/**\n * Description\n * @method describeTable\n * @param {Object} proc process list\n */\nmodule.exports = function(proc) {\n  var table = new Table({\n    style : {'padding-left' : 1, head : ['cyan', 'bold'], compact : true}\n  })\n\n  var pm2_env = proc.pm2_env\n\n  var created_at = 'N/A'\n\n  if (pm2_env.axm_options && pm2_env.axm_options.human_info) {\n    postModuleInfos(pm2_env.name, pm2_env.axm_options.human_info)\n  }\n\n  try {\n    if (pm2_env.created_at != null)\n      created_at = new Date(pm2_env.created_at).toISOString()\n  } catch (e) {\n  }\n\n  console.log(chalk.bold.inverse(' Describing process with id %d - name %s '), pm2_env.pm_id, pm2_env.name)\n  UxHelpers.safe_push(table,\n            { 'status' : UxHelpers.colorStatus(pm2_env.status) },\n            { 'name': pm2_env.name },\n            { 'namespace': pm2_env.namespace },\n            { 'version': pm2_env.version },\n            { 'restarts' : pm2_env.restart_time },\n            { 'uptime' : (pm2_env.pm_uptime && pm2_env.status == 'online') ? UxHelpers.timeSince(pm2_env.pm_uptime) : 0 },\n            { 'script path' : pm2_env.pm_exec_path },\n            { 'script args' : pm2_env.args ? (typeof pm2_env.args == 'string' ? JSON.parse(pm2_env.args.replace(/'/g, '\"')):pm2_env.args).join(' ') : null },\n            { 'error log path' : pm2_env.pm_err_log_path },\n            { 'out log path' : pm2_env.pm_out_log_path },\n            { 'pid path' : pm2_env.pm_pid_path },\n\n            { 'interpreter' : pm2_env.exec_interpreter },\n            { 'interpreter args' : pm2_env.node_args.length != 0 ? pm2_env.node_args : null },\n\n            { 'script id' : pm2_env.pm_id },\n            { 'exec cwd' : pm2_env.pm_cwd },\n\n            { 'exec mode' : pm2_env.exec_mode },\n            { 'node.js version' : pm2_env.node_version },\n            { 'node env': pm2_env.env.NODE_ENV },\n            { 'watch & reload' : pm2_env.watch ? chalk.green.bold('✔') : '✘' },\n            { 'unstable restarts' : pm2_env.unstable_restarts },\n            { 'created at' : created_at }\n           )\n\n  if ('pm_log_path' in pm2_env){\n    table.splice(6, 0, {'entire log path': pm2_env.pm_log_path})\n  }\n\n  if ('cron_restart' in pm2_env){\n    table.splice(5, 0, {'cron restart': pm2_env.cron_restart})\n  }\n\n  console.log(table.toString())\n\n  /**\n   * Module conf display\n   */\n  if (pm2_env.axm_options &&\n      pm2_env.axm_options.module_conf &&\n      Object.keys(pm2_env.axm_options.module_conf).length > 0) {\n    var table_conf = new Table({\n      style : {'padding-left' : 1, head : ['cyan', 'bold'], compact : true}\n    })\n    console.log('Process configuration')\n\n    Object.keys(pm2_env.axm_options.module_conf).forEach(function(key) {\n      var tmp = {}\n      tmp[key] = pm2_env.axm_options.module_conf[key]\n      UxHelpers.safe_push(table_conf, tmp)\n    })\n\n    console.log(table_conf.toString())\n  }\n\n  /**\n   * Versioning metadata\n   */\n  if (pm2_env.versioning) {\n\n    var table2 = new Table({\n      style : {'padding-left' : 1, head : ['cyan', 'bold'], compact : true}\n    })\n\n    console.log(chalk.inverse.bold(' Revision control metadata '))\n    UxHelpers.safe_push(table2,\n              { 'revision control' : pm2_env.versioning.type },\n              { 'remote url' : pm2_env.versioning.url },\n              { 'repository root' : pm2_env.versioning.repo_path },\n              { 'last update' : pm2_env.versioning.update_time },\n              { 'revision' : pm2_env.versioning.revision },\n              { 'comment' :  pm2_env.versioning.comment ? pm2_env.versioning.comment.trim().slice(0, 60) : '' },\n              { 'branch' :  pm2_env.versioning.branch }\n             )\n    console.log(table2.toString())\n  }\n\n  if (pm2_env.axm_actions && Object.keys(pm2_env.axm_actions).length > 0) {\n    var table_actions = new Table({\n      style : {'padding-left' : 1, head : ['cyan', 'bold'], compact : true}\n    })\n\n    console.log(chalk.inverse.bold(' Actions available '))\n    pm2_env.axm_actions.forEach(function(action_set) {\n      UxHelpers.safe_push(table_actions, [action_set.action_name])\n    })\n\n    console.log(table_actions.toString())\n    Common.printOut(chalk.white.italic(' Trigger via: pm2 trigger %s <action_name>\\n'), pm2_env.name)\n  }\n\n  if (pm2_env.axm_monitor && Object.keys(pm2_env.axm_monitor).length > 0) {\n    var table_probes = new Table({\n      style : {'padding-left' : 1, head : ['cyan', 'bold'], compact : true}\n    })\n\n    console.log(chalk.inverse.bold(' Code metrics value '))\n    Object.keys(pm2_env.axm_monitor).forEach(function(key) {\n      var obj = {}\n      var metric_name = pm2_env.axm_monitor[key].hasOwnProperty(\"value\") ? pm2_env.axm_monitor[key].value : pm2_env.axm_monitor[key]\n      var metric_unit = pm2_env.axm_monitor[key].hasOwnProperty(\"unit\") ? pm2_env.axm_monitor[key].unit : ''\n      var value = `${metric_name} ${metric_unit}`\n      obj[key] = value\n      UxHelpers.safe_push(table_probes, obj)\n    })\n\n    console.log(table_probes.toString())\n  }\n\n  var table_env = new Table({\n    style : {'padding-left' : 1, head : ['cyan', 'bold'], compact : true}\n  })\n\n  console.log(chalk.inverse.bold(' Divergent env variables from local env '))\n\n  var _env = Common.safeExtend({}, pm2_env)\n  var diff_env = {}\n\n  Object.keys(process.env).forEach(k => {\n    if (!_env[k] || _env[k] != process.env[k]) {\n      diff_env[k] = process.env[k]\n    }\n  })\n\n  Object.keys(diff_env).forEach(function(key) {\n    var obj = {}\n    if (_env[key]) {\n      // 1. fix env value is not a String and slice is undeinfed\n      // 2. fix process.stdout.columns is undefined and causes empty string output\n      // 3. columns defaults to 300 - same as specified in pm2-ls\n      obj[key] = String(_env[key]).slice(0, (process.stdout.columns || 300) - 60)\n      UxHelpers.safe_push(table_env, obj)\n    }\n  })\n\n  console.log(table_env.toString())\n  console.log()\n  Common.printOut(chalk.white.italic(' Add your own code metrics: http://bit.ly/code-metrics'))\n  Common.printOut(chalk.white.italic(' Use `pm2 logs %s [--lines 1000]` to display logs'), pm2_env.name)\n  Common.printOut(chalk.white.italic(' Use `pm2 env %s` to display environment variables'), pm2_env.pm_id)\n  Common.printOut(chalk.white.italic(' Use `pm2 monit` to monitor CPU and Memory usage'), pm2_env.name)\n}\n"
  },
  {
    "path": "lib/API/UX/pm2-ls-minimal.js",
    "content": "\nconst UxHelpers = require('./helpers.js')\nconst p = require('path')\n\n/**\n * Minimal display via pm2 ls -m\n * @method miniDisplay\n * @param {Object} list process list\n */\nmodule.exports = function(list) {\n  list.forEach(function(l) {\n\n    var mode = l.pm2_env.exec_mode.split('_mode')[0]\n    var status = l.pm2_env.status\n    var key = l.pm2_env.name || p.basename(l.pm2_env.pm_exec_path.script)\n\n    console.log('+--- %s', key)\n    console.log('namespace : %s', l.pm2_env.namespace)\n    console.log('version : %s', l.pm2_env.version)\n    console.log('pid : %s', l.pid)\n    console.log('pm2 id : %s', l.pm2_env.pm_id)\n    console.log('status : %s', status)\n    console.log('mode : %s', mode)\n    console.log('restarted : %d', l.pm2_env.restart_time ? l.pm2_env.restart_time : 0)\n    console.log('uptime : %s', (l.pm2_env.pm_uptime && status == 'online') ? UxHelpers.timeSince(l.pm2_env.pm_uptime) : 0)\n    console.log('memory usage : %s', l.monit ? UxHelpers.bytesToSize(l.monit.memory, 1) : '')\n    console.log('error log : %s', l.pm2_env.pm_err_log_path)\n    console.log('watching : %s', l.pm2_env.watch ? 'yes' : 'no')\n    console.log('PID file : %s\\n', l.pm2_env.pm_pid_path)\n  })\n}\n"
  },
  {
    "path": "lib/API/UX/pm2-ls.js",
    "content": "\nconst cst = require('../../../constants')\nconst Common = require('../../Common')\nconst Configuration = require('../../Configuration')\nconst UxHelpers = require('./helpers.js')\nconst chalk = require('ansis')\nconst Table = require('cli-tableau')\nconst Passwd = require('../../tools/passwd.js')\n\nconst List = {}\n\nconst CONDENSED_MODE = (process.stdout.columns || 300) < 134\n\n/**\n * Check if dump file contains same apps that the one managed by PM2\n */\nfunction checkIfProcessAreDumped(list) {\n  try {\n    var dump_raw = require('fs').readFileSync(cst.DUMP_FILE_PATH)\n    var dump = JSON.parse(dump_raw)\n    var apps_dumped = dump.map(proc => proc.name)\n    var apps_running = list\n        .filter(proc => proc.pm2_env.pmx_module != true)\n        .map(proc => proc.name)\n    var diff = apps_dumped.filter(a => !apps_running.includes(a))\n    if (diff.length > 0) {\n      Common.warn(`Current process list is not synchronized with saved list. App ${chalk.bold(diff.join(' '))} differs. Type 'pm2 save' to synchronize.`)\n    }\n    else if (apps_dumped.length != apps_running.length) {\n      Common.warn(`Current process list is not synchronized with saved list. Type 'pm2 save' to synchronize.`)\n    }\n  } catch(e) {\n  }\n}\n\nvar proc_id = 0\n\n/**\n * List Applications and Modules managed by PM2\n */\nfunction listModulesAndAppsManaged(list, commander) {\n  var name_col_size = 11\n\n  if (list && list.length > 0)\n    name_col_size = (list.reduce((p, c) => (p.name.length > c.name.length) ? p : c)).name.length + 5\n\n  var id_width = Math.max(\n    2 + (Math.max(...list.map((l) => String(l.pm2_env.pm_id || 0).length)) || 0),\n    4\n  );\n\n  var app_head = {\n    id: id_width,\n    name: name_col_size,\n    namespace: 13,\n    version: 9,\n    mode: 9,\n    pid: 10,\n    uptime: 8,\n    '↺': 6,\n    status: 11,\n    cpu: 10,\n    mem: 10,\n    user: 10,\n    watching: 10\n  }\n\n  var mod_head = {\n    id: id_width,\n    module: 30,\n    version: 15,\n    pid: 10,\n    status: 10,\n    '↺': 6,\n    cpu: 10,\n    mem: 10,\n    user: 10\n  }\n\n  if (CONDENSED_MODE) {\n    app_head = {\n      id: id_width,\n      name: 20,\n      mode: 10,\n      '↺': 6,\n      status: 11,\n      cpu: 10,\n      memory: 10\n    }\n\n    mod_head = {\n      id: id_width,\n      name: 20,\n      status: 10,\n      cpu: 10,\n      mem: 10\n    }\n  }\n\n  var app_table = new Table({\n    head : Object.keys(app_head),\n    colWidths: Object.keys(app_head).map(k => app_head[k]),\n    colAligns : ['left'],\n    style : {'padding-left' : 1, head : ['cyan', 'bold'], compact : true}\n  })\n\n  var module_table = new Table({\n    head : Object.keys(mod_head),\n    colWidths: Object.keys(mod_head).map(k => mod_head[k]),\n    colAligns : ['left'],\n    style : {'padding-left' : 1, head : ['cyan', 'bold'],  compact : true}\n  })\n\n  var sortField = 'name', sortOrder = 'asc', sort,\n      fields = {\n        name: 'pm2_env.name',\n        namespace: 'pm2_env.namespace',\n        pid: 'pid',\n        id: 'pm_id',\n        cpu: 'monit.cpu',\n        memory: 'monit.memory',\n        uptime: 'pm2_env.pm_uptime',\n        status: 'pm2_env.status'\n      }\n\n  if (commander && commander.sort) {\n    sort = commander.sort.split(':');\n\n    if(fields[sort[0].toLowerCase()]) {\n      sortField = sort[0].toLowerCase();\n      sortOrder = sort.length === 2 ? sort[1] : 'asc';\n    }\n  }\n\n  list.sort(function(a, b) {\n    var fieldA = UxHelpers.getNestedProperty(fields[sortField], a)\n    var fieldB = UxHelpers.getNestedProperty(fields[sortField], b)\n\n    if (sortOrder === 'desc') {\n      if (fieldA > fieldB)\n        return -1\n      if (fieldA < fieldB)\n        return 1\n    } else {\n      if (fieldA < fieldB)\n        return -1\n      if (fieldA > fieldB)\n        return 1\n    }\n    return 0\n  })\n\n  list.forEach(function(l) {\n    var obj = {}\n\n    if (l.pm2_env.pm_id > proc_id) {\n      proc_id = l.pm2_env.pm_id\n    }\n\n    var mode = l.pm2_env.exec_mode\n    var status = l.pm2_env.status\n    var key = l.pm2_env.pm_id\n    key = chalk.bold.cyan(key)\n\n    if (l.pm2_env.axm_options) {\n      var is_tracing_enabled = false\n\n      if (l.pm2_env.axm_options.tracing &&\n          typeof(l.pm2_env.axm_options.tracing) == 'boolean' &&\n          l.pm2_env.axm_options.tracing == true)\n        is_tracing_enabled = true\n\n      if (l.pm2_env.axm_options.tracing &&\n          l.pm2_env.axm_options.tracing.enabled &&\n          typeof(l.pm2_env.axm_options.tracing.enabled) == 'boolean' &&\n          l.pm2_env.axm_options.tracing.enabled == true)\n        is_tracing_enabled = true\n\n      if (is_tracing_enabled == true)\n        l.pm2_env.name = chalk.green('☵')  + ' ' + l.pm2_env.name\n\n      if (l.pm2_env._km_monitored)\n        l.pm2_env.name = chalk.bold.green('◉')  + ' ' + l.pm2_env.name\n    }\n\n    if (l.pm2_env.pmx_module == true) {\n      if (l.pm2_env.name == 'pm2-sysmonit') return\n      // pm2 ls for Modules\n      obj[key] = []\n\n      obj[key].push(l.name)\n\n      // Module version + PID\n      if (!CONDENSED_MODE) {\n        var pid = l.pm2_env.axm_options.pid ? l.pm2_env.axm_options.pid : l.pid\n        obj[key].push(l.pm2_env.version || 'N/A', pid)\n      }\n\n      // Status\n      obj[key].push(UxHelpers.colorStatus(status))\n\n      // Restart\n      if (!CONDENSED_MODE)\n        obj[key].push(l.pm2_env.restart_time ? l.pm2_env.restart_time : 0)\n\n      // CPU + Memory\n      obj[key].push(l.monit ? (l.monit.cpu + '%') : 'N/A', l.monit ? UxHelpers.bytesToSize(l.monit.memory, 1) : 'N/A' )\n\n      // User\n      if (!CONDENSED_MODE) {\n\n        if (l.pm2_env.uid && typeof(l.pm2_env.uid) == 'number') {\n          // Resolve user id to username\n          let users = Passwd.getUsers()\n          Object.keys(users).forEach(function(username) {\n            var user = users[username]\n            if (user.userId == l.pm2_env.uid) {\n              l.pm2_env.uid = user.username\n            }\n          })\n        }\n        obj[key].push(chalk.bold(l.pm2_env.uid || l.pm2_env.username))\n      }\n\n      UxHelpers.safe_push(module_table, obj)\n    }\n    else {\n      // pm2 ls for Applications\n      obj[key] = []\n\n      // PM2 ID\n      obj[key].push(l.pm2_env.name)\n\n      // Namespace\n      if (!CONDENSED_MODE)\n        obj[key].push(l.pm2_env.namespace)\n\n      // Version\n      if (!CONDENSED_MODE)\n        obj[key].push(l.pm2_env.version)\n\n      // Exec mode\n      obj[key].push(mode == 'fork_mode' ? chalk.inverse.bold('fork') : chalk.blue.bold('cluster'))\n\n      // PID\n      if (!CONDENSED_MODE)\n        obj[key].push(l.pid)\n\n      // Uptime\n      if (!CONDENSED_MODE)\n        obj[key].push((l.pm2_env.pm_uptime && status == 'online') ? UxHelpers.timeSince(l.pm2_env.pm_uptime) : 0)\n\n      // Restart\n      obj[key].push(l.pm2_env.restart_time ? l.pm2_env.restart_time : 0)\n\n      // Status\n      obj[key].push(UxHelpers.colorStatus(status))\n\n\n      // CPU\n      obj[key].push(l.monit ? l.monit.cpu + '%' : 'N/A')\n\n      // Memory\n      obj[key].push(l.monit ? UxHelpers.bytesToSize(l.monit.memory, 1) : 'N/A')\n\n      // User\n      if (!CONDENSED_MODE) {\n        if (l.pm2_env.uid && typeof(l.pm2_env.uid) == 'number') {\n          // Resolve user id to username\n          let users = Passwd.getUsers()\n          Object.keys(users).forEach(function(username) {\n            var user = users[username]\n            if (user.userId == l.pm2_env.uid) {\n              l.pm2_env.uid = user.username\n            }\n          })\n        }\n        obj[key].push(chalk.bold(l.pm2_env.uid || l.pm2_env.username))\n      }\n\n      // Watch status\n      if (!CONDENSED_MODE)\n        obj[key].push(l.pm2_env.watch ? chalk.green.bold('enabled') : chalk.gray('disabled'))\n\n      UxHelpers.safe_push(app_table, obj)\n    }\n\n  })\n\n  // Print Applications Managed\n  console.log(app_table.toString())\n\n  // Print Modules Managed\n  if (module_table.length > 0) {\n    console.log(chalk.bold(`Module${module_table.length > 1 ? 's' : ''}`))\n    console.log(module_table.toString())\n  }\n\n  proc_id++\n}\n\n// Container display\nfunction containersListing(sys_infos) {\n  var stacked_docker = (process.stdout.columns || 100) < 140\n\n  var docker_head = {\n    id: 4,\n    image: 50,\n    status: 10,\n    '↺': 6,\n    cpu: 10,\n    mem: 10,\n    'net I/O ⇵': 11,\n    'fs I/O ⇵': 11\n  }\n\n  if (stacked_docker) {\n    docker_head = {\n      id: 4,\n      image: 25,\n      status: 10,\n      cpu: 10,\n      mem: 10\n    }\n  }\n\n  var docker_table = new Table({\n    colWidths: Object.keys(docker_head).map(k => docker_head[k]),\n    head : Object.keys(docker_head),\n    colAligns : ['left'],\n    style : {'padding-left' : 1, head : ['cyan', 'bold'],  compact : true}\n  })\n\n  sys_infos.containers.forEach((c) => {\n    var cpu = c.stats.cpu_percent\n    var mem = c.stats.mem_percent == 0 ? '0' : c.stats.mem_percent\n    var id = chalk.bold.cyan(proc_id++)\n    var state = UxHelpers.colorStatus(c.state)\n\n    if (stacked_docker)\n      docker_table.push([id, c.image, state, `${cpu}%`, `${mem}mb`])\n    else {\n      docker_table.push([\n        id,\n        c.image,\n        state,\n        c.restartCount,\n        `${cpu == 0 ? '0' : cpu}%`,\n        `${mem}mb`,\n        `${c.stats.netIO.rx}/${isNaN(c.stats.netIO.tx) == true ? '0.0' : c.stats.netIO.tx}`,\n        `${c.stats.blockIO.r}/${c.stats.blockIO.w}`\n      ])\n    }\n  })\n\n  console.log(chalk.bold(`Container${sys_infos.containers.length > 1 ? 's' : ''}`))\n  console.log(docker_table.toString())\n}\n\n/**\n * High resource processes\n */\nfunction listHighResourcesProcesses(sys_infos) {\n  const CPU_MIN_SHOW = 60\n  const MEM_MIN_SHOW = 30\n\n  var sys_proc_head = ['id', 'cmd', 'pid', 'cpu', 'mem', 'uid']\n\n  var sys_proc_table = new Table({\n    colWidths: [4, CONDENSED_MODE ? 29 : 77, 10, 10, 10, 8],\n    head : sys_proc_head,\n    colAligns : ['left'],\n    style : {'padding-left' : 1, head : ['cyan', 'bold'],  compact : true}\n  })\n\n  sys_infos.processes.cpu_sorted = sys_infos.processes.cpu_sorted.filter((proc) => {\n    return proc.cpu > CPU_MIN_SHOW && proc.cmd.includes('node') === false &&\n      proc.cmd.includes('God Daemon') === false\n  })\n\n  sys_infos.processes.cpu_sorted.forEach(proc => {\n    var cpu = `${UxHelpers.colorizedMetric(proc.cpu, 40, 70, '%')}`\n    var mem = `${UxHelpers.colorizedMetric(proc.memory, 40, 70, '%')}`\n    var cmd = proc.cmd\n    sys_proc_table.push([chalk.bold.cyan(proc_id++), cmd, proc.pid, cpu, mem, proc.uid])\n  })\n\n  sys_infos.processes.mem_sorted = sys_infos.processes.mem_sorted.filter((proc) => {\n    return proc.memory > MEM_MIN_SHOW && proc.cmd.includes('node') == false\n  })\n\n  sys_infos.processes.mem_sorted.forEach((proc) => {\n    var cpu = `${UxHelpers.colorizedMetric(proc.cpu, 40, 70, '%')}`\n    var mem = `${UxHelpers.colorizedMetric(proc.memory, 40, 70, '%')}`\n    var cmd = proc.cmd\n    // if (proc.cmd.length > 50)\n    //   cmd = '…' + proc.cmd.slice(proc.cmd.length - 48, proc.cmd.length)\n    sys_proc_table.push([chalk.bold.cyan(proc_id++), cmd, proc.pid, cpu, mem, proc.uid])\n  })\n\n  if (sys_infos.processes.cpu_sorted.length >= 1 || sys_infos.processes.mem_sorted.length >= 1) {\n    console.log(chalk.bold('Intensive Processes'))\n    console.log(sys_proc_table.toString())\n  }\n}\n\n/**\n * Sys info line\n */\nfunction miniMonitBar(sys_infos) {\n  let sys_metrics = sys_infos.pm2_env.axm_monitor\n\n  let cpu = sys_metrics['CPU Usage']\n\n  if (typeof(cpu) == 'undefined') return\n\n  var sys_summary_line = `${chalk.bold.cyan('host metrics')} `\n  sys_summary_line += `| ${chalk.bold('cpu')}: ${UxHelpers.colorizedMetric(cpu.value, 40, 70, '%')}`\n\n  let temp = sys_metrics['CPU Temperature'].value\n  if (temp && temp != '-1') {\n    sys_summary_line += ` ${UxHelpers.colorizedMetric(temp, 50, 70, 'º')}`\n  }\n\n  let mem_total = sys_metrics['RAM Total'].value\n  let mem_available = sys_metrics['RAM Available'].value\n\n  if (mem_total) {\n    var perc_mem_usage = (((mem_available) / mem_total) * 100).toFixed(1)\n    sys_summary_line += ` | ${chalk.bold('mem free')}: ${UxHelpers.colorizedMetric(perc_mem_usage, 30, 10, '%')} `\n  }\n\n  let interfaces = Object.keys(sys_metrics).filter(m => m.includes('net') && m != 'net:default').map(i => i.split(':')[2]).filter((iface, i, self) => self.indexOf(iface) === i)\n\n  interfaces.forEach(iface => {\n    if (!sys_metrics[`net:rx_5:${iface}`]) return\n    sys_summary_line += `| ${chalk.bold(iface)}: `\n    sys_summary_line += `⇓ ${UxHelpers.colorizedMetric(sys_metrics[`net:rx_5:${iface}`].value, 10, 20, 'mb/s')} `\n    sys_summary_line += `⇑ ${UxHelpers.colorizedMetric(sys_metrics[`net:tx_5:${iface}`].value, 10, 20, 'mb/s')} `\n  })\n\n  if (CONDENSED_MODE == false) {\n    let read = sys_metrics['Disk Reads'].value\n    let write = sys_metrics['Disk Writes'].value\n\n    sys_summary_line += `| ${chalk.bold('disk')}: ⇓ ${UxHelpers.colorizedMetric(read, 10, 20, 'mb/s')}`\n    sys_summary_line += ` ⇑ ${UxHelpers.colorizedMetric(write, 10, 20, 'mb/s')} `\n\n    let disks = Object.keys(sys_metrics).filter(m => m.includes('fs:')).map(i => i.split(':')[2]).filter((iface, i, self) => self.indexOf(iface) === i)\n    var disk_nb = 0\n\n    disks.forEach(fs => {\n      let use = sys_metrics[`fs:use:${fs}`].value\n      if (use > 60)\n        sys_summary_line += `${chalk.gray(fs)} ${UxHelpers.colorizedMetric(use, 80, 90, '%')} `\n    })\n  }\n\n  sys_summary_line += '|'\n  console.log(sys_summary_line)\n}\n\n/**\n * pm2 ls\n * @method dispAsTable\n * @param {Object} list\n * @param {Object} system informations (via pm2 sysmonit/pm2 sysinfos)\n */\nmodule.exports = function(list, commander) {\n  var pm2_conf = Configuration.getSync('pm2')\n\n  if (!list)\n    return console.log('list empty')\n\n  listModulesAndAppsManaged(list, commander)\n\n  let sysmonit = list.filter(proc => proc.name == 'pm2-sysmonit')\n  if (sysmonit && sysmonit[0])\n    miniMonitBar(sysmonit[0])\n\n  // Disable warning message of process list not saved\n  //checkIfProcessAreDumped(list)\n}\n"
  },
  {
    "path": "lib/API/Version.js",
    "content": "\nvar cst        = require('../../constants.js');\nvar Common     = require('../Common.js');\nvar fs         = require('fs');\nvar eachSeries = require('async/eachSeries');\nvar child      = require('child_process');\n\nvar printError = Common.printError;\nvar printOut = Common.printOut;\n\nmodule.exports = function(CLI) {\n\n  var EXEC_TIMEOUT = 60000; // Default: 1 min\n\n  CLI.prototype._pull = function(opts, cb) {\n    var that = this;\n\n    var process_name = opts.process_name;\n    var reload_type = opts.action;\n\n    printOut(cst.PREFIX_MSG + 'Updating repository for process name %s', process_name);\n\n    that.Client.getProcessByNameOrId(process_name, function (err, processes) {\n\n      if (err || processes.length === 0) {\n        printError('No processes with this name or id : %s', process_name);\n        return cb ? cb({msg: 'Process not found: ' + process_name}) : that.exitCli(cst.ERROR_EXIT);\n      }\n\n      var proc = processes[0];\n      if (!proc.pm2_env.versioning) {\n        printOut(cst.PREFIX_MSG + 'No versioning system found for process %s', process_name);\n        return cb ? cb({success:false, msg: 'No versioning system found for process'}) : that.exitCli(cst.SUCCESS_EXIT);\n      }\n      require('vizion').update({\n        folder: proc.pm2_env.versioning.repo_path\n      }, function(err, meta) {\n        if (err !== null) {\n          return cb ? cb({msg:err}) : that.exitCli(cst.ERROR_EXIT);\n        }\n\n        if (meta.success === true) {\n          getPostUpdateCmds(proc.pm2_env.versioning.repo_path, process_name, function (command_list) {\n            execCommands(proc.pm2_env.versioning.repo_path, command_list, function(err, res) {\n              if (err !== null) {\n                printError(err);\n                return cb ? cb({msg: meta.output + err}) : that.exitCli(cst.ERROR_EXIT);\n              }\n              else {\n                printOut(cst.PREFIX_MSG + 'Process successfully updated %s', process_name);\n                printOut(cst.PREFIX_MSG + 'Current commit %s', meta.current_revision);\n                return that[reload_type](process_name, function(err, procs) {\n                  if (err && cb) return cb(err);\n                  if (err) console.error(err);\n                  return cb ? cb(null, meta.output + res) : that.exitCli(cst.SUCCESS_EXIT);\n                });\n              }\n            });\n          });\n        }\n        else {\n          printOut(cst.PREFIX_MSG + 'Already up-to-date or an error occured for app: %s', process_name);\n          return cb ? cb({success:false, msg : 'Already up to date'}) : that.exitCli(cst.SUCCESS_EXIT);\n        }\n        return false;\n      });\n      return false;\n    });\n  };\n\n  /**\n   * CLI method for updating a repository to a specific commit id\n   * @method pullCommitId\n   * @param {string} process_name\n   * @param {string} commit_id\n   * @return\n   */\n  CLI.prototype.pullCommitId = function(process_name, commit_id, cb) {\n    var reload_type = 'reload';\n    var that = this;\n\n    printOut(cst.PREFIX_MSG + 'Updating repository for process name %s', process_name);\n\n    that.Client.getProcessByNameOrId(process_name, function (err, processes) {\n\n      if (err || processes.length === 0) {\n        printError('No processes with this name or id : %s', process_name);\n        return cb ? cb({msg: 'Process not found: ' + process_name}) : that.exitCli(cst.ERROR_EXIT);\n      }\n\n      var proc = processes[0];\n      if (proc.pm2_env.versioning) {\n        require('vizion').isUpToDate({folder: proc.pm2_env.versioning.repo_path}, function(err, meta) {\n          if (err !== null)\n            return cb ? cb({msg:err}) : that.exitCli(cst.ERROR_EXIT);\n          require('vizion').revertTo(\n            {revision: commit_id,\n             folder: proc.pm2_env.versioning.repo_path},\n            function(err2, meta2) {\n              if (!err2 && meta2.success) {\n                getPostUpdateCmds(proc.pm2_env.versioning.repo_path, process_name, function (command_list) {\n                  execCommands(proc.pm2_env.versioning.repo_path, command_list, function(err, res) {\n                    if (err !== null)\n                    {\n                      printError(err);\n                      return cb ? cb({msg:err}) : that.exitCli(cst.ERROR_EXIT);\n                    }\n                    else {\n                      printOut(cst.PREFIX_MSG + 'Process successfully updated %s', process_name);\n                      printOut(cst.PREFIX_MSG + 'Current commit %s', commit_id);\n                      return that[reload_type](process_name, cb);\n                    }\n                  });\n                });\n              }\n              else {\n                printOut(cst.PREFIX_MSG + 'Already up-to-date or an error occured: %s', process_name);\n                return cb ? cb(null, {success:meta.success}) : that.exitCli(cst.SUCCESS_EXIT);\n              }\n            });\n        });\n      }\n      else {\n        printOut(cst.PREFIX_MSG + 'No versioning system found for process %s', process_name);\n        return cb ? cb(null, {success:false}) : that.exitCli(cst.SUCCESS_EXIT);\n      }\n    });\n  };\n\n  /**\n   * CLI method for downgrading a repository to the previous commit (older)\n   * @method backward\n   * @param {string} process_name\n   * @return\n   */\n  CLI.prototype.backward = function(process_name, cb) {\n    var that = this;\n    printOut(cst.PREFIX_MSG + 'Downgrading to previous commit repository for process name %s', process_name);\n\n    that.Client.getProcessByNameOrId(process_name, function (err, processes) {\n\n      if (err || processes.length === 0) {\n        printError('No processes with this name or id : %s', process_name);\n        return cb ? cb({msg: 'Process not found: ' + process_name}) : that.exitCli(cst.ERROR_EXIT);\n      }\n\n      var proc = processes[0];\n      // in case user searched by id/pid\n      process_name = proc.name;\n\n      if (proc.pm2_env.versioning === undefined ||\n          proc.pm2_env.versioning === null)\n        return cb({msg : 'Versioning unknown'});\n\n      require('vizion').prev({\n        folder: proc.pm2_env.versioning.repo_path\n      }, function(err, meta) {\n        if (err)\n          return cb ? cb({msg:err, data : meta}) : that.exitCli(cst.ERROR_EXIT);\n\n        if (meta.success !== true) {\n          printOut(cst.PREFIX_MSG + 'No versioning system found for process %s', process_name);\n          return cb ? cb({msg:err, data : meta}) : that.exitCli(cst.ERROR_EXIT);\n        }\n\n        getPostUpdateCmds(proc.pm2_env.versioning.repo_path, process_name, function (command_list) {\n          execCommands(proc.pm2_env.versioning.repo_path, command_list, function(err, res) {\n            if (err !== null) {\n              require('vizion').next({folder: proc.pm2_env.versioning.repo_path}, function(err2, meta2) {\n                printError(err);\n                return cb ? cb({msg: meta.output + err}) : that.exitCli(cst.ERROR_EXIT);\n              });\n              return false;\n            }\n\n            printOut(cst.PREFIX_MSG + 'Process successfully updated %s', process_name);\n            printOut(cst.PREFIX_MSG + 'Current commit %s', meta.current_revision);\n            that.reload(process_name, function(err, procs) {\n              if (err) return cb(err);\n              return cb ? cb(null, meta.output + res) : that.exitCli(cst.SUCCESS_EXIT);\n            });\n          });\n        });\n      });\n    });\n  };\n\n  /**\n   * CLI method for updating a repository to the next commit (more recent)\n   * @method forward\n   * @param {string} process_name\n   * @return\n   */\n  CLI.prototype.forward = function(process_name, cb) {\n    var that = this;\n    printOut(cst.PREFIX_MSG + 'Updating to next commit repository for process name %s', process_name);\n\n    that.Client.getProcessByNameOrId(process_name, function (err, processes) {\n\n      if (err || processes.length === 0) {\n        printError('No processes with this name or id: %s', process_name);\n        return cb ? cb({msg: 'Process not found: ' + process_name}) : that.exitCli(cst.ERROR_EXIT);\n      }\n\n      var proc = processes[0];\n      // in case user searched by id/pid\n      process_name = proc.name;\n      if (proc.pm2_env.versioning) {\n        require('vizion').next({folder: proc.pm2_env.versioning.repo_path}, function(err, meta) {\n          if (err !== null)\n            return cb ? cb({msg:err}) : that.exitCli(cst.ERROR_EXIT);\n          if (meta.success === true) {\n            getPostUpdateCmds(proc.pm2_env.versioning.repo_path, process_name, function (command_list) {\n              execCommands(proc.pm2_env.versioning.repo_path, command_list, function(err, res) {\n                if (err !== null)\n                {\n                  require('vizion').prev({folder: proc.pm2_env.versioning.repo_path}, function(err2, meta2) {\n                    printError(err);\n                    return cb ? cb({msg:meta.output + err}) : that.exitCli(cst.ERROR_EXIT);\n                  });\n                }\n                else {\n                  printOut(cst.PREFIX_MSG + 'Process successfully updated %s', process_name);\n                  printOut(cst.PREFIX_MSG + 'Current commit %s', meta.current_revision);\n                  that.reload(process_name, function(err, procs) {\n                    if (err) return cb(err);\n                    return cb ? cb(null, meta.output + res) : that.exitCli(cst.SUCCESS_EXIT);\n                  });\n                }\n              });\n            });\n          }\n          else {\n            printOut(cst.PREFIX_MSG + 'Already up-to-date or an error occured: %s', process_name);\n            return cb ? cb(null, {success:meta.success}) : that.exitCli(cst.SUCCESS_EXIT);\n          }\n        });\n      }\n      else {\n        printOut(cst.PREFIX_MSG + 'No versioning system found for process %s', process_name);\n        return cb ? cb({success:false, msg: 'No versioning system found'}) : that.exitCli(cst.SUCCESS_EXIT);\n      }\n    });\n  };\n\n  var exec = function (cmd, callback) {\n    var output = '';\n\n    var c = child.exec(cmd, {\n      env: process.env,\n      maxBuffer: 3*1024*1024,\n      timeout: EXEC_TIMEOUT\n    }, function(err) {\n      if (callback)\n        callback(err ? err.code : 0, output);\n    });\n\n    c.stdout.on('data', function(data) {\n      output += data;\n    });\n\n    c.stderr.on('data', function(data) {\n      output += data;\n    });\n  };\n\n  /**\n   *\n   * @method execCommands\n   * @param {string} repo_path\n   * @param {object} command_list\n   * @return\n   */\n  var execCommands = function(repo_path, command_list, cb) {\n    var stdout = '';\n\n    eachSeries(command_list, function(command, callback) {\n      stdout += '\\n' + command;\n      exec('cd '+repo_path+';'+command,\n           function(code, output) {\n             stdout += '\\n' + output;\n             if (code === 0)\n               callback();\n             else\n               callback('`'+command+'` failed');\n           });\n    }, function(err) {\n      if (err)\n        return cb(stdout + '\\n' + err);\n      return cb(null, stdout);\n    });\n  }\n\n  /**\n   * Description Search process.json for post-update commands\n   * @method getPostUpdateCmds\n   * @param {string} repo_path\n   * @param {string} proc_name\n   * @return\n   */\n  var getPostUpdateCmds = function(repo_path, proc_name, cb) {\n    if (typeof repo_path !== 'string')\n      return cb([]);\n    if (repo_path[repo_path.length - 1] !== '/')\n      repo_path += '/';\n\n    var searchForCommands = function(file, callback) {\n      fs.exists(repo_path+file, function(exists) {\n        if (exists) {\n          try {\n            var conf_string = fs.readFileSync(repo_path + file);\n            var data = Common.parseConfig(conf_string, repo_path + file);\n          } catch (e) {\n            console.error(e.message || e);\n          }\n\n          if (data && data.apps) {\n            eachSeries(data.apps, function(item, callb) {\n              if (item.name && item.name === proc_name) {\n                if (item.post_update && typeof(item.post_update) === 'object') {\n                  if (item.exec_timeout)\n                    EXEC_TIMEOUT = parseInt(item.exec_timeout);\n                  return callb(item.post_update);\n                }\n                else {\n                  return callb();\n                }\n              }\n              else\n                return callb();\n            }, function(final) {\n              return callback(final);\n            });\n          }\n          else {\n            return callback();\n          }\n        }\n        else {\n          return callback();\n        }\n      });\n    };\n\n    eachSeries(['ecosystem.json', 'process.json', 'package.json'], searchForCommands,\n                     function(final) {\n                       return cb(final ? final : []);\n                     });\n  };\n\n\n  /**\n   * CLI method for updating a repository\n   * @method pullAndRestart\n   * @param {string} process_name name of processes to pull\n   * @return\n   */\n  CLI.prototype.pullAndRestart = function (process_name, cb) {\n    this._pull({process_name: process_name, action: 'reload'}, cb);\n  };\n\n  /**\n   * CLI method for updating a repository\n   * @method pullAndReload\n   * @param {string} process_name name of processes to pull\n   * @return\n   */\n  CLI.prototype.pullAndReload = function (process_name, cb) {\n    this._pull({process_name: process_name, action: 'reload'}, cb);\n  };\n\n  /**\n   * CLI method for updating a repository to a specific commit id\n   * @method pullCommitId\n   * @param {object} opts\n   * @return\n   */\n  CLI.prototype._pullCommitId = function (opts, cb) {\n    this.pullCommitId(opts.pm2_name, opts.commit_id, cb);\n  };\n\n}\n"
  },
  {
    "path": "lib/API/interpreter.json",
    "content": "{\n  \".sh\"     : \"bash\",\n  \".py\"     : \"python\",\n  \".rb\"     : \"ruby\",\n  \".php\"    : \"php\",\n  \".pl\"     : \"perl\",\n  \".js\"     : \"node\",\n  \".coffee\" : \"coffee\",\n  \".ls\"     : \"lsc\",\n  \".ts\"     : \"bun\",\n  \".tsx\"    : \"bun\"\n}\n"
  },
  {
    "path": "lib/API/pm2-plus/PM2IO.js",
    "content": "'use strict'\n\nvar cst = require('../../../constants.js');\nconst chalk = require('ansis');\nconst path = require('path');\nconst fs  = require('fs');\nconst Table = require('cli-tableau');\nconst pkg = require('../../../package.json')\nconst IOAPI = require('@pm2/js-api')\nconst promptly = require('promptly')\nvar CLIStrategy = require('./auth-strategies/CliAuth')\nvar WebStrategy = require('./auth-strategies/WebAuth')\nconst exec = require('child_process').exec\n\nconst OAUTH_CLIENT_ID_WEB = '138558311'\nconst OAUTH_CLIENT_ID_CLI = '0943857435'\n\nmodule.exports = class PM2ioHandler {\n\n  static usePM2Client (instance) {\n    this.pm2 = instance\n  }\n\n  static strategy () {\n    switch (process.platform) {\n      case 'darwin': {\n        return new WebStrategy({\n          client_id: OAUTH_CLIENT_ID_WEB\n        })\n      }\n      case 'win32': {\n        return new WebStrategy({\n          client_id: OAUTH_CLIENT_ID_WEB\n        })\n      }\n      case 'linux': {\n        const isDesktop = process.env.XDG_CURRENT_DESKTOP || process.env.XDG_SESSION_DESKTOP || process.env.DISPLAY\n        const isSSH = process.env.SSH_TTY || process.env.SSH_CONNECTION\n        if (isDesktop && !isSSH) {\n          return new WebStrategy({\n            client_id: OAUTH_CLIENT_ID_WEB\n          })\n        } else {\n          return new CLIStrategy({\n            client_id: OAUTH_CLIENT_ID_CLI\n          })\n        }\n      }\n      default: {\n        return new CLIStrategy({\n          client_id: OAUTH_CLIENT_ID_CLI\n        })\n      }\n    }\n  }\n\n  static init () {\n    this._strategy = this.strategy()\n    /**\n     * If you are using a local backend you should give those options :\n     * {\n     *   services: {\n     *    API: 'http://localhost:3000',\n     *    OAUTH: 'http://localhost:3100'\n     *   }\n     *  }\n     */\n    this.io = new IOAPI().use(this._strategy)\n  }\n\n  static launch (command, opts) {\n    // first init the strategy and the io client\n    this.init()\n\n    switch (command) {\n      case 'connect' :\n      case 'login' :\n      case 'register' :\n      case undefined :\n      case 'authenticate' : {\n        this.authenticate()\n        break\n      }\n      case 'validate' : {\n        this.validateAccount(opts)\n        break\n      }\n      case 'help' :\n      case 'welcome': {\n        var dt = fs.readFileSync(path.join(__dirname, './pres/welcome'));\n        console.log(dt.toString());\n        return process.exit(0)\n      }\n      case 'logout': {\n        this._strategy.isAuthenticated().then(isConnected => {\n          // try to kill the agent anyway\n          this.pm2.killAgent(err => {})\n\n          if (isConnected === false) {\n            console.log(`${cst.PM2_IO_MSG} Already disconnected`)\n            return process.exit(0)\n          }\n\n          this._strategy._retrieveTokens((err, tokens) => {\n            if (err) {\n              console.log(`${cst.PM2_IO_MSG} Successfully disconnected`)\n              return process.exit(0)\n            }\n            this._strategy.deleteTokens(this.io).then(_ => {\n              console.log(`${cst.PM2_IO_MSG} Successfully disconnected`)\n              return process.exit(0)\n            }).catch(err => {\n              console.log(`${cst.PM2_IO_MSG_ERR} Unexpected error: ${err.message}`)\n              return process.exit(1)\n            })\n          })\n        }).catch(err => {\n          console.error(`${cst.PM2_IO_MSG_ERR} Failed to logout: ${err.message}`)\n          console.error(`${cst.PM2_IO_MSG_ERR} You can also contact us to get help: contact@pm2.io`)\n        })\n        break\n      }\n      case 'create': {\n        this._strategy.isAuthenticated().then(res => {\n          // if the user isn't authenticated, we make them do the whole flow\n          if (res !== true) {\n            this.authenticate()\n          } else {\n            this.createBucket(this.createBucketHandler.bind(this))\n          }\n        }).catch(err => {\n          console.error(`${cst.PM2_IO_MSG_ERR} Failed to create to the bucket: ${err.message}`)\n          console.error(`${cst.PM2_IO_MSG_ERR} You can also contact us to get help: contact@pm2.io`)\n        })\n        break\n      }\n      case 'web': {\n        this._strategy.isAuthenticated().then(res => {\n          // if the user isn't authenticated, we make them do the whole flow\n          if (res === false) {\n            console.error(`${cst.PM2_IO_MSG_ERR} You need to be authenticated to do that, please use: pm2 plus login`)\n            return process.exit(1)\n          }\n          this._strategy._retrieveTokens(() => {\n            return this.openUI()\n          })\n        }).catch(err => {\n          console.error(`${cst.PM2_IO_MSG_ERR} Failed to open the UI: ${err.message}`)\n          console.error(`${cst.PM2_IO_MSG_ERR} You can also contact us to get help: contact@pm2.io`)\n        })\n        break\n      }\n      default : {\n        console.log(`${cst.PM2_IO_MSG_ERR} Invalid command ${command}, available : login,register,validate,connect or web`)\n        process.exit(1)\n      }\n    }\n  }\n\n  static openUI () {\n    this.io.bucket.retrieveAll().then(res => {\n      const buckets = res.data\n\n      if (buckets.length === 0) {\n        return this.createBucket((err, bucket) => {\n          if (err) {\n            console.error(`${cst.PM2_IO_MSG_ERR} Failed to connect to the bucket: ${err.message}`)\n            if (bucket) {\n              console.error(`${cst.PM2_IO_MSG_ERR} You can retry using: pm2 plus link ${bucket.secret_id} ${bucket.public_id}`)\n            }\n            console.error(`${cst.PM2_IO_MSG_ERR} You can also contact us to get help: contact@pm2.io`)\n            return process.exit(0)\n          }\n          const targetURL = `https://app.pm2.io/#/bucket/${bucket._id}`\n          console.log(`${cst.PM2_IO_MSG} Please follow the popup or go to this URL :`, '\\n', '    ', targetURL)\n          this.open(targetURL)\n          return process.exit(0)\n        })\n      }\n\n      var table = new Table({\n        style : {'padding-left' : 1, head : ['cyan', 'bold'], compact : true},\n        head : ['Bucket name', 'Plan type']\n      })\n\n      buckets.forEach(function(bucket) {\n        table.push([bucket.name, bucket.credits.offer_type])\n      })\n      console.log(table.toString())\n      console.log(`${cst.PM2_IO_MSG} If you don't want to open the UI to a bucket, type 'none'`)\n\n      const choices = buckets.map(bucket => bucket.name)\n      choices.push('none')\n\n      promptly.choose(`${cst.PM2_IO_MSG} Type the name of the bucket you want to connect to :`, choices, (err, value) => {\n        if (value === 'none') process.exit(0)\n\n        const bucket = buckets.find(bucket => bucket.name === value)\n        if (bucket === undefined) return process.exit(0)\n\n        const targetURL = `https://app.pm2.io/#/bucket/${bucket._id}`\n        console.log(`${cst.PM2_IO_MSG} Please follow the popup or go to this URL :`, '\\n', '    ', targetURL)\n        this.open(targetURL)\n        return process.exit(0)\n      })\n    })\n  }\n\n  static validateAccount (token) {\n    this.io.auth.validEmail(token)\n      .then(res => {\n        console.log(`${cst.PM2_IO_MSG} Email succesfully validated.`)\n        console.log(`${cst.PM2_IO_MSG} You can now proceed and use: pm2 plus connect`)\n        return process.exit(0)\n      }).catch(err => {\n        if (err.status === 401) {\n          console.error(`${cst.PM2_IO_MSG_ERR} Invalid token`)\n          return process.exit(1)\n        } else if (err.status === 301) {\n          console.log(`${cst.PM2_IO_MSG} Email succesfully validated.`)\n          console.log(`${cst.PM2_IO_MSG} You can now proceed and use: pm2 plus connect`)\n          return process.exit(0)\n        }\n        const msg = err.data ? err.data.error_description || err.data.msg : err.message\n        console.error(`${cst.PM2_IO_MSG_ERR} Failed to validate your email: ${msg}`)\n        console.error(`${cst.PM2_IO_MSG_ERR} You can also contact us to get help: contact@pm2.io`)\n        return process.exit(1)\n      })\n  }\n\n  static createBucketHandler (err, bucket) {\n    if (err) {\n      console.trace(`${cst.PM2_IO_MSG_ERR} Failed to connect to the bucket: ${err.message}`)\n      if (bucket) {\n        console.error(`${cst.PM2_IO_MSG_ERR} You can retry using: pm2 plus link ${bucket.secret_id} ${bucket.public_id}`)\n      }\n      console.error(`${cst.PM2_IO_MSG_ERR} You can also contact us to get help: contact@pm2.io`)\n      return process.exit(0)\n    }\n    if (bucket === undefined) {\n      return process.exit(0)\n    }\n    console.log(`${cst.PM2_IO_MSG} Successfully connected to bucket ${bucket.name}`)\n    var targetURL = `https://app.pm2.io/#/bucket/${bucket._id}`\n    console.log(`${cst.PM2_IO_MSG} You can use the web interface over there: ${targetURL}`)\n    this.open(targetURL)\n    return process.exit(0)\n  }\n\n  static createBucket (cb) {\n    console.log(`${cst.PM2_IO_MSG} By default we allow you to trial PM2 Plus for 14 days without any credit card.`)\n\n    this.io.bucket.create({\n      name: 'PM2 Plus Monitoring'\n    }).then(res => {\n      const bucket = res.data.bucket\n\n      console.log(`${cst.PM2_IO_MSG} Successfully created the bucket`)\n      this.pm2.link({\n        public_key: bucket.public_id,\n        secret_key: bucket.secret_id,\n        pm2_version: pkg.version\n      }, (err) => {\n        if (err) {\n          return cb(new Error('Failed to connect your local PM2 to your bucket'), bucket)\n        } else {\n          return cb(null, bucket)\n        }\n      })\n    }).catch(err => {\n      return cb(new Error(`Failed to create a bucket: ${err.message}`))\n    })\n  }\n\n  /**\n   * Connect the local agent to a specific bucket\n   * @param {Function} cb\n   */\n  static connectToBucket (cb) {\n    this.io.bucket.retrieveAll().then(res => {\n      const buckets = res.data\n\n      if (buckets.length === 0) {\n        return this.createBucket(cb)\n      }\n\n      var table = new Table({\n        style : {'padding-left' : 1, head : ['cyan', 'bold'], compact : true},\n        head : ['Bucket name', 'Plan type']\n      })\n\n      buckets.forEach(function(bucket) {\n        table.push([bucket.name, bucket.payment.offer_type])\n      })\n      console.log(table.toString())\n      console.log(`${cst.PM2_IO_MSG} If you don't want to connect to a bucket, type 'none'`)\n\n      const choices = buckets.map(bucket => bucket.name)\n      choices.push('none')\n\n      promptly.choose(`${cst.PM2_IO_MSG} Type the name of the bucket you want to connect to :`, choices, (err, value) => {\n        if (value === 'none') return cb()\n\n        const bucket = buckets.find(bucket => bucket.name === value)\n        if (bucket === undefined) return cb()\n        this.pm2.link({\n          public_key: bucket.public_id,\n          secret_key: bucket.secret_id,\n          pm2_version: pkg.version\n        }, (err) => {\n          return err ? cb(err) : cb(null, bucket)\n        })\n      })\n    })\n  }\n\n  /**\n   * Authenticate the user with either of the strategy\n   * @param {Function} cb\n   */\n  static authenticate () {\n    this._strategy._retrieveTokens((err, tokens) => {\n      if (err) {\n        const msg = err.data ? err.data.error_description || err.data.msg : err.message\n        console.log(`${cst.PM2_IO_MSG_ERR} Unexpected error : ${msg}`)\n        return process.exit(1)\n      }\n      console.log(`${cst.PM2_IO_MSG} Successfully authenticated`)\n      this.io.user.retrieve().then(res => {\n        const user = res.data\n\n        this.io.user.retrieve().then(res => {\n          const tmpUser = res.data\n          console.log(`${cst.PM2_IO_MSG} Successfully validated`)\n          this.connectToBucket(this.createBucketHandler.bind(this))\n        })\n      })\n    })\n  }\n\n  static open (target, appName, callback) {\n    let opener\n    const escape = function (s) {\n      return s.replace(/\"/g, '\\\\\"')\n    }\n\n    if (typeof (appName) === 'function') {\n      callback = appName\n      appName = null\n    }\n\n    switch (process.platform) {\n      case 'darwin': {\n        opener = appName ? `open -a \"${escape(appName)}\"` : `open`\n        break\n      }\n      case 'win32': {\n        opener = appName ? `start \"\" ${escape(appName)}\"` : `start \"\"`\n        break\n      }\n      default: {\n        opener = appName ? escape(appName) : `xdg-open`\n        break\n      }\n    }\n\n    if (process.env.SUDO_USER) {\n      opener = 'sudo -u ' + process.env.SUDO_USER + ' ' + opener\n    }\n    return exec(`${opener} \"${escape(target)}\"`, callback)\n  }\n}\n"
  },
  {
    "path": "lib/API/pm2-plus/auth-strategies/CliAuth.js",
    "content": "'use strict'\n\nconst AuthStrategy = require('@pm2/js-api/src/auth_strategies/strategy')\nconst querystring = require('querystring');\n\nconst http = require('http')\nconst fs = require('fs')\nconst url = require('url')\nconst exec = require('child_process').exec\nconst tryEach = require('async/tryEach')\nconst path = require('path')\nconst os = require('os')\nconst needle = require('needle')\nconst chalk = require('ansis')\nconst cst = require('../../../../constants.js')\nconst promptly = require('promptly')\n\nmodule.exports = class CliStrategy extends AuthStrategy {\n  // the client will try to call this but we handle this part ourselves\n  retrieveTokens (km, cb) {\n    this.authenticated = false\n    this.callback = cb\n    this.km = km\n    this.BASE_URI = 'https://id.keymetrics.io';\n  }\n\n  // so the cli know if we need to tell user to login/register\n  isAuthenticated () {\n    return new Promise((resolve, reject) => {\n      if (this.authenticated) return resolve(true)\n\n      let tokensPath = cst.PM2_IO_ACCESS_TOKEN\n      fs.readFile(tokensPath, (err, tokens) => {\n        if (err && err.code === 'ENOENT') return resolve(false)\n        if (err) return reject(err)\n\n        // verify that the token is valid\n        try {\n          tokens = JSON.parse(tokens || '{}')\n        } catch (err) {\n          fs.unlinkSync(tokensPath)\n          return resolve(false)\n        }\n\n        // if the refresh tokens is here, the user could be automatically authenticated\n        return resolve(typeof tokens.refresh_token === 'string')\n      })\n    })\n  }\n\n  verifyToken (refresh) {\n    return this.km.auth.retrieveToken({\n      client_id: this.client_id,\n      refresh_token: refresh\n    })\n  }\n\n  // called when we are sure the user asked to be logged in\n  _retrieveTokens (optionalCallback) {\n    const km = this.km\n    const cb = this.callback\n\n    tryEach([\n      // try to find the token via the environment\n      (next) => {\n        if (!process.env.PM2_IO_TOKEN) {\n          return next(new Error('No token in env'))\n        }\n        this.verifyToken(process.env.PM2_IO_TOKEN)\n          .then((res) => {\n            return next(null, res.data)\n          }).catch(next)\n      },\n      // try to find it in the file system\n      (next) => {\n        fs.readFile(cst.PM2_IO_ACCESS_TOKEN, (err, tokens) => {\n          if (err) return next(err)\n          // verify that the token is valid\n          tokens = JSON.parse(tokens || '{}')\n          if (new Date(tokens.expire_at) > new Date(new Date().toISOString())) {\n            return next(null, tokens)\n          }\n\n          this.verifyToken(tokens.refresh_token)\n            .then((res) => {\n              return next(null, res.data)\n            }).catch(next)\n        })\n      },\n      // otherwise make the whole flow\n      (next) => {\n        return this.authenticate((err, data) => {\n          if (err instanceof Error) return next(err)\n          // verify that the token is valid\n          this.verifyToken(data.refresh_token)\n            .then((res) => {\n              return next(null, res.data)\n            }).catch(next)\n        })\n      }\n    ], (err, result) => {\n      // if present run the optional callback\n      if (typeof optionalCallback === 'function') {\n        optionalCallback(err, result)\n      }\n\n      if (result.refresh_token) {\n        this.authenticated = true\n        let file = cst.PM2_IO_ACCESS_TOKEN\n        fs.writeFile(file, JSON.stringify(result), () => {\n          return cb(err, result)\n        })\n      } else {\n        return cb(err, result)\n      }\n    })\n  }\n\n  authenticate (cb) {\n    console.log(`${cst.PM2_IO_MSG} Using non-browser authentication.`)\n    promptly.confirm(`${cst.PM2_IO_MSG} Do you have a pm2.io account? (y/n)`, (err, answer) => {\n      // Either login or register\n      return answer === true ? this.login(cb) : this.register(cb)\n    })\n  }\n\n  login (cb) {\n    let retry = () => {\n      promptly.prompt(`${cst.PM2_IO_MSG} Your username or email: `, (err, username) => {\n        if (err) return retry();\n\n        promptly.password(`${cst.PM2_IO_MSG} Your password: `, { replace : '*' }, (err, password) => {\n          if (err) return retry();\n\n          console.log(`${cst.PM2_IO_MSG} Authenticating ...`)\n          this._loginUser({\n            username: username,\n            password: password\n          }, (err, data) => {\n            if (err) {\n              console.error(`${cst.PM2_IO_MSG_ERR} Failed to authenticate: ${err.message}`)\n              return retry()\n            }\n            return cb(null, data)\n          })\n        })\n      })\n    }\n\n    retry()\n  }\n\n  register (cb) {\n    console.log(`${cst.PM2_IO_MSG} No problem ! We just need few informations to create your account`)\n\n    var retry = () => {\n      promptly.prompt(`${cst.PM2_IO_MSG} Please choose an username :`, {\n        validator : this._validateUsername,\n        retry : true\n      }, (err, username) => {\n        promptly.prompt(`${cst.PM2_IO_MSG} Please choose an email :`, {\n          validator : this._validateEmail,\n          retry : true\n        },(err, email) => {\n          promptly.password(`${cst.PM2_IO_MSG} Please choose a password :`, { replace : '*' }, (err, password) => {\n            promptly.confirm(`${cst.PM2_IO_MSG} Do you accept the terms and privacy policy (https://pm2.io/legals/terms_conditions.pdf) ?  (y/n)`, (err, answer) => {\n              if (err) {\n                console.error(chalk.bold.red(err));\n                return retry()\n              } else if (answer === false) {\n                console.error(`${cst.PM2_IO_MSG_ERR} You must accept the terms and privacy policy to contiue.`)\n                return retry()\n              }\n\n              this._registerUser({\n                email : email,\n                password : password,\n                username : username\n              }, (err, data) => {\n                console.log('\\n')\n                if (err) {\n                  console.error(`${cst.PM2_IO_MSG_ERR} Unexpect error: ${err.message}`)\n                  console.error(`${cst.PM2_IO_MSG_ERR} You can also contact us to get help: contact@pm2.io`)\n                  return process.exit(1)\n                }\n                return cb(undefined, data)\n              })\n            })\n          })\n        })\n      })\n    }\n    retry()\n  }\n\n  /**\n   * Register function\n   * @param opts.username\n   * @param opts.password\n   * @param opts.email\n   */\n  _registerUser (opts, cb) {\n    const data = Object.assign(opts, {\n      password_confirmation: opts.password,\n      accept_terms: true\n    })\n    needle.post(this.BASE_URI + '/api/oauth/register', data, {\n      json: true,\n      headers: {\n        'X-Register-Provider': 'pm2-register',\n        'x-client-id': this.client_id\n      }\n    }, function (err, res, body) {\n      if (err) return cb(err)\n      if (body.email && body.email.message) return cb(new Error(body.email.message))\n      if (body.username && body.username.message) return cb(new Error(body.username.message))\n      if (!body.access_token) return cb(new Error(body.msg))\n\n      return cb(null, {\n        refresh_token : body.refresh_token.token,\n        access_token : body.access_token.token\n      })\n    });\n  }\n\n  _loginUser (user_info, cb) {\n    const URL_AUTH = '/api/oauth/authorize?response_type=token&scope=all&client_id=' +\n            this.client_id + '&redirect_uri=http://localhost:43532';\n\n    needle.get(this.BASE_URI + URL_AUTH, (err, res) => {\n      if (err) return cb(err);\n\n      var cookie = res.cookies;\n\n      needle.post(this.BASE_URI + '/api/oauth/login', user_info, {\n        cookies : cookie\n      }, (err, resp, body) => {\n        if (err) return cb(err)\n        if (resp.statusCode != 200) return cb('Wrong credentials')\n\n        var location = resp.headers['x-redirect']\n\n        needle.get(this.BASE_URI + location, {\n          cookies : cookie\n        }, (err, res) => {\n          if (err) return cb(err);\n          var refresh_token = querystring.parse(url.parse(res.headers.location).query).access_token;\n          needle.post(this.BASE_URI + '/api/oauth/token', {\n            client_id : this.client_id,\n            grant_type : 'refresh_token',\n            refresh_token : refresh_token,\n            scope : 'all'\n          }, (err, res, body) => {\n            if (err) return cb(err)\n            return cb(null, body)\n          })\n        })\n      })\n    })\n  }\n\n  _validateEmail (email) {\n    var re = /^(([^<>()\\[\\]\\\\.,;:\\s@\"]+(\\.[^<>()\\[\\]\\\\.,;:\\s@\"]+)*)|(\".+\"))@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}])|(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))$/;\n    if (re.test(email) == false)\n      throw new Error('Not an email');\n    return email;\n  }\n\n  _validateUsername (value) {\n    if (value.length < 6) {\n      throw new Error('Min length of 6');\n    }\n    return value;\n  };\n\n  deleteTokens (km) {\n    return new Promise((resolve, reject) => {\n      // revoke the refreshToken\n      km.auth.revoke()\n        .then(res => {\n          // remove the token from the filesystem\n          let file = cst.PM2_IO_ACCESS_TOKEN\n          fs.unlinkSync(file)\n          return resolve(res)\n        }).catch(reject)\n    })\n  }\n}\n"
  },
  {
    "path": "lib/API/pm2-plus/auth-strategies/WebAuth.js",
    "content": "\n'use strict'\n\nconst cst = require('../../../../constants.js');\n\nconst AuthStrategy = require('@pm2/js-api/src/auth_strategies/strategy')\nconst http = require('http')\nconst fs = require('fs')\nconst url = require('url')\nconst exec = require('child_process').exec\nconst tryEach = require('async/tryEach');\n\nmodule.exports = class WebStrategy extends AuthStrategy {\n  // the client will try to call this but we handle this part ourselves\n  retrieveTokens (km, cb) {\n    this.authenticated = false\n    this.callback = cb\n    this.km = km\n  }\n\n  // so the cli know if we need to tell user to login/register\n  isAuthenticated () {\n    return new Promise((resolve, reject) => {\n      if (this.authenticated) return resolve(true)\n\n      let tokensPath = cst.PM2_IO_ACCESS_TOKEN\n      fs.readFile(tokensPath, (err, tokens) => {\n        if (err && err.code === 'ENOENT') return resolve(false)\n        if (err) return reject(err)\n\n        // verify that the token is valid\n        try {\n          tokens = JSON.parse(tokens || '{}')\n        } catch (err) {\n          fs.unlinkSync(tokensPath)\n          return resolve(false)\n        }\n\n        // if the refresh tokens is here, the user could be automatically authenticated\n        return resolve(typeof tokens.refresh_token === 'string')\n      })\n    })\n  }\n\n  // called when we are sure the user asked to be logged in\n  _retrieveTokens (optionalCallback) {\n    const km = this.km\n    const cb = this.callback\n\n    let verifyToken = (refresh) => {\n      return km.auth.retrieveToken({\n        client_id: this.client_id,\n        refresh_token: refresh\n      })\n    }\n    tryEach([\n      // try to find the token via the environment\n      (next) => {\n        if (!process.env.PM2_IO_TOKEN) {\n          return next(new Error('No token in env'))\n        }\n        verifyToken(process.env.PM2_IO_TOKEN)\n          .then((res) => {\n            return next(null, res.data)\n          }).catch(next)\n      },\n      // try to find it in the file system\n      (next) => {\n        fs.readFile(cst.PM2_IO_ACCESS_TOKEN, (err, tokens) => {\n          if (err) return next(err)\n          // verify that the token is valid\n          tokens = JSON.parse(tokens || '{}')\n          if (new Date(tokens.expire_at) > new Date(new Date().toISOString())) {\n            return next(null, tokens)\n          }\n\n          verifyToken(tokens.refresh_token)\n            .then((res) => {\n              return next(null, res.data)\n            }).catch(next)\n        })\n      },\n      // otherwise make the whole flow\n      (next) => {\n        return this.loginViaWeb((data) => {\n          // verify that the token is valid\n          verifyToken(data.access_token)\n            .then((res) => {\n              return next(null, res.data)\n            }).catch(err => next(err))\n        })\n      }\n    ], (err, result) => {\n      // if present run the optional callback\n      if (typeof optionalCallback === 'function') {\n        optionalCallback(err, result)\n      }\n\n      if (result.refresh_token) {\n        this.authenticated = true\n        let file = cst.PM2_IO_ACCESS_TOKEN\n        fs.writeFile(file, JSON.stringify(result), () => {\n          return cb(err, result)\n        })\n      } else {\n        return cb(err, result)\n      }\n    })\n  }\n\n  loginViaWeb (cb) {\n    const redirectURL = `${this.oauth_endpoint}${this.oauth_query}`\n\n    console.log(`${cst.PM2_IO_MSG} Please follow the popup or go to this URL :`, '\\n', '    ', redirectURL)\n\n    let shutdown = false\n    let server = http.createServer((req, res) => {\n      // only handle one request\n      if (shutdown === true) return res.end()\n      shutdown = true\n\n      let query = url.parse(req.url, true).query\n\n      res.write(`\n        <head>\n          <script>\n          </script>\n        </head>\n        <body>\n          <h2 style=\"text-align: center\">\n            You can go back to your terminal now :)\n          </h2>\n        </body>`)\n      res.end()\n      server.close()\n      return cb(query)\n    })\n    server.listen(43532, () => {\n      this.open(redirectURL)\n    })\n  }\n\n  deleteTokens (km) {\n    return new Promise((resolve, reject) => {\n      // revoke the refreshToken\n      km.auth.revoke()\n        .then(res => {\n          // remove the token from the filesystem\n          let file = cst.PM2_IO_ACCESS_TOKEN\n          fs.unlinkSync(file)\n          return resolve(res)\n        }).catch(reject)\n    })\n  }\n\n  open (target, appName, callback) {\n    let opener\n    const escape = function (s) {\n      return s.replace(/\"/g, '\\\\\"')\n    }\n\n    if (typeof (appName) === 'function') {\n      callback = appName\n      appName = null\n    }\n\n    switch (process.platform) {\n      case 'darwin': {\n        opener = appName ? `open -a \"${escape(appName)}\"` : `open`\n        break\n      }\n      case 'win32': {\n        opener = appName ? `start \"\" ${escape(appName)}\"` : `start \"\"`\n        break\n      }\n      default: {\n        opener = appName ? escape(appName) : `xdg-open`\n        break\n      }\n    }\n\n    if (process.env.SUDO_USER) {\n      opener = 'sudo -u ' + process.env.SUDO_USER + ' ' + opener\n    }\n    return exec(`${opener} \"${escape(target)}\"`, callback)\n  }\n}\n"
  },
  {
    "path": "lib/API/pm2-plus/helpers.js",
    "content": "\nvar cst = require('../../../constants.js');\nvar Common = require('../../Common.js');\n\nconst chalk = require('ansis');\nconst forEach = require('async/forEach');\nconst open = require('../../tools/open.js');\nconst Modules = require('../Modules');\n\nfunction processesAreAlreadyMonitored(CLI, cb) {\n  CLI.Client.executeRemote('getMonitorData', {}, function(err, list) {\n    if (err) return cb(false);\n    var l = list.filter(l => l.pm2_env.km_link == true)\n    var l2 = list.filter(l => l.name == 'pm2-server-monit')\n\n    return cb(l.length > 0 && l2.length > 0 ? true : false)\n  })\n}\n\nmodule.exports = function(CLI) {\n  CLI.prototype.openDashboard = function() {\n    if (!this.gl_interact_infos) {\n      Common.printError(chalk.bold.white('Agent if offline, type `$ pm2 plus` to log in'));\n      return this.exitCli(cst.ERROR_EXIT);\n    }\n\n    var uri = `https://app.pm2.io/#/r/${this.gl_interact_infos.public_key}`\n    console.log(cst.PM2_IO_MSG + ` Opening ${uri}`)\n    open(uri);\n    setTimeout(_ => {\n      this.exitCli();\n    }, 200);\n  };\n\n  CLI.prototype.clearSetup = function (opts, cb) {\n    const modules = ['event-loop-inspector']\n    this.gl_is_km_linked = false\n\n    forEach(modules, (_module, next) => {\n      Modules.uninstall(this, _module, () => {\n        next()\n      });\n    }, (err) => {\n      this.reload('all', () => {\n        return cb()\n      })\n    })\n  }\n\n  /**\n   * Install required package and enable flags for current running processes\n   */\n  CLI.prototype.minimumSetup = function (opts, cb) {\n    var self = this;\n    this.gl_is_km_linked = true\n\n    function install(cb) {\n      var modules = []\n\n      if (opts.type === 'enterprise' || opts.type === 'plus') {\n        modules = ['pm2-logrotate', 'pm2-server-monit']\n        if (opts.type === 'enterprise') {\n          modules.push('deep-metrics')\n        }\n      }\n\n      forEach(modules, (_module, next) => {\n        Modules.install(self, _module, {}, () => {\n          next()\n        });\n      }, (err) => {\n        self.reload('all', () => {\n          return cb()\n        })\n      })\n    }\n\n    processesAreAlreadyMonitored(self, (already_monitored) => {\n      if (already_monitored) {\n        console.log(cst.PM2_IO_MSG + ` PM2 ${opts.type || ''} bundle already installed`);\n        return cb()\n      }\n\n      if (opts.installAll)\n        return install(cb)\n\n      // promptly.confirm(chalk.bold('Install all pm2 plus dependencies ? (y/n)'), (err, answer) => {\n      //   if (!err && answer === true)\n      return install(cb)\n      // self.reload('all', () => {\n      //     return cb()\n      //   })\n      // });\n    })\n  }\n\n}\n"
  },
  {
    "path": "lib/API/pm2-plus/link.js",
    "content": "\nvar cst         = require('../../../constants.js');\nvar Common      = require('../../Common.js');\nvar chalk       = require('ansis');\nvar fs          = require('fs');\nvar KMDaemon    = require('@pm2/agent/src/InteractorClient');\nvar pkg         = require('../../../package.json')\n\nmodule.exports = function(CLI) {\n\n  CLI.prototype.linkManagement = function(cmd, public_key, machine, opts, cb) {\n    var that = this;\n\n    // pm2 link stop || kill\n    if (cmd == 'stop' || cmd == 'kill') {\n      that.gl_is_km_linked = false\n      console.log(cst.PM2_IO_MSG + ' Stopping agent...');\n\n      return that.killAgent(function(err) {\n        if (err) {\n          Common.printError(err);\n          return process.exit(cst.ERROR_EXIT);\n        }\n        console.log(cst.PM2_IO_MSG + ' Stopped');\n\n        that.reload('all', () => {\n          return process.exit(cst.SUCCESS_EXIT);\n        })\n      });\n    }\n\n    // pm2 link info\n    if (cmd == 'info') {\n      console.log(cst.PM2_IO_MSG + ' Getting agent information...');\n      that.agentInfos(function(err, infos) {\n        if (err) {\n          console.error(cst.PM2_IO_MSG_ERR + ' ' + err.message);\n          return that.exitCli(cst.ERROR_EXIT);\n        }\n        console.log(infos);\n        return that.exitCli(cst.SUCCESS_EXIT);\n      });\n      return false;\n    }\n\n    // pm2 link delete\n    if (cmd == 'delete') {\n      that.gl_is_km_linked = false\n      console.log(cst.PM2_IO_MSG + ' Permanently disable agent...');\n      that.killAgent(function(err) {\n        try {\n          fs.unlinkSync(cst.INTERACTION_CONF);\n        } catch(e) {\n          console.log(cst.PM2_IO_MSG + ' No interaction config file found');\n          return process.exit(cst.SUCCESS_EXIT);\n        }\n        console.log(cst.PM2_IO_MSG + ' Agent interaction ended');\n        if (!cb)\n          return process.exit(cst.SUCCESS_EXIT);\n        return cb()\n      });\n      return false;\n    }\n\n    if (cmd && !public_key) {\n      console.error(cst.PM2_IO_MSG + ' Command [%s] unknown or missing public key', cmd);\n      return process.exit(cst.ERROR_EXIT);\n    }\n\n    // pm2 link xxx yyy\n    var infos;\n\n    if (!cmd) {\n      infos = null;\n    }\n    else\n      infos = {\n        public_key : public_key,\n        secret_key : cmd,\n        machine_name : machine,\n        info_node : opts.infoNode || null,\n        pm2_version: pkg.version\n      }\n\n    that.link(infos, cb)\n  };\n\n  CLI.prototype.link = function(infos, cb) {\n    var that = this;\n\n    process.env.WS_JSON_PATCH = true\n\n    KMDaemon.launchAndInteract(cst, infos, function(err, dt) {\n      if (err) {\n        Common.printError(cst.PM2_IO_MSG + ' Run `$ pm2 plus` to connect')\n        return that.exitCli(cst.ERROR_EXIT);\n      }\n      console.log(chalk.bold.green('[+] PM2+ activated!'))\n      if (!cb) {\n        return that.exitCli(cst.SUCCESS_EXIT);\n      }\n      return cb(null, dt)\n    });\n  };\n\n  CLI.prototype.agentInfos = function(cb) {\n    KMDaemon.getInteractInfo(this._conf, function(err, data) {\n      if (err)\n        return cb(Common.retErr(err));\n      return cb(null, data);\n    });\n  };\n\n  CLI.prototype.killAgent = function(cb) {\n    var that = this;\n    KMDaemon.killInteractorDaemon(that._conf, function(err) {\n      if (err)\n        return cb ? cb(Common.retErr(err)) : that.exitCli(cst.SUCCESS_EXIT);\n      return cb ? cb(null) : that.exitCli(cst.SUCCESS_EXIT);\n    });\n  };\n\n  CLI.prototype.unlink = function(cb) {\n    this.linkManagement('delete', cb);\n  };\n};\n"
  },
  {
    "path": "lib/API/pm2-plus/pres/motd",
    "content": "\n  ██████╗ ███╗   ███╗██████╗     ██╗    ██╗ ██████╗ \n  ██╔══██╗████╗ ████║╚════██╗    ██║   ██╔╝██╔═══██╗\n  ██████╔╝██╔████╔██║ █████╔╝    ██║  ██╔╝ ██║   ██║\n  ██╔═══╝ ██║╚██╔╝██║██╔═══╝     ██║ ██╔╝  ██║   ██║\n  ██║     ██║ ╚═╝ ██║███████╗    ██║██╔╝   ╚██████╔╝\n  ╚═╝     ╚═╝     ╚═╝╚══════╝    ╚═╝╚═╝     ╚═════╝ \n                                                  \n                  https://pm2.io/\n\n     Harden your Node.js Production Environment\n\n      - Real-time Monitoring Web Interface\n      - Pro Active Alerting System\n      - Production Profiling for Memory and CPU\n      - PM2 Runtime High Availability Fallback\n"
  },
  {
    "path": "lib/API/pm2-plus/pres/motd.update",
    "content": "\n                  -------------\n\n\n  ██████╗ ███╗   ███╗██████╗     ██╗    ██╗ ██████╗\n  ██╔══██╗████╗ ████║╚════██╗    ██║   ██╔╝██╔═══██╗\n  ██████╔╝██╔████╔██║ █████╔╝    ██║  ██╔╝ ██║   ██║\n  ██╔═══╝ ██║╚██╔╝██║██╔═══╝     ██║ ██╔╝  ██║   ██║\n  ██║     ██║ ╚═╝ ██║███████╗    ██║██╔╝   ╚██████╔╝\n  ╚═╝     ╚═╝     ╚═╝╚══════╝    ╚═╝╚═╝     ╚═════╝\n\n                  https://pm2.io/\n\n     Harden your Node.js Production Environment\n\n      - Real-time Monitoring Web Interface\n      - Pro Active Alerting System\n      - Production Profiling for Memory and CPU\n      - PM2 Runtime High Availability Fallback\n\n\n             Start using it by typing:\n\n                    $ pm2 plus\n\n                  -------------\n"
  },
  {
    "path": "lib/API/pm2-plus/pres/welcome",
    "content": "\n                -------------\n\n               PM2 Plus Edition\n\n\n       PM2 Plus is a monitoring dashboard\n        specialized for Node.js Apps.\n\n               Create an account:\n              $ pm2 plus register\n\n    Connect your local PM2 to the PM2 Plus servers:\n              $ pm2 plus connect\n\n    See our UI with your own realtime dashboard:\n              $ pm2 plus web\n\n          More details available there:\n              http://pm2.io/plus\n\n                -------------\n\n        Having complex or specific needs?\n  You can also checkout our enterprise offer at:\n            http://pm2.io/enterprise\n\n                -------------\n"
  },
  {
    "path": "lib/API/pm2-plus/process-selector.js",
    "content": "const fs  = require('fs');\nconst forEachLimit = require('async/forEachLimit');\n\nvar cst = require('../../../constants.js');\nvar Common = require('../../Common.js');\n\nmodule.exports = function(CLI) {\n  /**\n   * Monitor Selectively Processes (auto filter in interaction)\n   * @param String state 'monitor' or 'unmonitor'\n   * @param String target <pm_id|name|all>\n   * @param Function cb callback\n   */\n  CLI.prototype.monitorState = function(state, target, cb) {\n    var that = this;\n\n    if (!target) {\n      Common.printError(cst.PREFIX_MSG_ERR + 'Please specify an <app_name|pm_id>');\n      return cb ? cb(new Error('argument missing')) : that.exitCli(cst.ERROR_EXIT);\n    }\n\n    function monitor (pm_id, cb) {\n      // State can be monitor or unmonitor\n      that.Client.executeRemote(state, pm_id, cb);\n    }\n    if (target === 'all') {\n      that.Client.getAllProcessId(function (err, procs) {\n        if (err) {\n          Common.printError(err);\n          return cb ? cb(Common.retErr(err)) : that.exitCli(cst.ERROR_EXIT);\n        }\n        forEachLimit(procs, 1, monitor, function (err, res) {\n          return typeof cb === 'function' ? cb(err, res) : that.speedList();\n        });\n      });\n    } else if (!Number.isInteger(parseInt(target))) {\n      this.Client.getProcessIdByName(target, true, function (err, procs) {\n        if (err) {\n          Common.printError(err);\n          return cb ? cb(Common.retErr(err)) : that.exitCli(cst.ERROR_EXIT);\n        }\n        forEachLimit(procs, 1, monitor, function (err, res) {\n          return typeof cb === 'function' ? cb(err, res) : that.speedList();\n        });\n      });\n    } else {\n      monitor(parseInt(target), function (err, res) {\n        return typeof cb === 'function' ? cb(err, res) : that.speedList();\n      });\n    }\n  };\n}\n"
  },
  {
    "path": "lib/API/schema.json",
    "content": "{\n  \"script\": {\n    \"type\": \"string\",\n    \"require\": true,\n    \"alias\" : \"exec\",\n    \"docDescription\": \"Path of the script to launch, required field\"\n  },\n  \"name\": {\n    \"type\": \"string\",\n    \"docDefault\": \"Script filename without the extension (app for app.js)\",\n    \"docDescription\": \"Process name in the process list\"\n  },\n  \"name_prefix\": {\n    \"type\": \"string\"\n  },\n  \"filter_env\": {\n    \"type\": [\n      \"boolean\",\n      \"array\",\n      \"string\"\n    ],\n    \"docDefault\": false,\n    \"docDescription\": \"Enable filtering global environments\"\n  },\n  \"namespace\": {\n    \"type\": \"string\",\n    \"docDefault\": \"default\",\n    \"docDescription\": \"Process namespace\"\n  },\n  \"install_url\": {\n    \"type\": \"string\"\n  },\n  \"cwd\": {\n    \"type\": \"string\",\n    \"docDefault\": \"CWD of the current environment (from your shell)\",\n    \"docDescription\": \"Current working directory to start the process with\"\n  },\n  \"args\": {\n    \"type\": [\n      \"array\",\n      \"string\"\n    ],\n    \"docDescription\": \"Arguments to pass to the script\"\n  },\n  \"exec_interpreter\": {\n    \"type\": \"string\",\n    \"alias\": \"interpreter\",\n    \"docDefault\": \"node\",\n    \"docDescription\": \"Interpreter absolute path\"\n  },\n  \"node_args\": {\n    \"type\": [\n      \"array\",\n      \"string\"\n    ],\n    \"alias\": [\"interpreterArgs\", \"interpreter_args\"],\n    \"docDescription\": \"Arguments to pass to the interpreter\"\n  },\n  \"out_file\": {\n    \"type\": \"string\",\n    \"alias\": [\"out\", \"output\", \"out_log\"],\n    \"docDefault\": \"~/.pm2/logs/<app_name>-out.log\",\n    \"docDescription\": \"File path for stdout (each line is appended to this file)\"\n  },\n  \"error_file\": {\n    \"type\": \"string\",\n    \"alias\": [\"error\", \"err\", \"err_file\", \"err_log\"],\n    \"docDefault\": \"~/.pm2/logs/<app_name>-error.err\",\n    \"docDescription\": \"File path for stderr (each line is appended to this file)\"\n  },\n  \"log_file\": {\n    \"type\": [\n      \"boolean\",\n      \"string\"\n    ],\n    \"alias\": \"log\",\n    \"docDefault\": \"/dev/null\",\n    \"docDescription\": \"File path for combined stdout and stderr (each line is appended to this file)\"\n  },\n  \"disable_logs\": {\n    \"type\": \"boolean\",\n    \"docDefault\": false,\n    \"docDescription\": \"Disable all logs storage\"\n  },\n  \"log_type\": {\n    \"type\": \"string\",\n    \"docDescription\": \"Define a specific log output type, possible value: json\"\n  },\n  \"log_date_format\": {\n    \"type\": \"string\",\n    \"docDescription\": \"Format for log timestamps in day.js format (eg YYYY-MM-DD HH:mm Z)\"\n  },\n  \"time\": {\n    \"type\": \"boolean\"\n  },\n  \"env\": {\n    \"type\": [\n      \"object\",\n      \"string\"\n    ],\n    \"docDescription\": \"Specify environment variables to be injected\"\n  },\n  \"^env_\\\\S*$\": {\n    \"type\": [\n      \"object\",\n      \"string\"\n    ],\n    \"docDescription\": \"Specify environment variables to be injected when using --env <env_name>\"\n  },\n  \"max_memory_restart\": {\n    \"type\": [\n      \"string\",\n      \"number\"\n    ],\n    \"regex\": \"^\\\\d+(G|M|K)?$\",\n    \"ext_type\": \"sbyte\",\n    \"desc\": \"it should be a NUMBER - byte, \\\"[NUMBER]G\\\"(Gigabyte), \\\"[NUMBER]M\\\"(Megabyte) or \\\"[NUMBER]K\\\"(Kilobyte)\",\n    \"docDescription\": \"Restart the app if an amount of memory is exceeded (format: /[0-9](K&#124;M&#124;G)?/ K for KB, 'M' for MB, 'G' for GB, default to B)\"\n  },\n  \"pid_file\": {\n    \"type\": \"string\",\n    \"alias\": \"pid\",\n    \"docDefault\": \"~/.pm2/pids/app_name-id.pid\",\n    \"docDescription\": \"File path where the pid of the started process is written by pm2\"\n  },\n  \"restart_delay\": {\n    \"type\" : \"number\",\n    \"docDefault\": 0,\n    \"docDescription\": \"Time in ms to wait before restarting a crashing app\"\n  },\n  \"exp_backoff_restart_delay\": {\n    \"type\": \"number\",\n    \"docDefault\": 0,\n    \"docDescription\": \"Restart Time in ms to wait before restarting a crashing app\"\n  },\n  \"source_map_support\": {\n    \"type\": \"boolean\",\n    \"docDefault\": true,\n    \"docDescription\": \"Enable or disable the source map support\"\n  },\n  \"disable_source_map_support\": {\n    \"type\": \"boolean\",\n    \"docDefault\": false,\n    \"docDescription\": \"Enable or disable the source map support\"\n  },\n  \"wait_ready\": {\n    \"type\": \"boolean\",\n    \"docDefault\": false,\n    \"docDescription\": \"Make the process wait for a process.send('ready')\"\n  },\n  \"instances\": {\n    \"type\": \"number\",\n    \"docDefault\": 1,\n    \"docDescription\": \"Number of instances to be started in cluster mode\"\n  },\n  \"kill_timeout\": {\n    \"type\": \"number\",\n    \"docDefault\": 1600,\n    \"docDescription\": \"Time in ms before sending the final SIGKILL signal after SIGINT\"\n  },\n  \"shutdown_with_message\": {\n    \"type\": \"boolean\",\n    \"docDefault\": false,\n    \"docDescription\": \"Shutdown an application with process.send('shutdown') instead of process.kill(pid, SIGINT)\"\n  },\n  \"listen_timeout\": {\n    \"type\": \"number\",\n    \"docDescription\": \"Time in ms before forcing a reload if app is still not listening/has still note sent ready\"\n  },\n  \"cron_restart\": {\n    \"type\": [\n      \"string\",\n      \"number\"\n    ],\n    \"alias\": \"cron\",\n    \"docDescription\": \"A cron pattern to restart your app\"\n  },\n  \"merge_logs\": {\n    \"type\": \"boolean\",\n    \"alias\" : \"combine_logs\",\n    \"docDefault\": false,\n    \"docDescription\": \"In cluster mode, merge each type of logs into a single file (instead of having one for each cluster)\"\n  },\n  \"vizion\": {\n    \"type\": \"boolean\",\n    \"default\" : true,\n    \"docDefault\" : \"True\",\n    \"docDescription\": \"Enable or disable the versioning metadatas (vizion library)\"\n  },\n  \"autostart\": {\n    \"type\": \"boolean\",\n    \"default\": true,\n    \"docDefault\": \"True\",\n    \"docDescription\": \"Enable or disable auto start when adding process\"\n  },\n  \"autorestart\": {\n    \"type\": \"boolean\",\n    \"default\": true,\n    \"docDefault\": \"True\",\n    \"docDescription\": \"Enable or disable auto restart after process failure\"\n  },\n  \"stop_exit_codes\": {\n    \"type\":  [\n      \"array\",\n      \"number\"\n    ],\n    \"docDescription\": \"List of exit codes that should allow the process to stop (skip autorestart).\"\n  },\n  \"watch_delay\": {\n    \"type\": \"number\",\n    \"docDefault\": \"True\",\n    \"docDescription\": \"Restart delay on file change detected\"\n  },\n  \"watch\": {\n    \"type\": [\n      \"boolean\",\n      \"array\",\n      \"string\"\n    ],\n    \"docDefault\": false,\n    \"docDescription\": \"Enable or disable the watch mode\"\n  },\n  \"ignore_watch\": {\n    \"type\": [\n      \"array\",\n      \"string\"\n    ],\n    \"docDescription\": \"List of paths to ignore (regex)\"\n  },\n  \"watch_options\": {\n    \"type\": \"object\",\n    \"docDescription\": \"Object that will be used as an options with chokidar (refer to chokidar documentation)\"\n  },\n  \"min_uptime\": {\n    \"type\": [\n      \"number\",\n      \"string\"\n    ],\n    \"regex\": \"^\\\\d+(h|m|s)?$\",\n    \"desc\": \"it should be a NUMBER - milliseconds, \\\"[NUMBER]h\\\"(hours), \\\"[NUMBER]m\\\"(minutes) or \\\"[NUMBER]s\\\"(seconds)\",\n    \"min\": 100,\n    \"ext_type\": \"stime\",\n    \"docDefault\": 1000,\n    \"docDescription\": \"Minimum uptime of the app to be considered started (format is /[0-9]+(h&#124;m&#124;s)?/, for hours, minutes, seconds, docDefault to ms)\"\n  },\n  \"max_restarts\": {\n    \"type\": \"number\",\n    \"min\": 0,\n    \"docDefault\": 16,\n    \"docDescription\": \"Number of times a script is restarted when it exits in less than min_uptime\"\n  },\n  \"execute_command\": {\n    \"type\": \"boolean\"\n  },\n  \"exec_mode\": {\n    \"type\": \"string\",\n    \"regex\": \"^(cluster|fork)(_mode)?$\",\n    \"desc\": \"it should be \\\"cluster\\\"(\\\"cluster_mode\\\") or \\\"fork\\\"(\\\"fork_mode\\\") only\",\n    \"docDefault\": \"fork\",\n    \"docDescription\": \"Set the execution mode, possible values: fork&#124;cluster\"\n  },\n  \"force\": {\n    \"type\": \"boolean\",\n    \"docDefault\": false,\n    \"docDescription\": \"Start a script even if it is already running (only the script path is considered)\"\n  },\n  \"append_env_to_name\": {\n    \"type\": \"boolean\",\n    \"docDefault\": false,\n    \"docDescription\": \"Append the environment name to the app name\"\n  },\n  \"post_update\": {\n    \"type\": \"array\",\n    \"docDescription\": \"List of commands executed after a pull/upgrade operation performed from Keymetrics dashboard\"\n  },\n  \"trace\": {\n    \"type\": [\n      \"boolean\"\n    ],\n    \"docDefault\": false,\n    \"docDescription\": \"Enable or disable the transaction tracing\"\n  },\n  \"disable_trace\": {\n    \"type\": [\n      \"boolean\"\n    ],\n    \"docDefault\": true,\n    \"docDescription\": \"Enable or disable the transaction tracing\"\n  },\n  \"v8\": {\n    \"type\": [\n      \"boolean\"\n    ]\n  },\n  \"event_loop_inspector\": {\n    \"type\": [\n      \"boolean\"\n    ]\n  },\n  \"deep_monitoring\": {\n    \"type\": [\n      \"boolean\"\n    ]\n  },\n  \"increment_var\": {\n    \"type\": \"string\",\n    \"docDescription\": \"Specify the name of an environment variable to inject which increments for each cluster\"\n  },\n  \"instance_var\": {\n    \"type\": \"string\",\n    \"default\": \"NODE_APP_INSTANCE\",\n    \"docDefault\": \"NODE_APP_INSTANCE\",\n    \"docDescription\": \"Rename the NODE_APP_INSTANCE environment variable\"\n  },\n  \"pmx\": {\n    \"type\": [\"boolean\", \"string\"],\n    \"default\": true,\n    \"docDefault\": \"True\",\n    \"docDescription\": \"Enable or disable pmx wrapping\"\n  },\n  \"automation\": {\n    \"type\": \"boolean\",\n    \"default\": true,\n    \"docDefault\": \"True\",\n    \"docDescription\": \"Enable or disable pmx wrapping\"\n  },\n  \"treekill\": {\n    \"type\": \"boolean\",\n    \"default\": true,\n    \"docDefault\": \"True\",\n    \"docDescription\": \"Only kill the main process, not detached children\"\n  },\n  \"port\": {\n    \"type\": \"number\",\n    \"docDescription\": \"Shortcut to inject a PORT environment variable\"\n  },\n  \"username\" : {\n    \"type\": \"string\",\n    \"docDescription\": \"Current user that started the process\"\n  },\n  \"uid\": {\n    \"type\" : [\n      \"number\",\n      \"string\"\n    ],\n    \"alias\": \"user\",\n    \"docDefault\": \"Current user uid\",\n    \"docDescription\": \"Set user id\"\n  },\n  \"gid\": {\n    \"type\" : [\n      \"number\",\n      \"string\"\n    ],\n    \"docDefault\": \"Current user gid\",\n    \"docDescription\": \"Set group id\"\n  },\n  \"windowsHide\": {\n    \"type\": \"boolean\",\n    \"docDefault\": \"True\",\n    \"docDescription\": \"Enable or disable the Windows popup when starting an app\",\n    \"default\": true\n  },\n  \"kill_retry_time\": {\n    \"type\": \"number\",\n    \"default\" : 100\n  },\n  \"write\": {\n    \"type\": \"boolean\"\n  },\n  \"io\": {\n    \"type\": \"object\",\n    \"docDescription\": \"Specify apm values and configuration\"\n  }\n}\n"
  },
  {
    "path": "lib/API.js",
    "content": "/**\n * Copyright 2013-2022 the PM2 project authors. All rights reserved.\n * Use of this source code is governed by a license that\n * can be found in the LICENSE file.\n */\n'use strict';\n\nconst commander   = require('commander');\nconst fs          = require('fs');\nconst path        = require('path');\nconst eachLimit   = require('async/eachLimit');\nconst series      = require('async/series');\nconst debug       = require('debug')('pm2:cli');\nconst util        = require('util');\nconst chalk       = require('ansis');\nconst fclone      = require('fclone');\n\nvar DockerMgmt  = require('./API/ExtraMgmt/Docker.js')\nvar conf        = require('../constants.js');\nvar Client      = require('./Client');\nvar Common      = require('./Common');\nvar KMDaemon    = require('@pm2/agent/src/InteractorClient');\nvar Config      = require('./tools/Config');\nvar Modularizer = require('./API/Modules/Modularizer.js');\nvar path_structure = require('../paths.js');\nvar UX          = require('./API/UX');\nvar pkg         = require('../package.json');\nvar hf = require('./API/Modules/flagExt.js');\nvar Configuration = require('./Configuration.js');\nconst sexec = require('./tools/sexec.js')\n\nvar IMMUTABLE_MSG = chalk.bold.blue('Use --update-env to update environment variables');\n\n/**\n * Main Function to be imported\n * can be aliased to PM2\n *\n * To use it when PM2 is installed as a module:\n *\n * var PM2 = require('pm2');\n *\n * var pm2 = PM2(<opts>);\n *\n *\n * @param {Object}  opts\n * @param {String}  [opts.cwd=<current>]         override pm2 cwd for starting scripts\n * @param {String}  [opts.pm2_home=[<paths.js>]] pm2 directory for log, pids, socket files\n * @param {Boolean} [opts.independent=false]     unique PM2 instance (random pm2_home)\n * @param {Boolean} [opts.daemon_mode=true]      should be called in the same process or not\n * @param {String}  [opts.public_key=null]       pm2 plus bucket public key\n * @param {String}  [opts.secret_key=null]       pm2 plus bucket secret key\n * @param {String}  [opts.machine_name=null]     pm2 plus instance name\n */\nclass API {\n\n  constructor (opts) {\n    if (!opts) opts = {};\n    var that = this;\n\n    this.daemon_mode = typeof(opts.daemon_mode) == 'undefined' ? true : opts.daemon_mode;\n    this.pm2_home = conf.PM2_ROOT_PATH;\n    this.public_key = conf.PUBLIC_KEY || opts.public_key || null;\n    this.secret_key = conf.SECRET_KEY || opts.secret_key || null;\n    this.machine_name = conf.MACHINE_NAME || opts.machine_name || null\n\n    /**\n     * CWD resolution\n     */\n    this.cwd = process.cwd();\n    if (opts.cwd) {\n      this.cwd = path.resolve(opts.cwd);\n    }\n\n    /**\n     * PM2 HOME resolution\n     */\n    if (opts.pm2_home && opts.independent == true)\n      throw new Error('You cannot set a pm2_home and independent instance in same time');\n\n    if (opts.pm2_home) {\n      // Override default conf file\n      this.pm2_home = opts.pm2_home;\n      conf = Object.assign(conf, path_structure(this.pm2_home));\n    }\n    else if (opts.independent == true && conf.IS_WINDOWS === false) {\n      // Create an unique pm2 instance\n      const crypto = require('crypto');\n      var random_file = crypto.randomBytes(8).toString('hex');\n      this.pm2_home = path.join('/tmp', random_file);\n\n      // If we dont explicitly tell to have a daemon\n      // It will go as in proc\n      if (typeof(opts.daemon_mode) == 'undefined')\n        this.daemon_mode = false;\n      conf = Object.assign(conf, path_structure(this.pm2_home));\n    }\n\n    this._conf = conf;\n\n    if (conf.IS_WINDOWS) {\n      // Weird fix, may need to be dropped\n      // @todo windows connoisseur double check\n      if (process.stdout._handle && process.stdout._handle.setBlocking)\n        process.stdout._handle.setBlocking(true);\n    }\n\n    this.Client = new Client({\n      pm2_home: that.pm2_home,\n      conf: this._conf,\n      secret_key: this.secret_key,\n      public_key: this.public_key,\n      daemon_mode: this.daemon_mode,\n      machine_name: this.machine_name\n    });\n\n    this.pm2_configuration = Configuration.getSync('pm2') || {}\n\n    this.gl_interact_infos = null;\n    this.gl_is_km_linked = false;\n\n    try {\n      var pid = fs.readFileSync(conf.INTERACTOR_PID_PATH);\n      pid = parseInt(pid.toString().trim());\n      process.kill(pid, 0);\n      that.gl_is_km_linked = true;\n    } catch (e) {\n      that.gl_is_km_linked = false;\n    }\n\n    // For testing purposes\n    if (this.secret_key && process.env.NODE_ENV == 'local_test')\n      that.gl_is_km_linked = true;\n\n    KMDaemon.ping(this._conf, function(err, result) {\n      if (!err && result === true) {\n        fs.readFile(conf.INTERACTION_CONF, (err, _conf) => {\n          if (!err) {\n            try {\n              that.gl_interact_infos = JSON.parse(_conf.toString())\n            } catch(e) {\n              var json5 = require('./tools/json5.js')\n              try {\n                that.gl_interact_infos = json5.parse(_conf.toString())\n              } catch(e) {\n                console.error(e)\n                that.gl_interact_infos = null\n              }\n            }\n          }\n        })\n      }\n    })\n\n    this.gl_retry = 0;\n  }\n\n  /**\n   * Connect to PM2\n   * Calling this command is now optional\n   *\n   * @param {Function} cb callback once pm2 is ready for commands\n   */\n  connect (noDaemon, cb) {\n    var that = this;\n    this.start_timer = new Date();\n\n    if (typeof(cb) == 'undefined') {\n      cb = noDaemon;\n      noDaemon = false;\n    } else if (noDaemon === true) {\n      // Backward compatibility with PM2 1.x\n      this.Client.daemon_mode = false;\n      this.daemon_mode = false;\n    }\n\n    this.Client.start(function(err, meta) {\n      if (err)\n        return cb(err);\n\n      if (meta.new_pm2_instance == false && that.daemon_mode === true)\n        return cb(err, meta);\n\n      that.launchSysMonitoring(() => {})\n      // If new pm2 instance has been popped\n      // Launch all modules\n      that.launchAll(that, function(err_mod) {\n        return cb(err, meta);\n      });\n    });\n  }\n\n  /**\n   * Usefull when custom PM2 created with independent flag set to true\n   * This will cleanup the newly created instance\n   * by removing folder, killing PM2 and so on\n   *\n   * @param {Function} cb callback once cleanup is successfull\n   */\n  destroy (cb) {\n    var that = this;\n\n    debug('Killing and deleting current deamon');\n\n    this.killDaemon(function() {\n      var cmd = 'rm -rf ' + that.pm2_home;\n      var test_path = path.join(that.pm2_home, 'module_conf.json');\n      var test_path_2 = path.join(that.pm2_home, 'pm2.pid');\n\n      if (that.pm2_home.indexOf('.pm2') > -1)\n        return cb(new Error('Destroy is not a allowed method on .pm2'));\n\n      fs.access(test_path, fs.constants.R_OK, function(err) {\n        if (err) return cb(err);\n        debug('Deleting temporary folder %s', that.pm2_home);\n        sexec(cmd, cb);\n      });\n    });\n  }\n\n  /**\n   * Disconnect from PM2 instance\n   * This will allow your software to exit by itself\n   *\n   * @param {Function} [cb] optional callback once connection closed\n   */\n  disconnect (cb) {\n    var that = this;\n\n    if (!cb) cb = function() {};\n\n    this.Client.close(function(err, data) {\n      debug('The session lasted %ds', (new Date() - that.start_timer) / 1000);\n      return cb(err, data);\n    });\n  };\n\n  /**\n   * Alias on disconnect\n   * @param cb\n   */\n  close (cb) {\n    this.disconnect(cb);\n  }\n\n  /**\n   * Launch modules\n   *\n   * @param {Function} cb callback once pm2 has launched modules\n   */\n  launchModules (cb) {\n    this.launchAll(this, cb);\n  }\n\n  /**\n   * Enable bus allowing to retrieve various process event\n   * like logs, restarts, reloads\n   *\n   * @param {Function} cb callback called with 1st param err and 2nb param the bus\n   */\n  launchBus (cb) {\n    this.Client.launchBus(cb);\n  }\n\n  /**\n   * Exit methods for API\n   * @param {Integer} code exit code for terminal\n   */\n  exitCli (code) {\n    var that = this;\n\n    // Do nothing if PM2 called programmatically (also in speedlist)\n    if (conf.PM2_PROGRAMMATIC && process.env.PM2_USAGE != 'CLI') return false;\n\n    KMDaemon.disconnectRPC(function() {\n      that.Client.close(function() {\n        code = code || 0;\n        // Safe exits process after all streams are drained.\n        // file descriptor flag.\n        var fds = 0;\n        // exits process when stdout (1) and sdterr(2) are both drained.\n        function tryToExit() {\n          if ((fds & 1) && (fds & 2)) {\n            debug('This command took %ds to execute', (new Date() - that.start_timer) / 1000);\n            process.exit(code);\n          }\n        }\n\n        [process.stdout, process.stderr].forEach(function(std) {\n          var fd = std.fd;\n          if (!std.bufferSize) {\n            // bufferSize equals 0 means current stream is drained.\n            fds = fds | fd;\n          } else {\n            // Appends nothing to the std queue, but will trigger `tryToExit` event on `drain`.\n            std.write && std.write('', function() {\n              fds = fds | fd;\n              tryToExit();\n            });\n          }\n          // Does not write anything more.\n          delete std.write;\n        });\n        tryToExit();\n      });\n    });\n  }\n\n////////////////////////////\n// Application management //\n////////////////////////////\n\n  /**\n   * Start a file or json with configuration\n   * @param {Object||String} cmd script to start or json\n   * @param {Function} cb called when application has been started\n   */\n  start (cmd, opts, cb) {\n    if (typeof(opts) == \"function\") {\n      cb = opts;\n      opts = {};\n    }\n    if (!opts) opts = {};\n\n    var that = this;\n    if (Array.isArray(opts.watch) && opts.watch.length === 0)\n      opts.watch = (opts.rawArgs ? !!~opts.rawArgs.indexOf('--watch') : !!~process.argv.indexOf('--watch')) || false;\n\n    if (Common.isConfigFile(cmd) || (typeof(cmd) === 'object')) {\n      that._startJson(cmd, opts, 'restartProcessId', (err, procs) => {\n        return cb ? cb(err, procs) : this.speedList()\n      })\n    }\n    else {\n      that._startScript(cmd, opts, (err, procs) => {\n        return cb ? cb(err, procs) : this.speedList(0)\n      })\n    }\n  }\n\n  /**\n   * Reset process counters\n   *\n   * @method resetMetaProcess\n   */\n  reset (process_name, cb) {\n    var that = this;\n\n    function processIds(ids, cb) {\n      eachLimit(ids, conf.CONCURRENT_ACTIONS, function(id, next) {\n        that.Client.executeRemote('resetMetaProcessId', id, function(err, res) {\n          if (err) console.error(err);\n          Common.printOut(conf.PREFIX_MSG + 'Resetting meta for process id %d', id);\n          return next();\n        });\n      }, function(err) {\n        if (err) return cb(Common.retErr(err));\n        return cb ? cb(null, {success:true}) : that.speedList();\n      });\n    }\n\n    if (process_name == 'all') {\n      that.Client.getAllProcessId(function(err, ids) {\n        if (err) {\n          Common.printError(err);\n          return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);\n        }\n        return processIds(ids, cb);\n      });\n    }\n    else if (isNaN(process_name)) {\n      that.Client.getProcessIdByName(process_name, function(err, ids) {\n        if (err) {\n          Common.printError(err);\n          return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);\n        }\n        if (ids.length === 0) {\n          Common.printError('Unknown process name');\n          return cb ? cb(new Error('Unknown process name')) : that.exitCli(conf.ERROR_EXIT);\n        }\n        return processIds(ids, cb);\n      });\n    } else {\n      processIds([process_name], cb);\n    }\n  }\n\n  /**\n   * Update daemonized PM2 Daemon\n   *\n   * @param {Function} cb callback when pm2 has been upgraded\n   */\n  update (cb) {\n    var that = this;\n\n    Common.printOut('Be sure to have the latest version by doing `npm install pm2@latest -g` before doing this procedure.');\n\n    // Dump PM2 processes\n    that.Client.executeRemote('notifyKillPM2', {}, function() {});\n\n    that.getVersion(function(err, new_version) {\n      // If not linked to PM2 plus, and update PM2 to latest, display motd.update\n      if (!that.gl_is_km_linked && !err && (pkg.version != new_version)) {\n        var dt = fs.readFileSync(path.join(__dirname, that._conf.PM2_UPDATE));\n        console.log(dt.toString());\n      }\n\n      that.dump(function(err) {\n        that.killDaemon(function() {\n          that.Client.launchDaemon({interactor:false}, function(err, child) {\n            that.Client.launchRPC(function() {\n              that.resurrect(function() {\n                Common.printOut(chalk.blue.bold('>>>>>>>>>> PM2 updated'));\n                that.launchSysMonitoring(() => {})\n                that.launchAll(that, function() {\n                  KMDaemon.launchAndInteract(that._conf, {\n                    pm2_version: pkg.version\n                  }, function(err, data, interactor_proc) {\n                  })\n                  setTimeout(() => {\n                    return cb ? cb(null, {success:true}) : that.speedList();\n                  }, 250)\n                });\n              });\n            });\n          });\n        });\n      });\n    });\n\n    return false;\n  }\n\n  /**\n   * Reload an application\n   *\n   * @param {String} process_name Application Name or All\n   * @param {Object} opts         Options\n   * @param {Function} cb         Callback\n   */\n  reload (process_name, opts, cb) {\n    var that = this;\n\n    if (typeof(opts) == \"function\") {\n      cb = opts;\n      opts = {};\n    }\n\n    var delay = Common.lockReload();\n    if (delay > 0 && opts.force != true) {\n      Common.printError(conf.PREFIX_MSG_ERR + 'Reload already in progress, please try again in ' + Math.floor((conf.RELOAD_LOCK_TIMEOUT - delay) / 1000) + ' seconds or use --force');\n      return cb ? cb(new Error('Reload in progress')) : that.exitCli(conf.ERROR_EXIT);\n    }\n\n    if (Common.isConfigFile(process_name))\n      that._startJson(process_name, opts, 'reloadProcessId', function(err, apps) {\n        Common.unlockReload();\n        if (err)\n          return cb ? cb(err) : that.exitCli(conf.ERROR_EXIT);\n        return cb ? cb(null, apps) : that.exitCli(conf.SUCCESS_EXIT);\n      });\n    else {\n      if (opts && opts.env) {\n        var err = 'Using --env [env] without passing the ecosystem.config.js does not work'\n        Common.err(err);\n        Common.unlockReload();\n        return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);\n      }\n\n      if (opts && !opts.updateEnv)\n        Common.printOut(IMMUTABLE_MSG);\n\n      that._operate('reloadProcessId', process_name, opts, function(err, apps) {\n        Common.unlockReload();\n\n        if (err)\n          return cb ? cb(err) : that.exitCli(conf.ERROR_EXIT);\n        return cb ? cb(null, apps) : that.exitCli(conf.SUCCESS_EXIT);\n      });\n    }\n  }\n\n  /**\n   * Restart process\n   *\n   * @param {String} cmd   Application Name / Process id / JSON application file / 'all'\n   * @param {Object} opts  Extra options to be updated\n   * @param {Function} cb  Callback\n   */\n  restart (cmd, opts, cb) {\n    if (typeof(opts) == \"function\") {\n      cb = opts;\n      opts = {};\n    }\n    var that = this;\n\n    if (typeof(cmd) === 'number')\n      cmd = cmd.toString();\n\n    if (cmd == \"-\") {\n      // Restart from PIPED JSON\n      process.stdin.resume();\n      process.stdin.setEncoding('utf8');\n      process.stdin.on('data', function (param) {\n        process.stdin.pause();\n        that.actionFromJson('restartProcessId', param, opts, 'pipe', cb);\n      });\n    }\n    else if (Common.isConfigFile(cmd) || typeof(cmd) === 'object')\n      that._startJson(cmd, opts, 'restartProcessId', cb);\n    else {\n      if (opts && opts.env) {\n        var err = 'Using --env [env] without passing the ecosystem.config.js does not work'\n        Common.err(err);\n        return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);\n      }\n      if (opts && !opts.updateEnv)\n        Common.printOut(IMMUTABLE_MSG);\n      that._operate('restartProcessId', cmd, opts, cb);\n    }\n  }\n\n  /**\n   * Delete process\n   *\n   * @param {String} process_name Application Name / Process id / Application file / 'all'\n   * @param {Function} cb Callback\n   */\n  delete (process_name, jsonVia, cb) {\n    var that = this;\n\n    if (typeof(jsonVia) === \"function\") {\n      cb = jsonVia;\n      jsonVia = null;\n    }\n\n    if (typeof(process_name) === \"number\") {\n      process_name = process_name.toString();\n    }\n\n    if (jsonVia == 'pipe')\n      return that.actionFromJson('deleteProcessId', process_name, commander, 'pipe', (err, procs) => {\n        return cb ? cb(err, procs) : this.speedList()\n      });\n    if (Common.isConfigFile(process_name))\n      return that.actionFromJson('deleteProcessId', process_name, commander, 'file', (err, procs) => {\n        return cb ? cb(err, procs) : this.speedList()\n      });\n    else {\n      that._operate('deleteProcessId', process_name, (err, procs) => {\n        return cb ? cb(err, procs) : this.speedList()\n      });\n    }\n  }\n\n  /**\n   * Stop process\n   *\n   * @param {String} process_name Application Name / Process id / Application file / 'all'\n   * @param {Function} cb Callback\n   */\n  stop (process_name, cb) {\n    var that = this;\n\n    if (typeof(process_name) === 'number')\n      process_name = process_name.toString();\n\n    if (process_name == \"-\") {\n      process.stdin.resume();\n      process.stdin.setEncoding('utf8');\n      process.stdin.on('data', function (param) {\n        process.stdin.pause();\n        that.actionFromJson('stopProcessId', param, commander, 'pipe', (err, procs) => {\n          return cb ? cb(err, procs) : this.speedList()\n        })\n      });\n    }\n    else if (Common.isConfigFile(process_name))\n      that.actionFromJson('stopProcessId', process_name, commander, 'file', (err, procs) => {\n        return cb ? cb(err, procs) : this.speedList()\n      });\n    else\n      that._operate('stopProcessId', process_name, (err, procs) => {\n        return cb ? cb(err, procs) : this.speedList()\n      });\n  }\n\n  /**\n   * Get list of all processes managed\n   *\n   * @param {Function} cb Callback\n   */\n  list (opts, cb) {\n    var that = this;\n\n    if (typeof(opts) == 'function') {\n      cb = opts;\n      opts = null;\n    }\n\n    that.Client.executeRemote('getMonitorData', {}, function(err, list) {\n      if (err) {\n        Common.printError(err);\n        return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);\n      }\n\n      if (opts && opts.rawArgs && opts.rawArgs.indexOf('--watch') > -1) {\n        var dayjs = require('dayjs');\n        function show() {\n          process.stdout.write('\\x1b[2J');\n          process.stdout.write('\\x1b[0f');\n          console.log('Last refresh: ', dayjs().format());\n          that.Client.executeRemote('getMonitorData', {}, function(err, list) {\n            UX.list(list, null);\n          });\n        }\n\n        show();\n        setInterval(show, 900);\n        return false;\n      }\n\n      return cb ? cb(null, list) : that.speedList(null);\n    });\n  }\n\n  /**\n   * Kill Daemon\n   *\n   * @param {Function} cb Callback\n   */\n  killDaemon (cb) {\n    process.env.PM2_STATUS = 'stopping'\n\n    var that = this;\n\n    that.Client.executeRemote('notifyKillPM2', {}, function() {});\n\n    that._operate('deleteProcessId', 'all', function(err, list) {\n      Common.printOut(conf.PREFIX_MSG + '[v] All Applications Stopped');\n      process.env.PM2_SILENT = 'false';\n\n      that.killAgent(function(err, data) {\n        if (!err) {\n          Common.printOut(conf.PREFIX_MSG + '[v] Agent Stopped');\n        }\n\n        that.Client.killDaemon(function(err, res) {\n          if (err) Common.printError(err);\n          Common.printOut(conf.PREFIX_MSG + '[v] PM2 Daemon Stopped');\n          return cb ? cb(err, res) : that.exitCli(conf.SUCCESS_EXIT);\n        });\n\n      });\n    })\n  }\n\n  kill (cb) {\n    this.killDaemon(cb);\n  }\n\n  /////////////////////\n  // Private methods //\n  /////////////////////\n\n  /**\n   * Method to START / RESTART a script\n   *\n   * @private\n   * @param {string} script script name (will be resolved according to location)\n   */\n  _startScript (script, opts, cb) {\n    if (typeof opts == \"function\") {\n      cb = opts;\n      opts = {};\n    }\n    var that = this;\n\n    /**\n     * Commander.js tricks\n     */\n    var app_conf = Config.filterOptions(opts);\n    var appConf = {};\n\n    if (typeof app_conf.name == 'function')\n      delete app_conf.name;\n\n    delete app_conf.args;\n\n    // Retrieve arguments via -- <args>\n    var argsIndex;\n\n    if (opts.rawArgs && (argsIndex = opts.rawArgs.indexOf('--')) >= 0)\n      app_conf.args = opts.rawArgs.slice(argsIndex + 1);\n    else if (opts.scriptArgs)\n      app_conf.args = opts.scriptArgs;\n\n    app_conf.script = script;\n    if(!app_conf.namespace)\n      app_conf.namespace = 'default';\n\n    if ((appConf = Common.verifyConfs(app_conf)) instanceof Error) {\n      Common.err(appConf)\n      return cb ? cb(Common.retErr(appConf)) : that.exitCli(conf.ERROR_EXIT);\n    }\n\n    app_conf = appConf[0];\n\n    if (opts.watchDelay) {\n      if (typeof opts.watchDelay === \"string\" && opts.watchDelay.indexOf(\"ms\") !== -1)\n        app_conf.watch_delay = parseInt(opts.watchDelay);\n      else {\n        app_conf.watch_delay = parseFloat(opts.watchDelay) * 1000;\n      }\n    }\n\n    var mas = [];\n    if(typeof opts.ext != 'undefined')\n      hf.make_available_extension(opts, mas); // for -e flag\n    mas.length > 0 ? app_conf.ignore_watch = mas : 0;\n\n    /**\n     * If -w option, write configuration to configuration.json file\n     */\n    if (app_conf.write) {\n      var dst_path = path.join(process.env.PWD || process.cwd(), app_conf.name + '-pm2.json');\n      Common.printOut(conf.PREFIX_MSG + 'Writing configuration to', chalk.blue(dst_path));\n      // pretty JSON\n      try {\n        fs.writeFileSync(dst_path, JSON.stringify(app_conf, null, 2));\n      } catch (e) {\n        console.error(e.stack || e);\n      }\n    }\n\n    series([\n      restartExistingProcessName,\n      restartExistingNameSpace,\n      restartExistingProcessId,\n      restartExistingProcessPathOrStartNew\n    ], function(err, data) {\n      if (err instanceof Error)\n        return cb ? cb(err) : that.exitCli(conf.ERROR_EXIT);\n\n      var ret = {};\n\n      data.forEach(function(_dt) {\n        if (_dt !== undefined)\n          ret = _dt;\n      });\n\n      return cb ? cb(null, ret) : that.speedList();\n    });\n\n    /**\n     * If start <app_name> start/restart application\n     */\n    function restartExistingProcessName(cb) {\n      if (!isNaN(script) ||\n        (typeof script === 'string' && script.indexOf('/') != -1) ||\n        (typeof script === 'string' && path.extname(script) !== ''))\n        return cb(null);\n\n        that.Client.getProcessIdByName(script, function(err, ids) {\n          if (err && cb) return cb(err);\n          if (ids.length > 0) {\n            that._operate('restartProcessId', script, opts, function(err, list) {\n              if (err) return cb(err);\n              Common.printOut(conf.PREFIX_MSG + 'Process successfully started');\n              return cb(true, list);\n            });\n          }\n          else return cb(null);\n        });\n    }\n\n    /**\n     * If start <namespace> start/restart namespace\n     */\n    function restartExistingNameSpace(cb) {\n      if (!isNaN(script) ||\n        (typeof script === 'string' && script.indexOf('/') != -1) ||\n        (typeof script === 'string' && path.extname(script) !== ''))\n        return cb(null);\n\n      if (script !== 'all') {\n        that.Client.getProcessIdsByNamespace(script, function (err, ids) {\n          if (err && cb) return cb(err);\n          if (ids.length > 0) {\n            that._operate('restartProcessId', script, opts, function (err, list) {\n              if (err) return cb(err);\n              Common.printOut(conf.PREFIX_MSG + 'Process successfully started');\n              return cb(true, list);\n            });\n          }\n          else return cb(null);\n        });\n      }\n      else {\n        that._operate('restartProcessId', 'all', function(err, list) {\n          if (err) return cb(err);\n          Common.printOut(conf.PREFIX_MSG + 'Process successfully started');\n          return cb(true, list);\n        });\n      }\n    }\n\n    function restartExistingProcessId(cb) {\n      if (isNaN(script)) return cb(null);\n\n      that._operate('restartProcessId', script, opts, function(err, list) {\n        if (err) return cb(err);\n        Common.printOut(conf.PREFIX_MSG + 'Process successfully started');\n        return cb(true, list);\n      });\n    }\n\n    /**\n     * Restart a process with the same full path\n     * Or start it\n     */\n    function restartExistingProcessPathOrStartNew(cb) {\n      that.Client.executeRemote('getMonitorData', {}, function(err, procs) {\n        if (err) return cb ? cb(new Error(err)) : that.exitCli(conf.ERROR_EXIT);\n\n        var full_path = path.resolve(that.cwd, script);\n        var managed_script = null;\n\n        procs.forEach(function(proc) {\n          if (proc.pm2_env.pm_exec_path == full_path &&\n              proc.pm2_env.name == app_conf.name)\n            managed_script = proc;\n        });\n\n        if (managed_script &&\n          (managed_script.pm2_env.status == conf.STOPPED_STATUS ||\n            managed_script.pm2_env.status == conf.STOPPING_STATUS ||\n            managed_script.pm2_env.status == conf.ERRORED_STATUS)) {\n          // Restart process if stopped\n          var app_name = managed_script.pm2_env.name;\n\n          that._operate('restartProcessId', app_name, opts, function(err, list) {\n            if (err) return cb ? cb(new Error(err)) : that.exitCli(conf.ERROR_EXIT);\n            Common.printOut(conf.PREFIX_MSG + 'Process successfully started');\n            return cb(true, list);\n          });\n          return false;\n        }\n        else if (managed_script && !opts.force) {\n          Common.err('Script already launched, add -f option to force re-execution');\n          return cb(new Error('Script already launched'));\n        }\n\n        var resolved_paths = null;\n\n        try {\n          resolved_paths = Common.resolveAppAttributes({\n            cwd      : that.cwd,\n            pm2_home : that.pm2_home\n          }, app_conf);\n        } catch(e) {\n          Common.err(e.message);\n          return cb(Common.retErr(e));\n        }\n\n        Common.printOut(conf.PREFIX_MSG + 'Starting %s in %s (%d instance' + (resolved_paths.instances > 1 ? 's' : '') + ')',\n          resolved_paths.pm_exec_path, resolved_paths.exec_mode, resolved_paths.instances);\n\n        if (!resolved_paths.env) resolved_paths.env = {};\n\n        // Set PM2 HOME in case of child process using PM2 API\n        resolved_paths.env['PM2_HOME'] = that.pm2_home;\n\n        var additional_env = Modularizer.getAdditionalConf(resolved_paths.name);\n        Object.assign(resolved_paths.env, additional_env);\n\n        // Is KM linked?\n        resolved_paths.km_link = that.gl_is_km_linked;\n\n        that.Client.executeRemote('prepare', resolved_paths, function(err, data) {\n          if (err) {\n            Common.printError(conf.PREFIX_MSG_ERR + 'Error while launching application', err.stack || err);\n            return cb(Common.retErr(err));\n          }\n\n          Common.printOut(conf.PREFIX_MSG + 'Done.');\n          return cb(true, data);\n        });\n        return false;\n      });\n    }\n  }\n\n  /**\n   * Method to start/restart/reload processes from a JSON file\n   * It will start app not started\n   * Can receive only option to skip applications\n   *\n   * @private\n   */\n  _startJson (file, opts, action, pipe, cb) {\n    var config     = {};\n    var appConf    = {};\n    var staticConf = [];\n    var deployConf = {};\n    var apps_info  = [];\n    var that = this;\n\n    /**\n     * Get File configuration\n     */\n    if (typeof(cb) === 'undefined' && typeof(pipe) === 'function') {\n      cb = pipe;\n    }\n    if (typeof(file) === 'object') {\n      config = file;\n    } else if (pipe === 'pipe') {\n      config = Common.parseConfig(file, 'pipe');\n    } else {\n      var data = null;\n\n      var isAbsolute = path.isAbsolute(file)\n      var file_path = isAbsolute ? file : path.join(that.cwd, file);\n\n      debug('Resolved filepath %s', file_path);\n\n      try {\n        data = fs.readFileSync(file_path);\n      } catch(e) {\n        Common.printError(conf.PREFIX_MSG_ERR + 'File ' + file +' not found');\n        return cb ? cb(Common.retErr(e)) : that.exitCli(conf.ERROR_EXIT);\n      }\n\n      try {\n        config = Common.parseConfig(data, file);\n      } catch(e) {\n        Common.printError(conf.PREFIX_MSG_ERR + 'File ' + file + ' malformated');\n        console.error(e);\n        return cb ? cb(Common.retErr(e)) : that.exitCli(conf.ERROR_EXIT);\n      }\n    }\n\n    /**\n     * Alias some optional fields\n     */\n    if (config.deploy)\n      deployConf = config.deploy;\n    if (config.static)\n      staticConf = config.static;\n    if (config.apps)\n      appConf = config.apps;\n    else if (config.pm2)\n      appConf = config.pm2;\n    else\n      appConf = config;\n    if (!Array.isArray(appConf))\n      appConf = [appConf];\n\n    if ((appConf = Common.verifyConfs(appConf)) instanceof Error)\n      return cb ? cb(appConf) : that.exitCli(conf.ERROR_EXIT);\n\n    process.env.PM2_JSON_PROCESSING = true;\n\n    // Get App list\n    var apps_name = [];\n    var proc_list = {};\n\n    // Add statics to apps\n    staticConf.forEach(function(serve) {\n      appConf.push({\n        name: serve.name ? serve.name : `static-page-server-${serve.port}`,\n        script: path.resolve(__dirname, 'API', 'Serve.js'),\n        env: {\n          PM2_SERVE_PORT: serve.port,\n          PM2_SERVE_HOST: serve.host,\n          PM2_SERVE_PATH: serve.path,\n          PM2_SERVE_SPA: serve.spa,\n          PM2_SERVE_DIRECTORY: serve.directory,\n          PM2_SERVE_BASIC_AUTH: serve.basic_auth !== undefined,\n          PM2_SERVE_BASIC_AUTH_USERNAME: serve.basic_auth ? serve.basic_auth.username : null,\n          PM2_SERVE_BASIC_AUTH_PASSWORD: serve.basic_auth ? serve.basic_auth.password : null,\n          PM2_SERVE_MONITOR: serve.monitor\n        }\n      });\n    });\n\n    // Here we pick only the field we want from the CLI when starting a JSON\n    appConf.forEach(function(app) {\n      if (!app.env) { app.env = {}; }\n      app.env.io = app.io;\n      // --only <app>\n      if (opts.only) {\n        var apps = opts.only.split(/,| /)\n        if (apps.indexOf(app.name) == -1)\n          return false\n      }\n      // Namespace\n      if (!app.namespace) {\n        if (opts.namespace)\n          app.namespace = opts.namespace;\n        else\n          app.namespace = 'default';\n      }\n      // --watch\n      if (!app.watch && opts.watch && opts.watch === true)\n        app.watch = true;\n      // --ignore-watch\n      if (!app.ignore_watch && opts.ignore_watch)\n        app.ignore_watch = opts.ignore_watch;\n      if (opts.install_url)\n        app.install_url = opts.install_url;\n      // --instances <nb>\n      if (opts.instances && typeof(opts.instances) === 'number')\n        app.instances = opts.instances;\n      // --uid <user>\n      if (opts.uid)\n        app.uid = opts.uid;\n      // --gid <user>\n      if (opts.gid)\n        app.gid = opts.gid;\n      // Specific\n      if (app.append_env_to_name && opts.env)\n        app.name += ('-' + opts.env);\n      if (opts.name_prefix && app.name.indexOf(opts.name_prefix) == -1)\n        app.name = `${opts.name_prefix}:${app.name}`\n\n      app.username = Common.getCurrentUsername();\n      apps_name.push(app.name);\n    });\n\n    that.Client.executeRemote('getMonitorData', {}, function(err, raw_proc_list) {\n      if (err) {\n        Common.printError(err);\n        return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);\n      }\n\n      /**\n       * Uniquify in memory process list\n       */\n      raw_proc_list.forEach(function(proc) {\n        proc_list[proc.name] = proc;\n      });\n\n      /**\n       * Auto detect application already started\n       * and act on them depending on action\n       */\n      eachLimit(Object.keys(proc_list), conf.CONCURRENT_ACTIONS, function(proc_name, next) {\n        // Skip app name (--only option)\n        if (apps_name.indexOf(proc_name) == -1)\n          return next();\n\n        if (!(action == 'reloadProcessId' ||\n            action == 'softReloadProcessId' ||\n            action == 'restartProcessId'))\n          throw new Error('Wrong action called');\n\n        var apps = appConf.filter(function(app) {\n          return app.name == proc_name;\n        });\n\n        var envs = apps.map(function(app){\n          // Binds env_diff to env and returns it.\n          return Common.mergeEnvironmentVariables(app, opts.env, deployConf);\n        });\n\n        // Assigns own enumerable properties of all\n        // Notice: if people use the same name in different apps,\n        //         duplicated envs will be overrode by the last one\n        var env = envs.reduce(function(e1, e2){\n          return Object.assign(e1, e2);\n        });\n\n        // When we are processing JSON, allow to keep the new env by default\n        env.updateEnv = true;\n\n        // Pass `env` option\n        that._operate(action, proc_name, env, function(err, ret) {\n          if (err) Common.printError(err);\n\n          // For return\n          apps_info = apps_info.concat(ret);\n\n          that.Client.notifyGod(action, proc_name);\n          // And Remove from array to spy\n          apps_name.splice(apps_name.indexOf(proc_name), 1);\n          return next();\n        });\n\n      }, function(err) {\n        if (err) return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);\n        if (apps_name.length > 0 && action != 'start')\n          Common.printOut(conf.PREFIX_MSG_WARNING + 'Applications %s not running, starting...', apps_name.join(', '));\n        // Start missing apps\n        return startApps(apps_name, function(err, apps) {\n          apps_info = apps_info.concat(apps);\n          return cb ? cb(err, apps_info) : that.speedList(err ? 1 : 0);\n        });\n      });\n      return false;\n    });\n\n    function startApps(app_name_to_start, cb) {\n      var apps_to_start = [];\n      var apps_started = [];\n      var apps_errored = [];\n\n      appConf.forEach(function(app, i) {\n        if (app_name_to_start.indexOf(app.name) != -1) {\n          apps_to_start.push(appConf[i]);\n        }\n      });\n\n      eachLimit(apps_to_start, conf.CONCURRENT_ACTIONS, function(app, next) {\n        if (opts.cwd)\n          app.cwd = opts.cwd;\n        if (opts.force_name)\n          app.name = opts.force_name;\n        if (opts.started_as_module)\n          app.pmx_module = true;\n\n        var resolved_paths = null;\n\n        // hardcode script name to use `serve` feature inside a process file\n        if (app.script === 'serve') {\n          app.script = path.resolve(__dirname, 'API', 'Serve.js')\n        }\n\n        try {\n          resolved_paths = Common.resolveAppAttributes({\n            cwd      : that.cwd,\n            pm2_home : that.pm2_home\n          }, app);\n        } catch (e) {\n          apps_errored.push(e)\n          Common.err(`Error: ${e.message}`)\n          return next();\n        }\n\n        if (!resolved_paths.env) resolved_paths.env = {};\n\n        // Set PM2 HOME in case of child process using PM2 API\n        resolved_paths.env['PM2_HOME'] = that.pm2_home;\n\n        var additional_env = Modularizer.getAdditionalConf(resolved_paths.name);\n        Object.assign(resolved_paths.env, additional_env);\n\n        resolved_paths.env = Common.mergeEnvironmentVariables(resolved_paths, opts.env, deployConf);\n\n        delete resolved_paths.env.current_conf;\n\n        // Is KM linked?\n        resolved_paths.km_link = that.gl_is_km_linked;\n\n        if (resolved_paths.wait_ready) {\n          Common.warn(`App ${resolved_paths.name} has option 'wait_ready' set, waiting for app to be ready...`)\n        }\n        that.Client.executeRemote('prepare', resolved_paths, function(err, data) {\n          if (err) {\n            Common.printError(conf.PREFIX_MSG_ERR + 'Process failed to launch %s', err.message ? err.message : err);\n            return next();\n          }\n          if (data.length === 0) {\n            Common.printError(conf.PREFIX_MSG_ERR + 'Process config loading failed', data);\n            return next();\n          }\n\n          Common.printOut(conf.PREFIX_MSG + 'App [%s] launched (%d instances)', data[0].pm2_env.name, data.length);\n          apps_started = apps_started.concat(data);\n          next();\n        });\n\n      }, function(err) {\n        var final_error = err || apps_errored.length > 0 ? apps_errored : null\n        return cb ? cb(final_error, apps_started) : that.speedList();\n      });\n      return false;\n    }\n  }\n\n  /**\n   * Apply a RPC method on the json file\n   * @private\n   * @method actionFromJson\n   * @param {string} action RPC Method\n   * @param {object} options\n   * @param {string|object} file file\n   * @param {string} jsonVia action type (=only 'pipe' ?)\n   * @param {Function}\n   */\n  actionFromJson (action, file, opts, jsonVia, cb) {\n    var appConf = {};\n    var ret_processes = [];\n    var that = this;\n\n    //accept programmatic calls\n    if (typeof file == 'object') {\n      cb = typeof jsonVia == 'function' ? jsonVia : cb;\n      appConf = file;\n    }\n    else if (jsonVia == 'file') {\n      var data = null;\n\n      try {\n        data = fs.readFileSync(file);\n      } catch(e) {\n        Common.printError(conf.PREFIX_MSG_ERR + 'File ' + file +' not found');\n        return cb ? cb(Common.retErr(e)) : that.exitCli(conf.ERROR_EXIT);\n      }\n\n      try {\n        appConf = Common.parseConfig(data, file);\n      } catch(e) {\n        Common.printError(conf.PREFIX_MSG_ERR + 'File ' + file + ' malformated');\n        console.error(e);\n        return cb ? cb(Common.retErr(e)) : that.exitCli(conf.ERROR_EXIT);\n      }\n    } else if (jsonVia == 'pipe') {\n      appConf = Common.parseConfig(file, 'pipe');\n    } else {\n      Common.printError('Bad call to actionFromJson, jsonVia should be one of file, pipe');\n      return that.exitCli(conf.ERROR_EXIT);\n    }\n\n    // Backward compatibility\n    if (appConf.apps)\n      appConf = appConf.apps;\n\n    if (!Array.isArray(appConf))\n      appConf = [appConf];\n\n    if ((appConf = Common.verifyConfs(appConf)) instanceof Error)\n      return cb ? cb(appConf) : that.exitCli(conf.ERROR_EXIT);\n\n    eachLimit(appConf, conf.CONCURRENT_ACTIONS, function(proc, next1) {\n      var name = '';\n      var new_env;\n\n      if (!proc.name)\n        name = path.basename(proc.script);\n      else\n        name = proc.name;\n\n      if (opts.only && opts.only != name)\n        return process.nextTick(next1);\n\n      if (opts && opts.env)\n        new_env = Common.mergeEnvironmentVariables(proc, opts.env);\n      else\n        new_env = Common.mergeEnvironmentVariables(proc);\n\n      that.Client.getProcessIdByName(name, function(err, ids) {\n        if (err) {\n          Common.printError(err);\n          return next1();\n        }\n        if (!ids) return next1();\n\n        eachLimit(ids, conf.CONCURRENT_ACTIONS, function(id, next2) {\n          var opts = {};\n\n          //stopProcessId could accept options to?\n          if (action == 'restartProcessId') {\n            opts = {id : id, env : new_env};\n          } else {\n            opts = id;\n          }\n\n          that.Client.executeRemote(action, opts, function(err, res) {\n            ret_processes.push(res);\n            if (err) {\n              Common.printError(err);\n              return next2();\n            }\n\n            if (action == 'restartProcessId') {\n              that.Client.notifyGod('restart', id);\n            } else if (action == 'deleteProcessId') {\n              that.Client.notifyGod('delete', id);\n            } else if (action == 'stopProcessId') {\n              that.Client.notifyGod('stop', id);\n            }\n\n            Common.printOut(conf.PREFIX_MSG + '[%s](%d) \\u2713', name, id);\n            return next2();\n          });\n        }, function(err) {\n          return next1(null, ret_processes);\n        });\n      });\n    }, function(err) {\n      if (cb) return cb(null, ret_processes);\n      else return that.speedList();\n    });\n  }\n\n\n  /**\n   * Main function to operate with PM2 daemon\n   *\n   * @param {String} action_name  Name of action (restartProcessId, deleteProcessId, stopProcessId)\n   * @param {String} process_name can be 'all', a id integer or process name\n   * @param {Object} envs         object with CLI options / environment\n   */\n  _operate (action_name, process_name, envs, cb) {\n    var that = this;\n    var update_env = false;\n    var ret = [];\n\n    // Make sure all options exist\n    if (!envs)\n      envs = {};\n\n    if (typeof(envs) == 'function'){\n      cb = envs;\n      envs = {};\n    }\n\n    // Set via env.update (JSON processing)\n    if (envs.updateEnv === true)\n      update_env = true;\n\n    var concurrent_actions = envs.parallel || conf.CONCURRENT_ACTIONS;\n\n    if (!process.env.PM2_JSON_PROCESSING || envs.commands) {\n      envs = that._handleAttributeUpdate(envs);\n    }\n\n    /**\n     * Set current updated configuration if not passed\n     */\n    if (!envs.current_conf) {\n      var _conf = fclone(envs);\n      envs = {\n        current_conf : _conf\n      }\n\n      // Is KM linked?\n      envs.current_conf.km_link = that.gl_is_km_linked;\n    }\n\n    /**\n     * Operate action on specific process id\n     */\n    function processIds(ids, cb) {\n      Common.printOut(conf.PREFIX_MSG + 'Applying action %s on app [%s](ids: %s)', action_name, process_name, ids);\n\n      if (ids.length <= 2)\n        concurrent_actions = 1;\n\n      if (action_name == 'deleteProcessId')\n        concurrent_actions = 10;\n\n      eachLimit(ids, concurrent_actions, function(id, next) {\n        var opts;\n\n        // These functions need extra param to be passed\n        if (action_name == 'restartProcessId' ||\n          action_name == 'reloadProcessId' ||\n          action_name == 'softReloadProcessId') {\n          var new_env = {};\n\n          if (update_env === true) {\n            if (conf.PM2_PROGRAMMATIC == true)\n              new_env = Common.safeExtend({}, process.env);\n            else\n              new_env = Object.assign({}, process.env);\n\n            Object.keys(envs).forEach(function(k) {\n              new_env[k] = envs[k];\n            });\n          }\n          else {\n            new_env = envs;\n          }\n\n          opts = {\n            id  : id,\n            env : new_env\n          };\n        }\n        else {\n          opts = id;\n        }\n\n        that.Client.executeRemote(action_name, opts, function(err, res) {\n          if (err) {\n            Common.printError(conf.PREFIX_MSG_ERR + 'Process %s not found', id);\n            return next(`Process ${id} not found`);\n          }\n\n          if (action_name == 'restartProcessId') {\n            that.Client.notifyGod('restart', id);\n          } else if (action_name == 'deleteProcessId') {\n            that.Client.notifyGod('delete', id);\n          } else if (action_name == 'stopProcessId') {\n            that.Client.notifyGod('stop', id);\n          } else if (action_name == 'reloadProcessId') {\n            that.Client.notifyGod('reload', id);\n          } else if (action_name == 'softReloadProcessId') {\n            that.Client.notifyGod('graceful reload', id);\n          }\n\n          if (!Array.isArray(res))\n            res = [res];\n\n          // Filter return\n          res.forEach(function(proc) {\n            Common.printOut(conf.PREFIX_MSG + '[%s](%d) \\u2713', proc.pm2_env ? proc.pm2_env.name : process_name, id);\n\n            if (action_name == 'stopProcessId' && proc.pm2_env && proc.pm2_env.cron_restart) {\n              Common.warn(`App ${chalk.bold(proc.pm2_env.name)} stopped but CRON RESTART is still UP ${proc.pm2_env.cron_restart}`)\n            }\n\n            if (!proc.pm2_env) return false;\n\n            ret.push({\n              name         : proc.pm2_env.name,\n              namespace: proc.pm2_env.namespace,\n              pm_id        : proc.pm2_env.pm_id,\n              status       : proc.pm2_env.status,\n              restart_time : proc.pm2_env.restart_time,\n              pm2_env : {\n                name         : proc.pm2_env.name,\n                namespace: proc.pm2_env.namespace,\n                pm_id        : proc.pm2_env.pm_id,\n                status       : proc.pm2_env.status,\n                restart_time : proc.pm2_env.restart_time,\n                env          : proc.pm2_env.env\n              }\n            });\n          });\n\n          return next();\n        });\n      }, function(err) {\n        if (err) return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);\n        return cb ? cb(null, ret) : that.speedList();\n      });\n    }\n\n    if (process_name == 'all') {\n      // When using shortcuts like 'all', do not delete modules\n      var fn\n\n      if (process.env.PM2_STATUS == 'stopping')\n        that.Client.getAllProcessId(function(err, ids) {\n          reoperate(err, ids)\n        });\n      else\n        that.Client.getAllProcessIdWithoutModules(function(err, ids) {\n          reoperate(err, ids)\n        });\n\n      function reoperate(err, ids) {\n        if (err) {\n          Common.printError(err);\n          return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);\n        }\n        if (!ids || ids.length === 0) {\n          Common.printError(conf.PREFIX_MSG_WARNING + 'No process found');\n          return cb ? cb(new Error('process name not found')) : that.exitCli(conf.ERROR_EXIT);\n        }\n        return processIds(ids, cb);\n      }\n    }\n    // operate using regex\n    else if (isNaN(process_name) && process_name[0] === '/' && process_name[process_name.length - 1] === '/') {\n      var regex = new RegExp(process_name.replace(/\\//g, ''));\n\n      that.Client.executeRemote('getMonitorData', {}, function(err, list) {\n        if (err) {\n          Common.printError('Error retrieving process list: ' + err);\n          return cb(err);\n        }\n        var found_proc = [];\n        list.forEach(function(proc) {\n          if (regex.test(proc.pm2_env.name)) {\n            found_proc.push(proc.pm_id);\n          }\n        });\n\n        if (found_proc.length === 0) {\n          Common.printError(conf.PREFIX_MSG_WARNING + 'No process found');\n          return cb ? cb(new Error('process name not found')) : that.exitCli(conf.ERROR_EXIT);\n        }\n\n        return processIds(found_proc, cb);\n      });\n    }\n    else if (isNaN(process_name)) {\n      /**\n       * We can not stop or delete a module but we can restart it\n       * to refresh configuration variable\n       */\n      var allow_module_restart = action_name == 'restartProcessId' ? true : false;\n\n      that.Client.getProcessIdByName(process_name, allow_module_restart, function (err, ids) {\n        if (err) {\n          Common.printError(err);\n          return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);\n        }\n        if (ids && ids.length > 0) {\n          /**\n         * Determine if the process to restart is a module\n         * if yes load configuration variables and merge with the current environment\n         */\n          var additional_env = Modularizer.getAdditionalConf(process_name);\n          Object.assign(envs, additional_env);\n          return processIds(ids, cb);\n        }\n\n        that.Client.getProcessIdsByNamespace(process_name, allow_module_restart, function (err, ns_process_ids) {\n          if (err) {\n            Common.printError(err);\n            return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);\n          }\n          if (!ns_process_ids || ns_process_ids.length === 0) {\n            Common.printError(conf.PREFIX_MSG_ERR + 'Process or Namespace %s not found', process_name);\n            return cb ? cb(new Error('process or namespace not found')) : that.exitCli(conf.ERROR_EXIT);\n          }\n\n          /**\n           * Determine if the process to restart is a module\n           * if yes load configuration variables and merge with the current environment\n           */\n          var ns_additional_env = Modularizer.getAdditionalConf(process_name);\n          Object.assign(envs, ns_additional_env);\n          return processIds(ns_process_ids, cb);\n        });\n      });\n    } else {\n      if (that.pm2_configuration.docker == \"true\" ||\n          that.pm2_configuration.docker == true) {\n        // Docker/Systemd process interaction detection\n        that.Client.executeRemote('getMonitorData', {}, (err, proc_list) => {\n          var higher_id = 0\n          proc_list.forEach(p => { p.pm_id > higher_id ? higher_id = p.pm_id : null })\n\n          // Is Docker/Systemd\n          if (process_name > higher_id)\n            return DockerMgmt.processCommand(that, higher_id, process_name, action_name, (err) => {\n              if (err) {\n                Common.printError(conf.PREFIX_MSG_ERR + (err.message ? err.message : err));\n                return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);\n              }\n\n              return cb ? cb(null, ret) : that.speedList();\n            })\n\n          // Check if application name as number is an app name\n          that.Client.getProcessIdByName(process_name, function(err, ids) {\n            if (ids.length > 0)\n              return processIds(ids, cb);\n\n            // Check if application name as number is an namespace\n            that.Client.getProcessIdsByNamespace(process_name, function(err, ns_process_ids) {\n              if (ns_process_ids.length > 0)\n                return processIds(ns_process_ids, cb);\n              // Else operate on pm id\n              return processIds([process_name], cb);\n            });\n          });\n        })\n      }\n      else {\n        // Check if application name as number is an app name\n        that.Client.getProcessIdByName(process_name, function(err, ids) {\n          if (ids.length > 0)\n            return processIds(ids, cb);\n\n          // Check if application name as number is an namespace\n          that.Client.getProcessIdsByNamespace(process_name, function(err, ns_process_ids) {\n            if (ns_process_ids.length > 0)\n              return processIds(ns_process_ids, cb);\n            // Else operate on pm id\n            return processIds([process_name], cb);\n          });\n        });\n      }\n    }\n  }\n\n  /**\n   * Converts CamelCase Commander.js arguments\n   * to Underscore\n   * (nodeArgs -> node_args)\n   */\n  _handleAttributeUpdate (opts) {\n    var conf = Config.filterOptions(opts);\n    var that = this;\n\n    if (typeof(conf.name) != 'string')\n      delete conf.name;\n\n    var argsIndex = 0;\n    if (opts.rawArgs && (argsIndex = opts.rawArgs.indexOf('--')) >= 0) {\n      conf.args = opts.rawArgs.slice(argsIndex + 1);\n    }\n\n    var appConf = Common.verifyConfs(conf)[0];\n\n    if (appConf instanceof Error) {\n      Common.printError('Error while transforming CamelCase args to underscore');\n      return appConf;\n    }\n\n    if (argsIndex == -1)\n      delete appConf.args;\n    if (appConf.name == 'undefined')\n      delete appConf.name;\n\n    delete appConf.exec_mode;\n\n    if (Array.isArray(appConf.watch) && appConf.watch.length === 0) {\n      if (!~opts.rawArgs.indexOf('--watch'))\n        delete appConf.watch\n    }\n\n    // Options set via environment variables\n    if (process.env.PM2_DEEP_MONITORING)\n      appConf.deep_monitoring = true;\n\n    // Force deletion of defaults values set by commander\n    // to avoid overriding specified configuration by user\n    if (appConf.treekill === true)\n      delete appConf.treekill;\n    if (appConf.pmx === true)\n      delete appConf.pmx;\n    if (appConf.vizion === true)\n      delete appConf.vizion;\n    if (appConf.automation === true)\n      delete appConf.automation;\n    if (appConf.autostart === true)\n      delete appConf.autostart;\n    if (appConf.autorestart === true)\n      delete appConf.autorestart;\n\n    return appConf;\n  }\n\n  getProcessIdByName (name, cb) {\n    var that = this;\n\n    this.Client.getProcessIdByName(name, function(err, id) {\n      if (err) {\n        Common.printError(err);\n        return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);\n      }\n      console.log(id);\n      return cb ? cb(null, id) : that.exitCli(conf.SUCCESS_EXIT);\n    });\n  }\n\n  /**\n   * Description\n   * @method jlist\n   * @param {} debug\n   * @return\n   */\n  jlist (debug) {\n    var that = this;\n\n    that.Client.executeRemote('getMonitorData', {}, function(err, list) {\n      if (err) {\n        Common.printError(err);\n        return that.exitCli(conf.ERROR_EXIT);\n      }\n\n      if (debug) {\n        process.stdout.write(util.inspect(list, false, null, false));\n      }\n      else {\n        process.stdout.write(JSON.stringify(list));\n      }\n\n      that.exitCli(conf.SUCCESS_EXIT);\n\n    });\n  }\n\n  /**\n   * Display system information\n   * @method slist\n   * @return\n   */\n  slist (tree) {\n    this.Client.executeRemote('getSystemData', {}, (err, sys_infos) => {\n      if (err) {\n        Common.err(err)\n        return this.exitCli(conf.ERROR_EXIT)\n      }\n\n      if (tree === true) {\n        var treeify = require('./tools/treeify.js')\n        console.log(treeify.asTree(sys_infos, true))\n      }\n      else\n        process.stdout.write(util.inspect(sys_infos, false, null, false))\n      this.exitCli(conf.SUCCESS_EXIT)\n    })\n  }\n\n  /**\n   * Description\n   * @method speedList\n   * @return\n   */\n  speedList (code, apps_acted) {\n    var that = this;\n    var systemdata = null\n    var acted = []\n\n    if ((code != 0 && code != null)) {\n      return that.exitCli(code ? code : conf.SUCCESS_EXIT);\n    }\n\n    if (apps_acted && apps_acted.length > 0) {\n      apps_acted.forEach(proc => {\n        acted.push(proc.pm2_env ? proc.pm2_env.pm_id : proc.pm_id)\n      })\n    }\n\n    // Do nothing if PM2 called programmatically and not called from CLI (also in exitCli)\n    if ((conf.PM2_PROGRAMMATIC && process.env.PM2_USAGE != 'CLI'))\n      return false;\n\n    return that.Client.executeRemote('getMonitorData', {}, (err, proc_list) => {\n      doList(err, proc_list)\n    })\n\n    function doList(err, list) {\n      if (err) {\n        if (that.gl_retry == 0) {\n          that.gl_retry += 1;\n          return setTimeout(that.speedList.bind(that), 1400);\n        }\n        console.error('Error retrieving process list: %s.\\nA process seems to be on infinite loop, retry in 5 seconds',err);\n        return that.exitCli(conf.ERROR_EXIT);\n      }\n      if (process.stdout.isTTY === false) {\n        UX.list_min(list);\n      }\n      else if (commander.miniList && !commander.silent)\n        UX.list_min(list);\n      else if (!commander.silent) {\n        if (that.gl_interact_infos) {\n          var dashboard_url = `https://app.pm2.io/#/r/${that.gl_interact_infos.public_key}`\n\n          if (that.gl_interact_infos.info_node != 'https://root.keymetrics.io') {\n            dashboard_url = `${that.gl_interact_infos.info_node}/#/r/${that.gl_interact_infos.public_key}`\n          }\n\n          Common.printOut('%s PM2+ activated | Instance Name: %s | Dash: %s',\n                          chalk.green.bold('⇆'),\n                          chalk.bold(that.gl_interact_infos.machine_name),\n                          chalk.bold(dashboard_url))\n        }\n        UX.list(list, commander);\n        //Common.printOut(chalk.white.italic(' Use `pm2 show <id|name>` to get more details about an app'));\n      }\n\n      if (that.Client.daemon_mode == false) {\n        Common.printOut('[--no-daemon] Continue to stream logs');\n        Common.printOut('[--no-daemon] Exit on target PM2 exit pid=' + fs.readFileSync(conf.PM2_PID_FILE_PATH).toString());\n        global._auto_exit = true;\n        return that.streamLogs('all', 0, false, 'HH:mm:ss', false);\n      }\n      // if (process.stdout.isTTY) if looking for start logs\n      else if (!process.env.TRAVIS && process.env.NODE_ENV != 'test' && acted.length > 0 && (commander.attach === true)) {\n        Common.info(`Log streaming apps id: ${chalk.cyan(acted.join(' '))}, exit with Ctrl-C or will exit in 10secs`)\n\n        // setTimeout(() => {\n        //   Common.info(`Log streaming exited automatically, run 'pm2 logs' to continue watching logs`)\n        //   return that.exitCli(code ? code : conf.SUCCESS_EXIT);\n        // }, 10000)\n\n        return acted.forEach((proc_name) => {\n          that.streamLogs(proc_name, 0, false, null, false);\n        })\n      }\n      else {\n        return that.exitCli(code ? code : conf.SUCCESS_EXIT);\n      }\n    }\n  }\n\n  /**\n   * Scale up/down a process\n   * @method scale\n   */\n  scale (app_name, number, cb) {\n    var that = this;\n\n    function addProcs(proc, value, cb) {\n      (function ex(proc, number) {\n        if (number-- === 0) return cb();\n        Common.printOut(conf.PREFIX_MSG + 'Scaling up application');\n        that.Client.executeRemote('duplicateProcessId', proc.pm2_env.pm_id, ex.bind(this, proc, number));\n      })(proc, number);\n    }\n\n    function rmProcs(procs, value, cb) {\n      var i = 0;\n\n      (function ex(procs, number) {\n        if (number++ === 0) return cb();\n        that._operate('deleteProcessId', procs[i++].pm2_env.pm_id, ex.bind(this, procs, number));\n      })(procs, number);\n    }\n\n    function end() {\n      return cb ? cb(null, {success:true}) : that.speedList();\n    }\n\n    this.Client.getProcessByName(app_name, function(err, procs) {\n      if (err) {\n        Common.printError(err);\n        return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);\n      }\n\n      if (!procs || procs.length === 0) {\n        Common.printError(conf.PREFIX_MSG_ERR + 'Application %s not found', app_name);\n        return cb ? cb(new Error('App not found')) : that.exitCli(conf.ERROR_EXIT);\n      }\n\n      var proc_number = procs.length;\n\n      if (typeof(number) === 'string' && number.indexOf('+') >= 0) {\n        number = parseInt(number, 10);\n        return addProcs(procs[0], number, end);\n      }\n      else if (typeof(number) === 'string' && number.indexOf('-') >= 0) {\n        number = parseInt(number, 10);\n        return rmProcs(procs[0], number, end);\n      }\n      else {\n        number = parseInt(number, 10);\n        number = number - proc_number;\n\n        if (number < 0)\n          return rmProcs(procs, number, end);\n        else if (number > 0)\n          return addProcs(procs[0], number, end);\n        else {\n          Common.printError(conf.PREFIX_MSG_ERR + 'Nothing to do');\n          return cb ? cb(new Error('Same process number')) : that.exitCli(conf.ERROR_EXIT);\n        }\n      }\n    });\n  }\n\n  /**\n   * Description\n   * @method describeProcess\n   * @param {} pm2_id\n   * @return\n   */\n  describe (pm2_id, cb) {\n    var that = this;\n\n    var found_proc = [];\n\n    that.Client.executeRemote('getMonitorData', {}, function(err, list) {\n      if (err) {\n        Common.printError('Error retrieving process list: ' + err);\n        that.exitCli(conf.ERROR_EXIT);\n      }\n\n      list.forEach(function(proc) {\n        if ((!isNaN(pm2_id)    && proc.pm_id == pm2_id) ||\n          (typeof(pm2_id) === 'string' && proc.name  == pm2_id)) {\n          found_proc.push(proc);\n        }\n      });\n\n      if (found_proc.length === 0) {\n        Common.printError(conf.PREFIX_MSG_WARNING + '%s doesn\\'t exist', pm2_id);\n        return cb ? cb(null, []) : that.exitCli(conf.ERROR_EXIT);\n      }\n\n      if (!cb) {\n        found_proc.forEach(function(proc) {\n          UX.describe(proc);\n        });\n      }\n\n      return cb ? cb(null, found_proc) : that.exitCli(conf.SUCCESS_EXIT);\n    });\n  }\n\n  /**\n   * API method to perform a deep update of PM2\n   * @method deepUpdate\n   */\n  deepUpdate (cb) {\n    var that = this;\n\n    Common.printOut(conf.PREFIX_MSG + 'Updating PM2...');\n\n    var child = sexec(\"npm i -g pm2@latest; pm2 update\");\n\n    child.stdout.on('end', function() {\n      Common.printOut(conf.PREFIX_MSG + 'PM2 successfully updated');\n      cb ? cb(null, {success:true}) : that.exitCli(conf.SUCCESS_EXIT);\n    });\n  }\n};\n\n\n//////////////////////////\n// Load all API methods //\n//////////////////////////\n\nrequire('./API/Extra.js')(API);\nrequire('./API/Deploy.js')(API);\nrequire('./API/Modules/index.js')(API);\n\nrequire('./API/pm2-plus/link.js')(API);\nrequire('./API/pm2-plus/process-selector.js')(API);\nrequire('./API/pm2-plus/helpers.js')(API);\n\nrequire('./API/Configuration.js')(API);\nrequire('./API/Version.js')(API);\nrequire('./API/Startup.js')(API);\nrequire('./API/LogManagement.js')(API);\nrequire('./API/Containerizer.js')(API);\n\n\nmodule.exports = API;\n"
  },
  {
    "path": "lib/Client.js",
    "content": "/**\n * Copyright 2013-2022 the PM2 project authors. All rights reserved.\n * Use of this source code is governed by a license that\n * can be found in the LICENSE file.\n */\n\nvar debug          = require('debug')('pm2:client');\nvar Common         = require('./Common.js');\nvar KMDaemon       = require('@pm2/agent/src/InteractorClient');\nvar rpc            = require('pm2-axon-rpc');\nvar forEach        = require('async/forEach');\nvar axon           = require('pm2-axon');\nvar util           = require('util');\nvar fs             = require('fs');\nvar path           = require('path');\nvar pkg            = require('../package.json');\nvar which          = require('./tools/which.js');\n\nfunction noop() {}\n\nvar Client = module.exports = function(opts) {\n  if (!opts) opts = {};\n\n  if (!opts.conf)\n    this.conf = require('../constants.js');\n  else {\n    this.conf     = opts.conf;\n  }\n\n  this.daemon_mode = typeof(opts.daemon_mode) === 'undefined' ? true : opts.daemon_mode;\n  this.pm2_home    = this.conf.PM2_ROOT_PATH;\n  this.secret_key   = opts.secret_key;\n  this.public_key   = opts.public_key;\n  this.machine_name = opts.machine_name;\n\n  // Create all folders and files needed\n  // Client depends to that to interact with PM2 properly\n  this.initFileStructure(this.conf);\n\n  debug('Using RPC file %s', this.conf.DAEMON_RPC_PORT);\n  debug('Using PUB file %s', this.conf.DAEMON_PUB_PORT);\n  this.rpc_socket_file = this.conf.DAEMON_RPC_PORT;\n  this.pub_socket_file = this.conf.DAEMON_PUB_PORT;\n};\n\n// @breaking change (noDaemonMode has been drop)\n// @todo ret err\nClient.prototype.start = function(cb) {\n  var that = this;\n\n  this.pingDaemon(function(daemonAlive) {\n    if (daemonAlive === true)\n      return that.launchRPC(function(err, meta) {\n        return cb(null, {\n          daemon_mode      : that.conf.daemon_mode,\n          new_pm2_instance : false,\n          rpc_socket_file  : that.rpc_socket_file,\n          pub_socket_file  : that.pub_socket_file,\n          pm2_home         : that.pm2_home\n        });\n      });\n\n    /**\n     * No Daemon mode\n     */\n    if (that.daemon_mode === false) {\n      var Daemon         = require('./Daemon.js');\n\n      var daemon = new Daemon({\n        pub_socket_file : that.conf.DAEMON_PUB_PORT,\n        rpc_socket_file : that.conf.DAEMON_RPC_PORT,\n        pid_file        : that.conf.PM2_PID_FILE_PATH,\n        ignore_signals  : true\n      });\n\n      console.log('Launching in no daemon mode');\n\n      daemon.innerStart(function() {\n        KMDaemon.launchAndInteract(that.conf, {\n          machine_name : that.machine_name,\n          public_key   : that.public_key,\n          secret_key   : that.secret_key,\n          pm2_version  : pkg.version\n        }, function(err, data, interactor_proc) {\n          that.interactor_process = interactor_proc;\n        });\n\n        that.launchRPC(function(err, meta) {\n          return cb(null, {\n            daemon_mode      : that.conf.daemon_mode,\n            new_pm2_instance : true,\n            rpc_socket_file  : that.rpc_socket_file,\n            pub_socket_file  : that.pub_socket_file,\n            pm2_home         : that.pm2_home\n          });\n        });\n      });\n      return false;\n    }\n\n    /**\n     * Daemon mode\n     */\n    that.launchDaemon(function(err, child) {\n      if (err) {\n        Common.printError(err);\n        return cb ? cb(err) : process.exit(that.conf.ERROR_EXIT);\n      }\n\n      if (!process.env.PM2_DISCRETE_MODE)\n        Common.printOut(that.conf.PREFIX_MSG + 'PM2 Successfully daemonized');\n\n      that.launchRPC(function(err, meta) {\n        return cb(null, {\n          daemon_mode      : that.conf.daemon_mode,\n          new_pm2_instance : true,\n          rpc_socket_file  : that.rpc_socket_file,\n          pub_socket_file  : that.pub_socket_file,\n          pm2_home         : that.pm2_home\n        });\n      });\n    });\n  });\n};\n\n// Init file structure of pm2_home\n// This includes\n// - pm2 pid and log path\n// - rpc and pub socket for command execution\nClient.prototype.initFileStructure = function (opts) {\n  if (!fs.existsSync(opts.DEFAULT_LOG_PATH)) {\n    try {\n      require('mkdirp').sync(opts.DEFAULT_LOG_PATH);\n    } catch (e) {\n      console.error(e.stack || e);\n    }\n  }\n\n  if (!fs.existsSync(opts.DEFAULT_PID_PATH)) {\n    try {\n      require('mkdirp').sync(opts.DEFAULT_PID_PATH);\n    } catch (e) {\n      console.error(e.stack || e);\n    }\n  }\n\n  if (!fs.existsSync(opts.PM2_MODULE_CONF_FILE)) {\n    try {\n      fs.writeFileSync(opts.PM2_MODULE_CONF_FILE, \"{}\");\n    } catch (e) {\n      console.error(e.stack || e);\n    }\n  }\n\n  if (!fs.existsSync(opts.DEFAULT_MODULE_PATH)) {\n    try {\n      require('mkdirp').sync(opts.DEFAULT_MODULE_PATH);\n    } catch (e) {\n      console.error(e.stack || e);\n    }\n  }\n\n  if (process.env.PM2_DISCRETE_MODE) {\n    try {\n      fs.writeFileSync(path.join(opts.PM2_HOME, 'touch'), Date.now().toString());\n    } catch(e) {\n      debug(e.stack || e);\n    }\n  }\n\n  if (!process.env.PM2_PROGRAMMATIC && !fs.existsSync(path.join(opts.PM2_HOME, 'touch'))) {\n\n    var vCheck = require('./VersionCheck.js')\n\n    vCheck({\n      state: 'install',\n      version: pkg.version\n    })\n\n    var dt = fs.readFileSync(path.join(__dirname, opts.PM2_BANNER));\n    console.log(dt.toString());\n    try {\n      fs.writeFileSync(path.join(opts.PM2_HOME, 'touch'), Date.now().toString());\n    } catch(e) {\n      debug(e.stack || e);\n    }\n  }\n};\n\nClient.prototype.close = function(cb) {\n  var that = this;\n\n  forEach([\n    that.disconnectRPC.bind(that),\n    that.disconnectBus.bind(that)\n  ], function(fn, next) {\n    fn(next)\n  }, cb);\n};\n\n/**\n * Launch the Daemon by forking this same file\n * The method Client.remoteWrapper will be called\n *\n * @method launchDaemon\n * @param {Object} opts\n * @param {Object} [opts.interactor=true] allow to disable interaction on launch\n */\nClient.prototype.launchDaemon = function(opts, cb) {\n  if (typeof(opts) == 'function') {\n    cb = opts;\n    opts = {\n      interactor : true\n    };\n  }\n\n  var that = this\n  var ClientJS = path.resolve(path.dirname(module.filename), 'Daemon.js');\n  var node_args = [];\n  var out, err;\n\n  // if (process.env.TRAVIS) {\n  //   // Redirect PM2 internal err and out to STDERR STDOUT when running with Travis\n  //   out = 1;\n  //   err = 2;\n  // }\n  // else {\n  out = fs.openSync(that.conf.PM2_LOG_FILE_PATH, 'a'),\n  err = fs.openSync(that.conf.PM2_LOG_FILE_PATH, 'a');\n  //}\n\n  if (this.conf.LOW_MEMORY_ENVIRONMENT) {\n    var os = require('os');\n    node_args.push('--gc-global'); // Does full GC (smaller memory footprint)\n    node_args.push('--max-old-space-size=' + Math.floor(os.totalmem() / 1024 / 1024));\n  }\n\n  // Node.js tuning for better performance\n  //node_args.push('--expose-gc'); // Allows manual GC in the code\n\n  /**\n   * Add node [arguments] depending on PM2_NODE_OPTIONS env variable\n   */\n  if (process.env.PM2_NODE_OPTIONS)\n    node_args = node_args.concat(process.env.PM2_NODE_OPTIONS.split(' '));\n  node_args.push(ClientJS);\n\n  if (!process.env.PM2_DISCRETE_MODE)\n    Common.printOut(that.conf.PREFIX_MSG + 'Spawning PM2 daemon with pm2_home=' + this.pm2_home);\n\n  var interpreter = process.execPath;\n\n  var child = require('child_process').spawn(interpreter, node_args, {\n    detached   : true,\n    cwd        : that.conf.cwd || process.cwd(),\n    windowsHide: true,\n    env        : Object.assign({\n      'SILENT'    : that.conf.DEBUG ? !that.conf.DEBUG : true,\n      'PM2_HOME'  : that.pm2_home\n    }, process.env),\n    stdio      : [null, out, err, 'ipc']\n  });\n\n  function onError(e) {\n    console.error(e.message || e);\n    return cb ? cb(e.message || e) : false;\n  }\n\n  child.once('error', onError);\n\n  if (this.conf.IS_BUN === false)\n    child.unref();\n\n  child.once('message', function(msg) {\n    debug('PM2 daemon launched with return message: ', msg);\n    child.removeListener('error', onError);\n    child.disconnect();\n    if (opts && opts.interactor == false)\n      return cb(null, child);\n\n    if (process.env.PM2_NO_INTERACTION == 'true')\n      return cb(null, child);\n\n    /**\n     * Here the Keymetrics agent is launched automaticcaly if\n     * it has been already configured before (via pm2 link)\n     */\n    KMDaemon.launchAndInteract(that.conf, {\n      machine_name : that.machine_name,\n      public_key   : that.public_key,\n      secret_key   : that.secret_key,\n      pm2_version  : pkg.version\n    }, function(err, data, interactor_proc) {\n      that.interactor_process = interactor_proc;\n      return cb(null, child);\n    });\n  });\n};\n\n/**\n * Ping the daemon to know if it alive or not\n * @api public\n * @method pingDaemon\n * @param {} cb\n * @return\n */\nClient.prototype.pingDaemon = function pingDaemon(cb) {\n  var req    = axon.socket('req');\n  var client = new rpc.Client(req);\n  var that = this;\n\n  debug('[PING PM2] Trying to connect to server');\n\n  client.sock.once('reconnect attempt', function() {\n    client.sock.close();\n    debug('Daemon not launched');\n    process.nextTick(function() {\n      return cb(false);\n    });\n  });\n\n  client.sock.once('error', function(e) {\n    if (e.code === 'EACCES') {\n      fs.stat(that.conf.DAEMON_RPC_PORT, function(e, stats) {\n        if (stats.uid === 0) {\n          console.error(that.conf.PREFIX_MSG_ERR + 'Permission denied, to give access to current user:');\n          console.log('$ sudo chown ' + process.env.USER + ':' + process.env.USER + ' ' + that.conf.DAEMON_RPC_PORT +  ' ' + that.conf.DAEMON_PUB_PORT);\n        }\n        else\n          console.error(that.conf.PREFIX_MSG_ERR + 'Permission denied, check permissions on ' + that.conf.DAEMON_RPC_PORT);\n\n        process.exit(1);\n      });\n    }\n    else\n      console.error(e.message || e);\n  });\n\n  client.sock.once('connect', function() {\n    client.sock.once('close', function() {\n      return cb(true);\n    });\n    client.sock.close();\n    debug('Daemon alive');\n  });\n\n  req.connect(this.rpc_socket_file);\n};\n\n/**\n * Methods to interact with the Daemon via RPC\n * This method wait to be connected to the Daemon\n * Once he's connected it trigger the command parsing (on ./bin/pm2 file, at the end)\n * @method launchRPC\n * @params {function} [cb]\n * @return\n */\nClient.prototype.launchRPC = function launchRPC(cb) {\n  var self = this;\n  debug('Launching RPC client on socket file %s', this.rpc_socket_file);\n  var req      = axon.socket('req');\n  this.client  = new rpc.Client(req);\n\n  var connectHandler = function() {\n    self.client.sock.removeListener('error', errorHandler);\n    debug('RPC Connected to Daemon');\n    if (cb) {\n      setTimeout(function() {\n        cb(null);\n      }, 4);\n    }\n  };\n\n  var errorHandler = function(e) {\n    self.client.sock.removeListener('connect', connectHandler);\n    if (cb) {\n      return cb(e);\n    }\n  };\n\n  this.client.sock.once('connect', connectHandler);\n  this.client.sock.once('error', errorHandler);\n  this.client_sock = req.connect(this.rpc_socket_file);\n};\n\n/**\n * Methods to close the RPC connection\n * @callback cb\n */\nClient.prototype.disconnectRPC = function disconnectRPC(cb) {\n  var that = this;\n  if (!cb) cb = noop;\n\n  if (!this.client_sock || !this.client_sock.close) {\n    this.client = null;\n    return process.nextTick(function() {\n      cb(new Error('SUB connection to PM2 is not launched'));\n    });\n  }\n\n  if (this.client_sock.connected === false ||\n      this.client_sock.closing === true) {\n    this.client = null;\n    return process.nextTick(function() {\n      cb(new Error('RPC already being closed'));\n    });\n  }\n\n  try {\n    var timer;\n\n    that.client_sock.once('close', function() {\n      clearTimeout(timer);\n      that.client = null;\n      debug('PM2 RPC cleanly closed');\n      return cb(null, { msg : 'RPC Successfully closed' });\n    });\n\n    timer = setTimeout(function() {\n      if (that.client_sock.destroy)\n        that.client_sock.destroy();\n      that.client = null;\n      return cb(null, { msg : 'RPC Successfully closed via timeout' });\n    }, 200);\n\n    that.client_sock.close();\n  } catch(e) {\n    debug('Error while disconnecting RPC PM2', e.stack || e);\n    return cb(e);\n  }\n  return false;\n};\n\nClient.prototype.launchBus = function launchEventSystem(cb) {\n  var self = this;\n  this.sub = axon.socket('sub-emitter');\n  this.sub_sock = this.sub.connect(this.pub_socket_file);\n\n  this.sub_sock.once('connect', function() {\n    return cb(null, self.sub, self.sub_sock);\n  });\n};\n\nClient.prototype.disconnectBus = function disconnectBus(cb) {\n  if (!cb) cb = noop;\n\n  var that = this;\n\n  if (!this.sub_sock || !this.sub_sock.close) {\n    that.sub = null;\n    return process.nextTick(function() {\n      cb(null, { msg : 'bus was not connected'});\n    });\n  }\n\n  if (this.sub_sock.connected === false ||\n      this.sub_sock.closing === true) {\n    that.sub = null;\n    return process.nextTick(function() {\n      cb(new Error('SUB connection is already being closed'));\n    });\n  }\n\n  try {\n    var timer;\n\n    that.sub_sock.once('close', function() {\n      that.sub = null;\n      clearTimeout(timer);\n      debug('PM2 PUB cleanly closed');\n      return cb();\n    });\n\n    timer = setTimeout(function() {\n      if (Client.sub_sock.destroy)\n        that.sub_sock.destroy();\n      return cb();\n    }, 200);\n\n    this.sub_sock.close();\n  } catch(e) {\n    return cb(e);\n  }\n};\n\n/**\n * Description\n * @method gestExposedMethods\n * @param {} cb\n * @return\n */\nClient.prototype.getExposedMethods = function getExposedMethods(cb) {\n  this.client.methods(cb);\n};\n\n/**\n * Description\n * @method executeRemote\n * @param {} method\n * @param {} env\n * @param {} fn\n * @return\n */\nClient.prototype.executeRemote = function executeRemote(method, app_conf, fn) {\n  var self = this;\n\n  // stop watch on stop | env is the process id\n  if (method.indexOf('stop') !== -1) {\n    this.stopWatch(method, app_conf);\n  }\n  // stop watching when process is deleted\n  else if (method.indexOf('delete') !== -1) {\n    this.stopWatch(method, app_conf);\n  }\n  // stop everything on kill\n  else if (method.indexOf('kill') !== -1) {\n    this.stopWatch('deleteAll', app_conf);\n  }\n  else if (method.indexOf('restartProcessId') !== -1 && process.argv.indexOf('--watch') > -1) {\n    delete app_conf.env.current_conf.watch;\n    this.toggleWatch(method, app_conf);\n  }\n\n  if (!this.client || !this.client.call) {\n    this.start(function(error) {\n      if (error) {\n        if (fn)\n          return fn(error);\n        console.error(error);\n        return process.exit(0);\n      }\n      if (self.client) {\n        return self.client.call(method, app_conf, fn);\n      }\n    });\n    return false;\n  }\n\n  debug('Calling daemon method pm2:%s on rpc socket:%s', method, this.rpc_socket_file);\n  return this.client.call(method, app_conf, fn);\n};\n\nClient.prototype.notifyGod = function(action_name, id, cb) {\n  this.executeRemote('notifyByProcessId', {\n    id : id,\n    action_name : action_name,\n    manually : true\n  }, function() {\n    debug('God notified');\n    return cb ? cb() : false;\n  });\n};\n\nClient.prototype.killDaemon = function killDaemon(fn) {\n  var timeout;\n  var that = this;\n\n  function quit() {\n    that.close(function() {\n      return fn ? fn(null, {success:true}) : false;\n    });\n  }\n\n  // under unix, we listen for signal (that is send by daemon to notify us that its shuting down)\n  if (process.platform !== 'win32' && process.platform !== 'win64') {\n    process.once('SIGQUIT', function() {\n      debug('Received SIGQUIT from pm2 daemon');\n      clearTimeout(timeout);\n      quit();\n    });\n  }\n  else {\n    // if under windows, try to ping the daemon to see if it still here\n    setTimeout(function() {\n      that.pingDaemon(function(alive) {\n        if (!alive) {\n          clearTimeout(timeout);\n          return quit();\n        }\n      });\n    }, 250)\n  }\n\n  timeout = setTimeout(function() {\n    quit();\n  }, 3000);\n\n  // Kill daemon\n  this.executeRemote('killMe', {pid : process.pid});\n};\n\n\n/**\n * Description\n * @method toggleWatch\n * @param {String} pm2 method name\n * @param {Object} application environment, should include id\n * @param {Function} callback\n */\nClient.prototype.toggleWatch = function toggleWatch(method, env, fn) {\n  debug('Calling toggleWatch');\n  this.client.call('toggleWatch', method, env, function() {\n    return fn ? fn() : false;\n  });\n};\n\n/**\n * Description\n * @method startWatch\n * @param {String} pm2 method name\n * @param {Object} application environment, should include id\n * @param {Function} callback\n */\nClient.prototype.startWatch = function restartWatch(method, env, fn) {\n  debug('Calling startWatch');\n  this.client.call('startWatch', method, env, function() {\n    return fn ? fn() : false;\n  });\n};\n\n/**\n * Description\n * @method stopWatch\n * @param {String} pm2 method name\n * @param {Object} application environment, should include id\n * @param {Function} callback\n */\nClient.prototype.stopWatch = function stopWatch(method, env, fn) {\n  debug('Calling stopWatch');\n  this.client.call('stopWatch', method, env, function() {\n    return fn ? fn() : false;\n  });\n};\n\nClient.prototype.getAllProcess = function(cb) {\n  var found_proc = [];\n\n  this.executeRemote('getMonitorData', {}, function(err, procs) {\n    if (err) {\n      Common.printError('Error retrieving process list: ' + err);\n      return cb(err);\n    }\n\n    return cb(null, procs);\n  });\n};\n\nClient.prototype.getAllProcessId = function(cb) {\n  var found_proc = [];\n\n  this.executeRemote('getMonitorData', {}, function(err, procs) {\n    if (err) {\n      Common.printError('Error retrieving process list: ' + err);\n      return cb(err);\n    }\n\n    return cb(null, procs.map(proc => proc.pm_id));\n  });\n};\n\nClient.prototype.getAllProcessIdWithoutModules = function(cb) {\n  var found_proc = [];\n\n  this.executeRemote('getMonitorData', {}, function(err, procs) {\n    if (err) {\n      Common.printError('Error retrieving process list: ' + err);\n      return cb(err);\n    }\n\n    var proc_ids = procs\n        .filter(proc => !proc.pm2_env.pmx_module)\n        .map(proc => proc.pm_id)\n\n    return cb(null, proc_ids);\n  });\n};\n\nClient.prototype.getProcessIdByName = function(name, force_all, cb) {\n  var found_proc   = [];\n  var full_details = {};\n\n  if (typeof(cb) === 'undefined') {\n    cb = force_all;\n    force_all = false;\n  }\n\n  if (typeof(name) == 'number')\n    name = name.toString();\n\n  this.executeRemote('getMonitorData', {}, function(err, list) {\n    if (err) {\n      Common.printError('Error retrieving process list: ' + err);\n      return cb(err);\n    }\n\n    list.forEach(function(proc) {\n      if (proc.pm2_env.name == name || proc.pm2_env.pm_exec_path == path.resolve(name)) {\n        found_proc.push(proc.pm_id);\n        full_details[proc.pm_id] = proc;\n      }\n    });\n\n    return cb(null, found_proc, full_details);\n  });\n};\n\nClient.prototype.getProcessIdsByNamespace = function(namespace, force_all, cb) {\n  var found_proc   = [];\n  var full_details = {};\n\n  if (typeof(cb) === 'undefined') {\n    cb = force_all;\n    force_all = false;\n  }\n\n  if (typeof(namespace) == 'number')\n    namespace = namespace.toString();\n\n  this.executeRemote('getMonitorData', {}, function(err, list) {\n    if (err) {\n      Common.printError('Error retrieving process list: ' + err);\n      return cb(err);\n    }\n\n    list.forEach(function(proc) {\n      if (proc.pm2_env.namespace == namespace) {\n        found_proc.push(proc.pm_id);\n        full_details[proc.pm_id] = proc;\n      }\n    });\n\n    return cb(null, found_proc, full_details);\n  });\n};\n\nClient.prototype.getProcessByName = function(name, cb) {\n  var found_proc = [];\n\n  this.executeRemote('getMonitorData', {}, function(err, list) {\n    if (err) {\n      Common.printError('Error retrieving process list: ' + err);\n      return cb(err);\n    }\n\n    list.forEach(function(proc) {\n      if (proc.pm2_env.name == name ||\n          proc.pm2_env.pm_exec_path == path.resolve(name)) {\n        found_proc.push(proc);\n      }\n    });\n\n    return cb(null, found_proc);\n  });\n};\n\nClient.prototype.getProcessByNameOrId = function (nameOrId, cb) {\n  var foundProc = [];\n\n  this.executeRemote('getMonitorData', {}, function (err, list) {\n    if (err) {\n      Common.printError('Error retrieving process list: ' + err);\n      return cb(err);\n    }\n\n    list.forEach(function (proc) {\n      if (proc.pm2_env.name === nameOrId ||\n        proc.pm2_env.pm_exec_path === path.resolve(nameOrId) ||\n        proc.pid === parseInt(nameOrId) ||\n        proc.pm2_env.pm_id === parseInt(nameOrId)) {\n        foundProc.push(proc);\n      }\n    });\n\n    return cb(null, foundProc);\n  });\n};\n"
  },
  {
    "path": "lib/Common.js",
    "content": "/**\n * Copyright 2013-2022 the PM2 project authors. All rights reserved.\n * Use of this source code is governed by a license that\n * can be found in the LICENSE file.\n */\n\n/**\n * Common Utilities ONLY USED IN ->CLI<-\n */\n\nvar fs        = require('fs');\nvar path      = require('path');\nvar os        = require('os');\nvar util      = require('util');\nvar chalk     = require('ansis');\nvar fclone    = require('fclone');\nvar semver    = require('semver');\nvar dayjs     = require('dayjs');\nvar execSync  = require('child_process').execSync;\nvar isBinary  = require('./tools/isbinaryfile.js');\nvar cst       = require('../constants.js');\nvar extItps   = require('./API/interpreter.json');\nvar Config    = require('./tools/Config');\nvar pkg       = require('../package.json');\nvar which     = require('./tools/which.js');\nvar Common = module.exports;\n\nfunction homedir() {\n  var env = process.env;\n  var home = env.HOME;\n  var user = env.LOGNAME || env.USER || env.LNAME || env.USERNAME;\n\n  if (process.platform === 'win32') {\n    return env.USERPROFILE || env.HOMEDRIVE + env.HOMEPATH || home || null;\n  }\n\n  if (process.platform === 'darwin') {\n    return home || (user ? '/Users/' + user : null);\n  }\n\n  if (process.platform === 'linux') {\n    return home || (process.getuid() === 0 ? '/root' : (user ? '/home/' + user : null));\n  }\n\n  return home || null;\n}\n\nfunction resolveHome(filepath) {\n  if (filepath[0] === '~') {\n    return path.join(homedir(), filepath.slice(1));\n  }\n  return filepath;\n}\n\nCommon.determineSilentCLI = function() {\n  // pm2 should ignore -s --silent -v if they are after '--'\n  var variadicArgsDashesPos = process.argv.indexOf('--');\n  var s1opt = process.argv.indexOf('--silent')\n  var s2opt = process.argv.indexOf('-s')\n\n  if (process.env.PM2_SILENT || (variadicArgsDashesPos > -1 &&\n       (s1opt != -1 && s1opt < variadicArgsDashesPos) &&\n       (s2opt != -1 != s2opt < variadicArgsDashesPos)) ||\n      (variadicArgsDashesPos == -1 && (s1opt > -1 || s2opt > -1))) {\n    for (var key in console){\n      var code = key.charCodeAt(0);\n      if (code >= 97 && code <= 122){\n        console[key] = function(){};\n      }\n    }\n    process.env.PM2_DISCRETE_MODE = true;\n  }\n}\n\nCommon.printVersion = function() {\n  var variadicArgsDashesPos = process.argv.indexOf('--');\n\n  if (process.argv.indexOf('-v') > -1 && process.argv.indexOf('-v') < variadicArgsDashesPos) {\n    console.log(pkg.version);\n    process.exit(0);\n  }\n}\n\nCommon.lockReload = function() {\n  try {\n    var t1 = fs.readFileSync(cst.PM2_RELOAD_LOCKFILE).toString();\n\n    // Check if content and if time < 30 return locked\n    // Else if content detected (lock file staled), allow and rewritte\n    if (t1 && t1 != '') {\n      var diff = dayjs().diff(parseInt(t1));\n      if (diff < cst.RELOAD_LOCK_TIMEOUT)\n        return diff;\n    }\n  } catch(e) {}\n\n  try {\n    // Write latest timestamp\n    fs.writeFileSync(cst.PM2_RELOAD_LOCKFILE, dayjs().valueOf().toString());\n    return 0;\n  } catch(e) {\n    console.error(e.message || e);\n  }\n};\n\nCommon.unlockReload = function() {\n  try {\n    fs.writeFileSync(cst.PM2_RELOAD_LOCKFILE, '');\n  } catch(e) {\n    console.error(e.message || e);\n  }\n};\n\n/**\n * Resolve app paths and replace missing values with defaults.\n * @method prepareAppConf\n * @param app {Object}\n * @param {} cwd\n * @param {} outputter\n * @return app\n */\nCommon.prepareAppConf = function(opts, app) {\n  /**\n   * Minimum validation\n   */\n  if (!app.script)\n    return new Error('No script path - aborting');\n\n  var cwd = null;\n\n  if (app.cwd) {\n    cwd = path.resolve(app.cwd);\n    process.env.PWD = app.cwd;\n  }\n\n  if (!app.node_args) {\n    app.node_args = [];\n  }\n\n  if (app.port && app.env) {\n    app.env.PORT = app.port;\n  }\n\n  // CWD option resolving\n  cwd && (cwd[0] != '/') && (cwd = path.resolve(process.cwd(), cwd));\n  cwd = cwd || opts.cwd;\n\n  // Full path script resolution\n  app.pm_exec_path = path.resolve(cwd, app.script);\n\n  // If script does not exist after resolution\n  if (!fs.existsSync(app.pm_exec_path)) {\n    var ckd;\n    // Try resolve command available in $PATH\n    if ((ckd = which(app.script))) {\n      if (typeof(ckd) !== 'string')\n        ckd = ckd.toString();\n      app.pm_exec_path = ckd;\n    }\n    else\n      // Throw critical error\n      return new Error(`Script not found: ${app.pm_exec_path}`);\n  }\n\n  /**\n   * Auto detect .map file and enable source map support automatically\n   */\n  if (app.disable_source_map_support != true) {\n    try {\n      fs.accessSync(app.pm_exec_path + '.map', fs.constants.R_OK);\n      app.source_map_support = true;\n    } catch(e) {}\n    delete app.disable_source_map_support;\n  }\n\n  delete app.script;\n\n  // Set current env by first adding the process environment and then extending/replacing it\n  // with env specified on command-line or JSON file.\n\n  var env = {};\n\n  /**\n   * Do not copy internal pm2 environment variables if acting on process\n   * is made from a programmatic script started by PM2 or if a pm_id is present in env\n   */\n  if (cst.PM2_PROGRAMMATIC || process.env.pm_id)\n    Common.safeExtend(env, process.env);\n  else\n    env = process.env;\n\n  function filterEnv (envObj) {\n    if (app.filter_env == true)\n      return {}\n\n    if (typeof app.filter_env === 'string') {\n      delete envObj[app.filter_env]\n      return envObj\n    }\n\n    var new_env = {};\n    var allowedKeys = app.filter_env.reduce((acc, current) =>\n                                            acc.filter( item => !item.includes(current)), Object.keys(envObj))\n    allowedKeys.forEach( key => new_env[key] = envObj[key]);\n    return new_env\n  }\n\n  app.env = [\n    {}, (app.filter_env && app.filter_env.length > 0) ? filterEnv(process.env) : env, app.env || {}\n  ].reduce(function(e1, e2){\n    return Object.assign(e1, e2);\n  });\n\n  app.pm_cwd = cwd;\n  // Interpreter\n  try {\n    Common.sink.resolveInterpreter(app);\n  } catch(e) {\n    return e\n  }\n\n  // Exec mode and cluster stuff\n  Common.sink.determineExecMode(app);\n\n  /**\n   * Scary\n   */\n  var formated_app_name = app.name.replace(/[^a-zA-Z0-9\\\\.\\\\-]/g, '-');\n\n  ['log', 'out', 'error', 'pid'].forEach(function(f){\n    var af = app[f + '_file'], ps, ext = (f == 'pid' ? 'pid':'log'), isStd = !~['log', 'pid'].indexOf(f);\n    if (af) af = resolveHome(af);\n\n    if ((f == 'log' && typeof af == 'boolean' && af) || (f != 'log' && !af)) {\n      ps = [cst['DEFAULT_' + ext.toUpperCase() + '_PATH'], formated_app_name + (isStd ? '-' + f : '') + '.' + ext];\n    } else if ((f != 'log' || (f == 'log' && af)) && af !== 'NULL' && af !== '/dev/null') {\n      ps = [cwd, af];\n\n      var dir = path.dirname(path.resolve(cwd, af));\n      if (!fs.existsSync(dir)) {\n        Common.printError(cst.PREFIX_MSG_WARNING + 'Folder does not exist: ' + dir);\n        Common.printOut(cst.PREFIX_MSG + 'Creating folder: ' + dir);\n        try {\n          require('mkdirp').sync(dir);\n        } catch (err) {\n          Common.printError(cst.PREFIX_MSG_ERR + 'Could not create folder: ' + path.dirname(af));\n          throw new Error('Could not create folder');\n        }\n      }\n\n    }\n    // PM2 paths\n    if (af !== 'NULL' && af !== '/dev/null') {\n      ps && (app['pm_' + (isStd ? f.substr(0, 3) + '_' : '') + ext + '_path'] = path.resolve.apply(null, ps));\n    } else if (path.sep === '\\\\') {\n      app['pm_' + (isStd ? f.substr(0, 3) + '_' : '') + ext + '_path'] = '\\\\\\\\.\\\\NUL';\n    } else {\n      app['pm_' + (isStd ? f.substr(0, 3) + '_' : '') + ext + '_path'] = '/dev/null';\n    }\n    delete app[f + '_file'];\n  });\n\n  return app;\n};\n\n/**\n * Definition of known config file extensions with their type\n */\nCommon.knonwConfigFileExtensions = {\n  '.json': 'json',\n  '.yml': 'yaml',\n  '.yaml': 'yaml',\n  '.config.js': 'js',\n  '.config.cjs': 'js',\n  '.config.mjs': 'mjs'\n}\n\n/**\n * Check if filename is a configuration file\n * @param {string} filename\n * @return {mixed} null if not conf file, json or yaml if conf\n */\nCommon.isConfigFile = function (filename) {\n  if (typeof (filename) !== 'string')\n    return null;\n\n  for (let extension in Common.knonwConfigFileExtensions) {\n    if (filename.indexOf(extension) !== -1) {\n      return Common.knonwConfigFileExtensions[extension];\n    }\n  }\n\n  return null;\n};\n\nCommon.getConfigFileCandidates = function (name) {\n  return Object.keys(Common.knonwConfigFileExtensions).map((extension) => name + extension);\n}\n\n/**\n * Parses a config file like ecosystem.config.js. Supported formats: JS, JSON, JSON5, YAML.\n * @param {string} confString  contents of the config file\n * @param {string} filename    path to the config file\n * @return {Object} config object\n */\nCommon.parseConfig = function(confObj, filename) {\n  var yamljs = require('js-yaml');\n  var vm     = require('vm');\n\n  var isConfigFile = Common.isConfigFile(filename);\n\n  if (!filename ||\n      filename == 'pipe' ||\n      filename == 'none' ||\n      isConfigFile == 'json') {\n    var code = '(' + confObj + ')';\n    var sandbox = {};\n\n    return vm.runInThisContext(code, sandbox, {\n      filename: path.resolve(filename),\n      displayErrors: false,\n      timeout: 1000\n    });\n  }\n  else if (isConfigFile == 'yaml') {\n    return yamljs.load(confObj.toString());\n  }\n  else if (isConfigFile == 'js' || isConfigFile == 'mjs') {\n    var confPath = require.resolve(path.resolve(filename));\n    delete require.cache[confPath];\n    return require(confPath);\n  }\n};\n\nCommon.retErr = function(e) {\n  if (!e)\n    return new Error('Unidentified error');\n  if (e instanceof Error)\n    return e;\n  return new Error(e);\n}\n\nCommon.sink = {};\n\nCommon.sink.determineCron = function(app) {\n  if (app.cron_restart == 0 || app.cron_restart == '0') {\n    Common.printOut(cst.PREFIX_MSG + 'disabling cron restart');\n    return\n  }\n\n  if (app.cron_restart) {\n    const Croner = require('croner');\n\n    try {\n      Common.printOut(cst.PREFIX_MSG + 'cron restart at ' + app.cron_restart);\n      Croner(app.cron_restart);\n    } catch(ex) {\n      return new Error(`Cron pattern error: ${ex.message}`);\n    }\n  }\n};\n\n/**\n * Handle alias (fork <=> fork_mode, cluster <=> cluster_mode)\n */\nCommon.sink.determineExecMode = function(app) {\n  if (app.exec_mode)\n    app.exec_mode = app.exec_mode.replace(/^(fork|cluster)$/, '$1_mode');\n\n  /**\n   * Here we put the default exec mode\n   */\n  if (!app.exec_mode &&\n      (app.instances >= 1 || app.instances === 0 || app.instances === -1) &&\n      (app.exec_interpreter.includes('node') === true || app.exec_interpreter.includes('bun') === true)) {\n    app.exec_mode = 'cluster_mode';\n  } else if (!app.exec_mode) {\n    app.exec_mode = 'fork_mode';\n  }\n  if (typeof app.instances == 'undefined')\n    app.instances = 1;\n};\n\nvar resolveNodeInterpreter = function(app) {\n  if (app.exec_mode && app.exec_mode.indexOf('cluster') > -1) {\n    Common.printError(cst.PREFIX_MSG_WARNING + chalk.bold.yellow('Choosing the Node.js version in cluster mode is not supported'));\n    return false;\n  }\n\n  var nvm_path = cst.IS_WINDOWS ? process.env.NVM_HOME : process.env.NVM_DIR;\n  if (!nvm_path) {\n    Common.printError(cst.PREFIX_MSG_ERR + chalk.red('NVM is not available in PATH'));\n    Common.printError(cst.PREFIX_MSG_ERR + chalk.red('Fallback to node in PATH'));\n    var msg = cst.IS_WINDOWS\n      ? 'https://github.com/coreybutler/nvm-windows/releases/'\n      : '$ curl https://raw.githubusercontent.com/creationix/nvm/master/install.sh | bash';\n    Common.printOut(cst.PREFIX_MSG_ERR + chalk.bold('Install NVM:\\n' + msg));\n  }\n  else {\n    var node_version  = app.exec_interpreter.split('@')[1];\n    var path_to_node  = cst.IS_WINDOWS\n      ? '/v' + node_version + '/node.exe'\n      : semver.satisfies(node_version, '>= 0.12.0')\n          ? '/versions/node/v' + node_version + '/bin/node'\n          : '/v' + node_version + '/bin/node';\n    var nvm_node_path  = path.join(nvm_path, path_to_node);\n    try {\n      fs.accessSync(nvm_node_path);\n    } catch(e) {\n      Common.printOut(cst.PREFIX_MSG + 'Installing Node v%s', node_version);\n      var nvm_bin = path.join(nvm_path, 'nvm.' + (cst.IS_WINDOWS ? 'exe' : 'sh'));\n      var nvm_cmd = cst.IS_WINDOWS\n        ? nvm_bin + ' install ' + node_version\n        : '. ' + nvm_bin + ' ; nvm install ' + node_version;\n\n      Common.printOut(cst.PREFIX_MSG + 'Executing: %s', nvm_cmd);\n\n      execSync(nvm_cmd, {\n        cwd: path.resolve(process.cwd()),\n        env: process.env,\n        maxBuffer: 20 * 1024 * 1024\n      });\n\n      // in order to support both arch, nvm for Windows renames 'node.exe' to:\n      // 'node32.exe' for x32 arch\n      // 'node64.exe' for x64 arch\n      if (cst.IS_WINDOWS)\n        nvm_node_path = nvm_node_path.replace(/node/, 'node' + process.arch.slice(1))\n    }\n\n    Common.printOut(cst.PREFIX_MSG + chalk.green.bold('Setting Node to v%s (path=%s)'),\n                    node_version,\n                    nvm_node_path);\n\n    app.exec_interpreter = nvm_node_path;\n  }\n};\n\n/**\n * Resolve interpreter\n */\nCommon.sink.resolveInterpreter = function(app) {\n  var noInterpreter = !app.exec_interpreter;\n  var extName = path.extname(app.pm_exec_path);\n  var betterInterpreter = extItps[extName];\n\n  // Bun support\n  if (noInterpreter && (extName == '.js' || extName == '.ts') && cst.IS_BUN === true) {\n    noInterpreter = false\n    app.exec_interpreter = process.execPath;\n  }\n\n  // No interpreter defined and correspondance in schema hashmap\n  if (noInterpreter && betterInterpreter) {\n    app.exec_interpreter = betterInterpreter;\n\n    if (betterInterpreter == \"python\") {\n      if (which('python') == null) {\n        if (which('python3') == null)\n          Common.printError(cst.PREFIX_MSG_WARNING + chalk.bold.yellow('python and python3 binaries not available in PATH'));\n        else\n          app.exec_interpreter = 'python3';\n      }\n    }\n  }\n  // Else if no Interpreter detect if process is binary\n  else if (noInterpreter) {\n    app.exec_interpreter = isBinary(app.pm_exec_path) ? 'none' : process.execPath;\n  }\n  else if (app.exec_interpreter.indexOf('node@') > -1)\n    resolveNodeInterpreter(app);\n\n  if (app.exec_interpreter.indexOf('python') > -1)\n    app.env.PYTHONUNBUFFERED = '1'\n\n  if (app.exec_interpreter == 'lsc') {\n    app.exec_interpreter = path.resolve(__dirname, '../node_modules/.bin/lsc');\n  }\n\n  if (app.exec_interpreter == 'coffee') {\n    app.exec_interpreter = path.resolve(__dirname, '../node_modules/.bin/coffee');\n  }\n\n  if (app.exec_interpreter != 'none' && which(app.exec_interpreter) == null) {\n    // If node is not present\n    if (app.exec_interpreter == 'node') {\n      Common.warn(`Using builtin node.js version on version ${process.version}`)\n      app.exec_interpreter = cst.BUILTIN_NODE_PATH\n    }\n    else\n      throw new Error(`Interpreter ${app.exec_interpreter} is NOT AVAILABLE in PATH. (type 'which ${app.exec_interpreter}' to double check.)`)\n  }\n\n  return app;\n};\n\nCommon.deepCopy = Common.serialize = Common.clone = function(obj) {\n  if (obj === null || obj === undefined) return {};\n  return fclone(obj);\n};\n\nCommon.errMod = function(msg) {\n  if (process.env.PM2_SILENT || process.env.PM2_PROGRAMMATIC === 'true') return false;\n  if (msg instanceof Error)\n    return console.error(msg.message);\n  return console.error(`${cst.PREFIX_MSG_MOD_ERR}${msg}`);\n}\n\nCommon.err = function(msg) {\n  if (process.env.PM2_SILENT || process.env.PM2_PROGRAMMATIC === 'true') return false;\n  if (msg instanceof Error)\n    return console.error(`${cst.PREFIX_MSG_ERR}${msg.message}`);\n  return console.error(`${cst.PREFIX_MSG_ERR}${msg}`);\n}\n\nCommon.printError = function(msg) {\n  if (process.env.PM2_SILENT || process.env.PM2_PROGRAMMATIC === 'true') return false;\n  if (msg instanceof Error)\n    return console.error(msg.message);\n  return console.error.apply(console, arguments);\n};\n\nCommon.log = function(msg) {\n  if (process.env.PM2_SILENT || process.env.PM2_PROGRAMMATIC === 'true') return false;\n  return console.log(`${cst.PREFIX_MSG}${msg}`);\n}\n\nCommon.info = function(msg) {\n  if (process.env.PM2_SILENT || process.env.PM2_PROGRAMMATIC === 'true') return false;\n  return console.log(`${cst.PREFIX_MSG_INFO}${msg}`);\n}\n\nCommon.warn = function(msg) {\n  if (process.env.PM2_SILENT || process.env.PM2_PROGRAMMATIC === 'true') return false;\n  return console.log(`${cst.PREFIX_MSG_WARNING}${msg}`);\n}\n\nCommon.logMod = function(msg) {\n  if (process.env.PM2_SILENT || process.env.PM2_PROGRAMMATIC === 'true') return false;\n  return console.log(`${cst.PREFIX_MSG_MOD}${msg}`);\n}\n\nCommon.printOut = function() {\n  if (process.env.PM2_SILENT === 'true' || process.env.PM2_PROGRAMMATIC === 'true') return false;\n  return console.log.apply(console, arguments);\n};\n\n\n/**\n * Raw extend\n */\nCommon.extend = function(destination, source) {\n  if (typeof destination !== 'object') {\n    destination = {};\n  }\n  if (!source || typeof source !== 'object') {\n    return destination;\n  }\n\n  Object.keys(source).forEach(function(new_key) {\n    if (source[new_key] != '[object Object]')\n      destination[new_key] = source[new_key];\n  });\n\n  return destination;\n};\n\n/**\n * This is useful when starting script programmatically\n */\nCommon.safeExtend = function(origin, add){\n  if (!add || typeof add != 'object') return origin;\n\n  //Ignore PM2's set environment variables from the nested env\n  var keysToIgnore = ['name', 'exec_mode', 'env', 'args', 'pm_cwd', 'exec_interpreter', 'pm_exec_path', 'node_args', 'pm_out_log_path', 'pm_err_log_path', 'pm_pid_path', 'pm_id', 'status', 'pm_uptime', 'created_at', 'windowsHide', 'username', 'merge_logs', 'kill_retry_time', 'prev_restart_delay', 'instance_var', 'unstable_restarts', 'restart_time', 'axm_actions', 'pmx_module', 'command', 'watch', 'filter_env', 'versioning', 'vizion_runing', 'MODULE_DEBUG', 'pmx', 'axm_options', 'created_at', 'watch', 'vizion', 'axm_dynamic', 'axm_monitor', 'instances', 'automation', 'autostart', 'autorestart', 'stop_exit_codes', 'unstable_restart', 'treekill', 'exit_code', 'vizion'];\n\n  var keys = Object.keys(add);\n  var i = keys.length;\n  while (i--) {\n  \t//Only copy stuff into the env that we don't have already.\n  \tif(keysToIgnore.indexOf(keys[i]) == -1 && add[keys[i]] != '[object Object]')\n      origin[keys[i]] = add[keys[i]];\n  }\n  return origin;\n};\n\n\n/**\n * Extend the app.env object of with the properties taken from the\n * app.env_[envName] and deploy configuration.\n * Also update current json attributes\n *\n * Used only for Configuration file processing\n *\n * @param {Object} app The app object.\n * @param {string} envName The given environment name.\n * @param {Object} deployConf Deployment configuration object (from JSON file or whatever).\n * @returns {Object} The app.env variables object.\n */\nCommon.mergeEnvironmentVariables = function(app_env, env_name, deploy_conf) {\n  var app = fclone(app_env);\n\n  var new_conf = {\n    env : {}\n  }\n\n  // Stringify possible object\n  for (var key in app.env) {\n    if (typeof app.env[key] == 'object') {\n      app.env[key] = JSON.stringify(app.env[key]);\n    }\n  }\n\n  /**\n   * Extra configuration update\n   */\n  Object.assign(new_conf, app);\n\n  if (env_name) {\n    // First merge variables from deploy.production.env object as least priority.\n    if (deploy_conf && deploy_conf[env_name] && deploy_conf[env_name]['env']) {\n      Object.assign(new_conf.env, deploy_conf[env_name]['env']);\n    }\n\n    Object.assign(new_conf.env, app.env);\n\n    // Then, last and highest priority, merge the app.env_production object.\n    if ('env_' + env_name in app) {\n      Object.assign(new_conf.env, app['env_' + env_name]);\n    }\n    else {\n      Common.printOut(cst.PREFIX_MSG_WARNING + chalk.bold('Environment [%s] is not defined in process file'), env_name);\n    }\n  }\n\n  delete new_conf.exec_mode\n\n  var res = {\n    current_conf: {}\n  }\n\n  Object.assign(res, new_conf.env);\n  Object.assign(res.current_conf, new_conf);\n\n  // #2541 force resolution of node interpreter\n  if (app.exec_interpreter &&\n      app.exec_interpreter.indexOf('@') > -1) {\n    resolveNodeInterpreter(app);\n    res.current_conf.exec_interpreter = app.exec_interpreter\n  }\n\n  return res\n}\n\n/**\n * This function will resolve paths, option and environment\n * CALLED before 'prepare' God call (=> PROCESS INITIALIZATION)\n * @method resolveAppAttributes\n * @param {Object} opts\n * @param {Object} opts.cwd\n * @param {Object} opts.pm2_home\n * @param {Object} appConf application configuration\n * @return app\n */\nCommon.resolveAppAttributes = function(opts, conf) {\n  var conf_copy = fclone(conf);\n\n  var app = Common.prepareAppConf(opts, conf_copy);\n  if (app instanceof Error) {\n    throw new Error(app.message);\n  }\n  return app;\n}\n\n/**\n * Verify configurations\n * Called on EVERY Operation (start/restart/reload/stop...)\n * @param {Array} appConfs\n * @returns {Array}\n */\nCommon.verifyConfs = function(appConfs) {\n  if (!appConfs || appConfs.length == 0) {\n    return [];\n  }\n\n  // Make sure it is an Array.\n  appConfs = [].concat(appConfs);\n\n  var verifiedConf = [];\n\n  for (var i = 0; i < appConfs.length; i++) {\n    var app = appConfs[i];\n\n    if (app.exec_mode)\n      app.exec_mode = app.exec_mode.replace(/^(fork|cluster)$/, '$1_mode');\n\n    // JSON conf: alias cmd to script\n    if (app.cmd && !app.script) {\n      app.script = app.cmd\n      delete app.cmd\n    }\n    // JSON conf: alias command to script\n    if (app.command && !app.script) {\n      app.script = app.command\n      delete app.command\n    }\n\n    if (!app.env) {\n      app.env = {}\n    }\n\n    // Render an app name if not existing.\n    Common.renderApplicationName(app);\n\n    if (app.execute_command == true) {\n      app.exec_mode = 'fork'\n      delete app.execute_command\n    }\n\n    app.username = Common.getCurrentUsername();\n\n    /**\n     * If command is like pm2 start \"python xx.py --ok\"\n     * Then automatically start the script with bash -c and set a name eq to command\n     */\n    if (app.script && app.script.indexOf(' ') > -1 && cst.IS_WINDOWS === false) {\n      var _script = app.script;\n\n      if (which('bash')) {\n        app.script = 'bash';\n        app.args = ['-c', _script];\n        if (!app.name) {\n          app.name = _script\n        }\n      }\n      else if (which('sh')) {\n        app.script = 'sh';\n        app.args = ['-c', _script];\n        if (!app.name) {\n          app.name = _script\n        }\n      }\n      else {\n        warn('bash or sh not available in $PATH, keeping script as is')\n      }\n    }\n\n    /**\n     * Add log_date_format by default\n     */\n    if (app.time || process.env.ASZ_MODE) {\n      app.log_date_format = 'YYYY-MM-DDTHH:mm:ss'\n    }\n\n    /**\n     * Checks + Resolve UID/GID\n     * comes from pm2 --uid <> --gid <> or --user\n     */\n    if (app.uid || app.gid || app.user) {\n      // 1/ Check if windows\n      if (cst.IS_WINDOWS === true) {\n        Common.printError(cst.PREFIX_MSG_ERR + '--uid and --git does not works on windows');\n        return new Error('--uid and --git does not works on windows');\n      }\n\n      // 2/ Verify that user is root (todo: verify if other has right)\n      if (process.env.NODE_ENV != 'test' && process.getuid && process.getuid() !== 0) {\n        Common.printError(cst.PREFIX_MSG_ERR + 'To use --uid and --gid please run pm2 as root');\n        return new Error('To use UID and GID please run PM2 as root');\n      }\n\n      // 3/ Resolve user info via /etc/password\n      var passwd = require('./tools/passwd.js')\n      var users\n      try {\n        users = passwd.getUsers()\n      } catch(e) {\n        Common.printError(e);\n        return new Error(e);\n      }\n\n      var user_info = users[app.uid || app.user]\n      if (!user_info) {\n        Common.printError(`${cst.PREFIX_MSG_ERR} User ${app.uid || app.user} cannot be found`);\n        return new Error(`${cst.PREFIX_MSG_ERR} User ${app.uid || app.user} cannot be found`);\n      }\n\n      app.env.HOME = user_info.homedir\n      app.uid = parseInt(user_info.userId)\n\n      // 4/ Resolve group id if gid is specified\n      if (app.gid) {\n        var groups\n        try {\n          groups = passwd.getGroups()\n        } catch(e) {\n          Common.printError(e);\n          return new Error(e);\n        }\n        var group_info = groups[app.gid]\n        if (!group_info) {\n          Common.printError(`${cst.PREFIX_MSG_ERR} Group ${app.gid} cannot be found`);\n          return new Error(`${cst.PREFIX_MSG_ERR} Group ${app.gid} cannot be found`);\n        }\n        app.gid = parseInt(group_info.id)\n      } else {\n        app.gid = parseInt(user_info.groupId)\n      }\n    }\n\n    /**\n     * Specific options of PM2.io\n     */\n    if (process.env.PM2_DEEP_MONITORING) {\n      app.deep_monitoring = true;\n    }\n\n    if (app.automation == false) {\n      app.pmx = false;\n    }\n\n    if (app.disable_trace) {\n      app.trace = false\n      delete app.disable_trace;\n    }\n\n    /**\n     * Instances params\n     */\n    if (app.instances == 'max') {\n      app.instances = 0;\n    }\n\n    if (typeof(app.instances) === 'string') {\n      app.instances = parseInt(app.instances) || 0;\n    }\n\n    if (app.exec_mode != 'cluster_mode' &&\n        !app.instances &&\n        typeof(app.merge_logs) == 'undefined') {\n      app.merge_logs = true;\n    }\n\n    var ret;\n\n    if (app.cron_restart) {\n      if ((ret = Common.sink.determineCron(app)) instanceof Error)\n        return ret;\n    }\n\n    /**\n     * Now validation configuration\n     */\n    var ret = Config.validateJSON(app);\n    if (ret.errors && ret.errors.length > 0){\n      ret.errors.forEach(function(err) { warn(err) });\n      return new Error(ret.errors);\n    }\n\n    verifiedConf.push(ret.config);\n  }\n\n  return verifiedConf;\n}\n\n/**\n * Get current username\n * Called on EVERY starting app\n *\n * @returns {String}\n */\nCommon.getCurrentUsername = function(){\n  var current_user = '';\n\n  if (os.userInfo) {\n    try {\n      current_user = os.userInfo().username;\n    } catch (err) {\n      // For the case of unhandled error for uv_os_get_passwd\n      // https://github.com/Unitech/pm2/issues/3184\n    }\n  }\n\n  if(current_user === '') {\n    current_user = process.env.USER || process.env.LNAME || process.env.USERNAME || process.env.SUDO_USER || process.env.C9_USER || process.env.LOGNAME;\n  }\n\n  return current_user;\n}\n\n/**\n * Render an app name if not existing.\n * @param {Object} conf\n */\nCommon.renderApplicationName = function(conf){\n  if (!conf.name && conf.script){\n    conf.name = conf.script !== undefined ? path.basename(conf.script) : 'undefined';\n    var lastDot = conf.name.lastIndexOf('.');\n    if (lastDot > 0){\n      conf.name = conf.name.slice(0, lastDot);\n    }\n  }\n}\n\n/**\n * Show warnings\n * @param {String} warning\n */\nfunction warn(warning){\n  Common.printOut(cst.PREFIX_MSG_WARNING + warning);\n}\n"
  },
  {
    "path": "lib/Configuration.js",
    "content": "/**\n * Copyright 2013-2022 the PM2 project authors. All rights reserved.\n * Use of this source code is governed by a license that\n * can be found in the LICENSE file.\n */\n\nvar Configuration = module.exports = {};\n\nvar fs            = require('fs');\n\nvar Common        = require('./Common');\nvar eachSeries    = require('async/eachSeries');\nvar cst           = require('../constants.js');\n\nfunction splitKey(key) {\n  var values = [key];\n\n  if (key.indexOf('.') > -1)\n    values = key.match(/(?:[^.\"]+|\"[^\"]*\")+/g).map(function(dt) { return dt.replace(/\"/g, '') });\n  else if (key.indexOf(':') > -1)\n    values = key.match(/(?:[^:\"]+|\"[^\"]*\")+/g).map(function(dt) { return dt.replace(/\"/g, '') });\n\n  return values;\n}\n\nfunction serializeConfiguration(json_conf) {\n  return JSON.stringify(json_conf, null, 4)\n}\n\nConfiguration.set = function(key, value, cb) {\n  fs.readFile(cst.PM2_MODULE_CONF_FILE, function(err, data) {\n    if (err) return cb(err);\n\n    var json_conf = JSON.parse(data);\n\n    var values = splitKey(key);\n\n    if (values.length > 0) {\n      var levels = values;\n\n      var tmp = json_conf;\n\n      levels.forEach(function(key, index) {\n        if (index == levels.length -1)\n          tmp[key] = value;\n        else if (!tmp[key]) {\n          tmp[key] = {};\n          tmp = tmp[key];\n        }\n        else {\n          if (typeof(tmp[key]) != 'object')\n            tmp[key] = {};\n          tmp = tmp[key];\n        }\n      });\n\n    }\n    else {\n      if (json_conf[key] && typeof(json_conf[key]) === 'string')\n        Common.printOut(cst.PREFIX_MSG + 'Replacing current value key %s by %s', key, value);\n\n      json_conf[key] = value;\n    }\n\n    fs.writeFile(cst.PM2_MODULE_CONF_FILE, serializeConfiguration(json_conf), function(err, data) {\n      if (err) return cb(err);\n\n      return cb(null, json_conf);\n    });\n    return false;\n  });\n};\n\nConfiguration.unset = function(key, cb) {\n  fs.readFile(cst.PM2_MODULE_CONF_FILE, function(err, data) {\n    if (err) return cb(err);\n\n    var json_conf = JSON.parse(data);\n\n    var values = splitKey(key);\n\n    if (values.length > 0) {\n      var levels = values;\n\n      var tmp = json_conf;\n\n      levels.forEach(function(key, index) {\n        if (index == levels.length -1)\n          delete tmp[key];\n        else if (!tmp[key]) {\n          tmp[key] = {};\n          tmp = tmp[key];\n        }\n        else {\n          if (typeof(tmp[key]) != 'object')\n            tmp[key] = {};\n          tmp = tmp[key];\n        }\n      });\n\n    }\n    else\n      delete json_conf[key];\n\n    if (err) return cb(err);\n\n    if (key === 'all')\n      json_conf = {};\n\n    fs.writeFile(cst.PM2_MODULE_CONF_FILE, serializeConfiguration(json_conf), function(err, data) {\n      if (err) return cb(err);\n\n      return cb(null, json_conf);\n    });\n    return false;\n  });\n}\n\nConfiguration.setSyncIfNotExist = function(key, value) {\n  try {\n    var conf = JSON.parse(fs.readFileSync(cst.PM2_MODULE_CONF_FILE));\n  } catch(e) {\n    return null;\n  }\n\n  var values = splitKey(key);\n  var exists = false;\n\n  if (values.length > 1 && conf && conf[values[0]]) {\n    exists = Object.keys(conf[values[0]]).some(function(key) {\n      if (key == values[1])\n        return true;\n      return false;\n    });\n  }\n\n  if (exists === false)\n    return Configuration.setSync(key, value);\n\n  return null;\n};\n\nConfiguration.setSync = function(key, value) {\n  try {\n    var data = fs.readFileSync(cst.PM2_MODULE_CONF_FILE);\n  } catch(e) {\n    return null;\n  }\n\n  var json_conf = JSON.parse(data);\n\n  var values = splitKey(key);\n\n  if (values.length > 0) {\n    var levels = values;\n\n    var tmp = json_conf;\n\n    levels.forEach(function(key, index) {\n      if (index == levels.length -1)\n        tmp[key] = value;\n      else if (!tmp[key]) {\n        tmp[key] = {};\n        tmp = tmp[key];\n      }\n      else {\n        if (typeof(tmp[key]) != 'object')\n          tmp[key] = {};\n        tmp = tmp[key];\n      }\n    });\n\n  }\n  else {\n    if (json_conf[key] && typeof(json_conf[key]) === 'string')\n      Common.printOut(cst.PREFIX_MSG + 'Replacing current value key %s by %s', key, value);\n\n    json_conf[key] = value;\n  }\n\n  if (key === 'all')\n    json_conf = {};\n\n  try {\n    fs.writeFileSync(cst.PM2_MODULE_CONF_FILE, serializeConfiguration(json_conf));\n    return json_conf;\n  } catch(e) {\n    console.error(e.message);\n    return null;\n  }\n};\n\nConfiguration.unsetSync = function(key) {\n  try {\n    var data = fs.readFileSync(cst.PM2_MODULE_CONF_FILE);\n  } catch(e) {\n    return null;\n  }\n\n  var json_conf = JSON.parse(data);\n\n  var values = splitKey(key);\n\n  if (values.length > 0) {\n    var levels = values;\n\n    var tmp = json_conf;\n\n    levels.forEach(function(key, index) {\n      if (index == levels.length -1)\n        delete tmp[key];\n      else if (!tmp[key]) {\n        tmp[key] = {};\n        tmp = tmp[key];\n      }\n      else {\n        if (typeof(tmp[key]) != 'object')\n          tmp[key] = {};\n        tmp = tmp[key];\n      }\n    });\n\n  }\n  else\n    delete json_conf[key];\n\n  if (key === 'all')\n    json_conf = {};\n\n  try {\n    fs.writeFileSync(cst.PM2_MODULE_CONF_FILE, serializeConfiguration(json_conf));\n  } catch(e) {\n    console.error(e.message);\n    return null;\n  }\n};\n\nConfiguration.multiset = function(serial, cb) {\n  var arrays = [];\n  serial = serial.match(/(?:[^ \"]+|\"[^\"]*\")+/g);\n\n  while (serial.length > 0)\n    arrays.push(serial.splice(0, 2));\n\n  eachSeries(arrays, function(el, next) {\n    Configuration.set(el[0], el[1], next);\n  }, cb);\n};\n\nConfiguration.get = function(key, cb) {\n  Configuration.getAll(function(err, data) {\n    var climb = splitKey(key);\n\n    climb.some(function(val) {\n      if (!data[val]) {\n        data = null;\n        return true;\n      }\n      data = data[val];\n      return false;\n    });\n\n    if (!data) return cb({err : 'Unknown key'}, null);\n    return cb(null, data);\n  });\n};\n\nConfiguration.getSync = function(key) {\n  try {\n    var data = Configuration.getAllSync();\n  } catch(e) {\n    return null;\n  }\n\n  var climb = splitKey(key);\n\n  climb.some(function(val) {\n    if (!data[val]) {\n      data = null;\n      return true;\n    }\n    data = data[val];\n    return false;\n  });\n\n  if (!data) return null;\n  return data;\n};\n\nConfiguration.getAll = function(cb) {\n  fs.readFile(cst.PM2_MODULE_CONF_FILE, function(err, data) {\n    if (err) return cb(err);\n    return cb(null, JSON.parse(data));\n  });\n};\n\nConfiguration.getAllSync = function() {\n  try {\n    return JSON.parse(fs.readFileSync(cst.PM2_MODULE_CONF_FILE));\n  } catch(e) {\n    console.error(e.stack || e);\n    return {};\n  }\n};\n"
  },
  {
    "path": "lib/Daemon.js",
    "content": "/**\n * Copyright 2013-2022 the PM2 project authors. All rights reserved.\n * Use of this source code is governed by a license that\n * can be found in the LICENSE file.\n */\n\nvar debug        = require('debug')('pm2:daemon');\nvar pkg          = require('../package.json');\nvar cst          = require('../constants.js');\nvar rpc          = require('pm2-axon-rpc');\nvar axon         = require('pm2-axon');\nvar domain       = require('domain');\nvar Utility      = require('./Utility.js');\nvar util         = require('util');\nvar fs           = require('fs');\nvar God          = require('./God');\nvar eachLimit    = require('async/eachLimit');\nvar fmt          = require('./tools/fmt.js');\nvar semver       = require('semver');\n\nvar Daemon = module.exports = function(opts) {\n  if (!opts) opts = {};\n\n  this.ignore_signals = opts.ignore_signals || false;\n  this.rpc_socket_ready = false;\n  this.pub_socket_ready = false;\n\n  this.pub_socket_file = opts.pub_socket_file || cst.DAEMON_PUB_PORT;\n  this.rpc_socket_file = opts.rpc_socket_file || cst.DAEMON_RPC_PORT;\n\n  this.pid_path        = opts.pid_file || cst.PM2_PID_FILE_PATH;\n};\n\nDaemon.prototype.start = function() {\n  var that = this;\n  var d = domain.create();\n\n  d.once('error', function(err) {\n    fmt.sep();\n    fmt.title('PM2 global error caught');\n    fmt.field('Time', new Date());\n    console.error(err.message);\n    console.error(err.stack);\n    fmt.sep();\n\n    console.error('[PM2] Resurrecting PM2');\n\n\t\tvar path = cst.IS_WINDOWS ? __dirname + '/../bin/pm2' : process.env['_'];\n    var fork_new_pm2 = require('child_process').spawn('node', [path, 'update'], {\n      detached: true,\n      windowsHide: true,\n      stdio: 'inherit'\n    });\n\n    fork_new_pm2.on('close', function() {\n      console.log('PM2 successfully forked');\n      process.exit(0);\n    })\n\n  });\n\n  d.run(function() {\n    that.innerStart();\n  });\n}\n\nDaemon.prototype.innerStart = function(cb) {\n  var that = this;\n\n  if (!cb) cb = function() {\n    fmt.sep();\n    fmt.title('New PM2 Daemon started');\n    fmt.field('Time', new Date());\n    fmt.field('PM2 version', pkg.version);\n    fmt.field('Node.js version', process.versions.node);\n    fmt.field('Current arch', process.arch);\n    fmt.field('PM2 home', cst.PM2_HOME);\n    fmt.field('PM2 PID file', that.pid_path);\n    fmt.field('RPC socket file', that.rpc_socket_file);\n    fmt.field('BUS socket file', that.pub_socket_file);\n    fmt.field('Application log path', cst.DEFAULT_LOG_PATH);\n    fmt.field('Worker Interval', cst.WORKER_INTERVAL);\n    fmt.field('Process dump file', cst.DUMP_FILE_PATH);\n    fmt.field('Concurrent actions', cst.CONCURRENT_ACTIONS);\n    fmt.field('SIGTERM timeout', cst.KILL_TIMEOUT);\n    fmt.field('Runtime Binary', process.execPath);\n    fmt.sep();\n  };\n\n  // Write Daemon PID into file\n  try {\n    fs.writeFileSync(that.pid_path, process.pid.toString());\n  } catch (e) {\n    console.error(e.stack || e);\n  }\n\n  if (this.ignore_signals != true)\n    this.handleSignals();\n\n  /**\n   * Pub system for real time notifications\n   */\n  this.pub    = axon.socket('pub-emitter');\n\n  this.pub_socket = this.pub.bind(this.pub_socket_file);\n\n  this.pub_socket.once('bind', function() {\n    fs.chmod(that.pub_socket_file, '775', function(e) {\n      if (e) console.error(e);\n\n      try {\n        if (process.env.PM2_SOCKET_USER && process.env.PM2_SOCKET_GROUP)\n          fs.chown(that.pub_socket_file,\n                   parseInt(process.env.PM2_SOCKET_USER),\n                   parseInt(process.env.PM2_SOCKET_GROUP), function(e) {\n                     if (e) console.error(e);\n                   });\n      } catch(e) {\n        console.error(e);\n      }\n    });\n\n    that.pub_socket_ready = true;\n    that.sendReady(cb);\n  });\n\n  /**\n   * Rep/Req - RPC system to interact with God\n   */\n  this.rep    = axon.socket('rep');\n\n  var server = new rpc.Server(this.rep);\n\n  this.rpc_socket = this.rep.bind(this.rpc_socket_file);\n\n  this.rpc_socket.once('bind', function() {\n    fs.chmod(that.rpc_socket_file, '775', function(e) {\n      if (e) console.error(e);\n\n      try {\n        if (process.env.PM2_SOCKET_USER && process.env.PM2_SOCKET_GROUP)\n          fs.chown(that.rpc_socket_file,\n                   parseInt(process.env.PM2_SOCKET_USER),\n                   parseInt(process.env.PM2_SOCKET_GROUP), function(e) {\n                     if (e) console.error(e);\n                   });\n      } catch(e) {\n        console.error(e);\n      }\n    });\n\n\n    that.rpc_socket_ready = true;\n    that.sendReady(cb);\n  });\n\n\n  /**\n   * Memory Snapshot\n   */\n  function profile(type, msg, cb) {\n    if (semver.satisfies(process.version, '< 8'))\n      return cb(null, { error: 'Node.js is not on right version' })\n\n    var cmd\n\n    if (type === 'cpu') {\n      cmd = {\n        enable: 'Profiler.enable',\n        start: 'Profiler.start',\n        stop: 'Profiler.stop',\n        disable: 'Profiler.disable'\n      }\n    }\n    if (type == 'mem') {\n      cmd = {\n        enable: 'HeapProfiler.enable',\n        start: 'HeapProfiler.startSampling',\n        stop: 'HeapProfiler.stopSampling',\n        disable: 'HeapProfiler.disable'\n      }\n    }\n\n    const inspector = require('inspector')\n    var session = new inspector.Session()\n\n    session.connect()\n\n    var timeout = msg.timeout || 5000\n\n    session.post(cmd.enable, (err, data) => {\n      if (err) return cb(null, { error: err.message || err })\n\n      console.log(`Starting ${cmd.start}`)\n      session.post(cmd.start, (err, data) => {\n        if (err) return cb(null, { error: err.message || err })\n\n        setTimeout(() => {\n          session.post(cmd.stop, (err, data) => {\n            if (err) return cb(null, { error: err.message || err })\n            const profile = data.profile\n\n            console.log(`Stopping ${cmd.stop}`)\n            session.post(cmd.disable)\n\n            fs.writeFile(msg.pwd, JSON.stringify(profile), (err) => {\n              if (err) return cb(null, { error: err.message || err })\n              return cb(null, { file : msg.pwd })\n            })\n          })\n        }, timeout)\n      })\n    })\n  }\n\n  server.expose({\n    killMe                  : that.close.bind(this),\n    profileCPU              : profile.bind(this, 'cpu'),\n    profileMEM              : profile.bind(this, 'mem'),\n    prepare                 : God.prepare,\n    getMonitorData          : God.getMonitorData,\n\n    startProcessId          : God.startProcessId,\n    stopProcessId           : God.stopProcessId,\n    restartProcessId        : God.restartProcessId,\n    deleteProcessId         : God.deleteProcessId,\n\n    sendLineToStdin         : God.sendLineToStdin,\n    softReloadProcessId     : God.softReloadProcessId,\n    reloadProcessId         : God.reloadProcessId,\n    duplicateProcessId      : God.duplicateProcessId,\n    resetMetaProcessId      : God.resetMetaProcessId,\n    stopWatch               : God.stopWatch,\n    startWatch              : God.startWatch,\n    toggleWatch             : God.toggleWatch,\n    notifyByProcessId       : God.notifyByProcessId,\n\n    notifyKillPM2           : God.notifyKillPM2,\n    monitor                 : God.monitor,\n    unmonitor               : God.unmonitor,\n\n    msgProcess              : God.msgProcess,\n    sendDataToProcessId     : God.sendDataToProcessId,\n    sendSignalToProcessId   : God.sendSignalToProcessId,\n    sendSignalToProcessName : God.sendSignalToProcessName,\n\n    ping                    : God.ping,\n    getVersion              : God.getVersion,\n    getReport               : God.getReport,\n    reloadLogs              : God.reloadLogs\n  });\n\n  this.startLogic();\n}\n\nDaemon.prototype.close = function(opts, cb) {\n  var that = this;\n\n  God.bus.emit('pm2:kill', {\n    status : 'killed',\n    msg    : 'pm2 has been killed via CLI'\n  });\n\n  if (God.system_infos_proc !== null)\n    God.system_infos_proc.kill()\n\n  /**\n   * Cleanly kill pm2\n   */\n  that.rpc_socket.close(function() {\n    that.pub_socket.close(function() {\n\n      // notify cli that the daemon is shuting down (only under unix since windows doesnt handle signals)\n      if (cst.IS_WINDOWS === false) {\n        try {\n          process.kill(parseInt(opts.pid), 'SIGQUIT');\n        } catch(e) {\n          console.error('Could not send SIGQUIT to CLI');\n        }\n      }\n\n      try {\n        fs.unlinkSync(that.pid_path);\n      } catch(e) {}\n\n      console.log('PM2 successfully stopped');\n      setTimeout(function() {\n        process.exit(cst.SUCCESS_EXIT);\n      }, 2);\n    });\n  });\n}\n\nDaemon.prototype.handleSignals = function() {\n  var that = this;\n\n  process.on('SIGTERM', that.gracefullExit.bind(this));\n  process.on('SIGINT', that.gracefullExit.bind(this));\n  process.on('SIGHUP', function() {});\n  process.on('SIGQUIT', that.gracefullExit.bind(this));\n  process.on('SIGUSR2', function() {\n    God.reloadLogs({}, function() {});\n  });\n}\n\nDaemon.prototype.sendReady = function(cb) {\n  // Send ready message to Client\n  if (this.rpc_socket_ready == true && this.pub_socket_ready == true) {\n    cb(null, {\n      pid         : process.pid,\n      pm2_version : pkg.version\n    });\n    if (typeof(process.send) != 'function')\n      return false;\n\n    process.send({\n      online      : true,\n      success     : true,\n      pid         : process.pid,\n      pm2_version : pkg.version\n    });\n  };\n}\n\nDaemon.prototype.gracefullExit = function() {\n  var that = this;\n\n  // never execute multiple gracefullExit simultaneously\n  // this can lead to loss of some apps in dump file\n  if (this.isExiting) return\n\n  this.isExiting = true\n\n  God.bus.emit('pm2:kill', {\n    status : 'killed',\n    msg    : 'pm2 has been killed by SIGNAL'\n  });\n\n  console.log('pm2 has been killed by signal, dumping process list before exit...');\n\n  if (God.system_infos_proc !== null)\n    God.system_infos_proc.kill()\n\n  God.dumpProcessList(function() {\n\n    var processes = God.getFormatedProcesses();\n\n    eachLimit(processes, 1, function(proc, next) {\n      console.log('Deleting process %s', proc.pm2_env.pm_id);\n      God.deleteProcessId(proc.pm2_env.pm_id, function() {\n        return next();\n      });\n    }, function(err) {\n      try {\n        fs.unlinkSync(that.pid_path);\n      } catch(e) {}\n      setTimeout(function() {\n        that.isExiting = false\n        console.log('Exited peacefully');\n        process.exit(cst.SUCCESS_EXIT);\n      }, 2);\n    });\n  });\n}\n\nDaemon.prototype.startLogic = function() {\n  var that = this;\n\n  /**\n   * Action treatment specifics\n   * Attach actions to pm2_env.axm_actions variables (name + options)\n   */\n  God.bus.on('axm:action', function axmActions(msg) {\n    var pm2_env = msg.process;\n    var exists  = false;\n    var axm_action = msg.data;\n\n    if (!pm2_env || !God.clusters_db[pm2_env.pm_id])\n      return console.error('AXM ACTION Unknown id %s', pm2_env.pm_id);\n\n    if (!God.clusters_db[pm2_env.pm_id].pm2_env.axm_actions)\n      God.clusters_db[pm2_env.pm_id].pm2_env.axm_actions = [];\n\n    God.clusters_db[pm2_env.pm_id].pm2_env.axm_actions.forEach(function(actions) {\n      if (actions.action_name == axm_action.action_name)\n        exists = true;\n    });\n\n    if (exists === false) {\n      debug('Adding action', axm_action);\n      God.clusters_db[pm2_env.pm_id].pm2_env.axm_actions.push(axm_action);\n    }\n    msg = null;\n  });\n\n  /**\n   * Configure module\n   */\n  God.bus.on('axm:option:configuration', function axmMonitor(msg) {\n    if (!msg.process)\n      return console.error('[axm:option:configuration] no process defined');\n\n    if (!God.clusters_db[msg.process.pm_id])\n      return console.error('[axm:option:configuration] Unknown id %s', msg.process.pm_id);\n\n    try {\n      // Application Name nverride\n      if (msg.data.name)\n        God.clusters_db[msg.process.pm_id].pm2_env.name = msg.data.name;\n\n      Object.keys(msg.data).forEach(function(conf_key) {\n        God.clusters_db[msg.process.pm_id].pm2_env.axm_options[conf_key] = Utility.clone(msg.data[conf_key]);\n      });\n    } catch(e) {\n      console.error(e.stack || e);\n    }\n    msg = null;\n  });\n\n  /**\n   * Process monitoring data (probes)\n   */\n  God.bus.on('axm:monitor', function axmMonitor(msg) {\n    if (!msg.process)\n      return console.error('[axm:monitor] no process defined');\n\n    if (!msg.process || !God.clusters_db[msg.process.pm_id])\n      return console.error('AXM MONITOR Unknown id %s', msg.process.pm_id);\n\n    Object.assign(God.clusters_db[msg.process.pm_id].pm2_env.axm_monitor, Utility.clone(msg.data));\n    msg = null;\n  });\n\n  /**\n   * Broadcast messages\n   */\n  God.bus.onAny(function(event, data_v) {\n    if (['axm:action',\n         'axm:monitor',\n         'axm:option:setPID',\n         'axm:option:configuration'].indexOf(event) > -1) {\n      data_v = null;\n      return false;\n    }\n    that.pub.emit(event, Utility.clone(data_v));\n    data_v = null;\n  });\n};\n\nif (require.main === module) {\n  process.title = process.env.PM2_DAEMON_TITLE || 'PM2 v' + pkg.version + ': God Daemon (' + process.env.PM2_HOME + ')';\n\n  var daemon = new Daemon();\n\n  daemon.start();\n}\n"
  },
  {
    "path": "lib/Event.js",
    "content": "/**\n * Copyright 2013-2022 the PM2 project authors. All rights reserved.\n * Use of this source code is governed by a license that\n * can be found in the LICENSE file.\n */\n\nvar Utility       = require('./Utility.js');\n\nmodule.exports = function(God) {\n\n  God.notify = function(action_name, data, manually) {\n    God.bus.emit('process:event', {\n      event      : action_name,\n      manually   : typeof(manually) == 'undefined' ? false : true,\n      process    : Utility.formatCLU(data),\n      at         : Utility.getDate()\n    });\n  };\n\n  God.notifyByProcessId = function(opts, cb) {\n    if (typeof(opts.id) === 'undefined') { return cb(new Error('process id missing')); }\n    var proc = God.clusters_db[opts.id];\n    if (!proc) { return cb(new Error('process id doesnt exists')); }\n\n    God.bus.emit('process:event', {\n      event      : opts.action_name,\n      manually   : typeof(opts.manually) == 'undefined' ? false : true,\n      process    : Utility.formatCLU(proc),\n      at         : Utility.getDate()\n    });\n\n    process.nextTick(function() {\n      return cb ? cb(null) : false;\n    });\n    return false;\n  };\n};\n"
  },
  {
    "path": "lib/God/ActionMethods.js",
    "content": "/**\n * Copyright 2013-2022 the PM2 project authors. All rights reserved.\n * Use of this source code is governed by a license that\n * can be found in the LICENSE file.\n */\n'use strict';\n\n/**\n * @file ActionMethod like restart, stop, monitor... are here\n * @author Alexandre Strzelewicz <as@unitech.io>\n * @project PM2\n */\n\nvar fs            = require('fs');\nvar path          = require('path');\nvar eachLimit     = require('async/eachLimit');\nvar os            = require('os');\nvar p             = path;\nvar cst           = require('../../constants.js');\nvar pkg           = require('../../package.json');\nvar pidusage      = require('pidusage');\nvar util          = require('util');\nvar debug         = require('debug')('pm2:ActionMethod');\nvar Utility       = require('../Utility');\n\n/**\n * Description\n * @method exports\n * @param {} God\n * @return\n */\nmodule.exports = function(God) {\n  /**\n   * Description\n   * @method getMonitorData\n   * @param {} env\n   * @param {} cb\n   * @return\n   */\n  God.getMonitorData = function getMonitorData(env, cb) {\n    var processes = God.getFormatedProcesses();\n    var pids = processes.filter(filterBadProcess)\n      .map(function(pro, i) {\n        var pid = getProcessId(pro)\n        return pid;\n      })\n\n    // No pids, return empty statistics\n    if (pids.length === 0) {\n      return cb(null, processes.map(function(pro) {\n        pro['monit'] = {\n          memory : 0,\n          cpu : 0\n        };\n\n        return pro\n      }))\n    }\n\n    pidusage(pids, function retPidUsage(err, statistics) {\n      // Just log, we'll set empty statistics\n      if (err) {\n        console.error('Error caught while calling pidusage');\n        console.error(err);\n\n        return cb(null, processes.map(function(pro) {\n          pro['monit'] = {\n            memory : 0,\n            cpu : 0\n          };\n          return pro\n        }))\n      }\n\n      if (!statistics) {\n        console.error('Statistics is not defined!')\n\n        return cb(null, processes.map(function(pro) {\n          pro['monit'] = {\n            memory : 0,\n            cpu : 0\n          };\n          return pro\n        }))\n      }\n\n      processes = processes.map(function(pro) {\n        if (filterBadProcess(pro) === false) {\n          pro['monit'] = {\n            memory : 0,\n            cpu : 0\n          };\n\n          return pro;\n        }\n\n        var pid = getProcessId(pro);\n        var stat = statistics[pid];\n\n        if (!stat) {\n          pro['monit'] = {\n            memory : 0,\n            cpu : 0\n          };\n\n          return pro;\n        }\n\n        pro['monit'] = {\n          memory: stat.memory,\n          cpu: Math.round(stat.cpu * 10) / 10\n        };\n\n        return pro;\n      });\n\n      cb(null, processes);\n    });\n  };\n\n  /**\n   * Description\n   * @method dumpProcessList\n   * @param {} cb\n   * @return\n   */\n  God.dumpProcessList = function(cb) {\n    var process_list = [];\n    var apps         = Utility.clone(God.getFormatedProcesses());\n    var that = this;\n\n    // Don't override the actual dump file if process list is empty\n    // unless user explicitely did `pm2 dump`.\n    // This often happens when PM2 crashed, we don't want to override\n    // the dump file with an empty list of process.\n    if (!apps[0]) {\n      debug('[PM2] Did not override dump file because list of processes is empty');\n      return cb(null, {success:true, process_list: process_list});\n    }\n\n    function fin(err) {\n\n      // try to fix issues with empty dump file\n      // like #3485\n      if (process_list.length === 0) {\n\n        // fix : if no dump file, no process, only module and after pm2 update\n        if (!fs.existsSync(cst.DUMP_FILE_PATH) && typeof that.clearDump === 'function') {\n          that.clearDump(function(){});\n        }\n\n        // if no process in list don't modify dump file\n        // process list should not be empty\n        return cb(null, {success:true, process_list: process_list});\n      }\n\n      // Back up dump file\n      try {\n        if (fs.existsSync(cst.DUMP_FILE_PATH)) {\n          fs.writeFileSync(cst.DUMP_BACKUP_FILE_PATH, fs.readFileSync(cst.DUMP_FILE_PATH));\n        }\n      } catch (e) {\n        console.error(e.stack || e);\n      }\n\n      // Overwrite dump file, delete if broken\n      try {\n        fs.writeFileSync(cst.DUMP_FILE_PATH, JSON.stringify(process_list));\n      } catch (e) {\n        console.error(e.stack || e);\n        try {\n          // try to backup file\n          if (fs.existsSync(cst.DUMP_BACKUP_FILE_PATH)) {\n            fs.writeFileSync(cst.DUMP_FILE_PATH, fs.readFileSync(cst.DUMP_BACKUP_FILE_PATH));\n          }\n        } catch (e) {\n          // don't keep broken file\n          fs.unlinkSync(cst.DUMP_FILE_PATH);\n          console.error(e.stack || e);\n        }\n      }\n\n      return cb(null, {success:true, process_list: process_list});\n    }\n\n    function saveProc(apps) {\n      if (!apps[0])\n        return fin(null);\n      delete apps[0].pm2_env.instances;\n      delete apps[0].pm2_env.pm_id;\n      // Do not dump modules\n      if (!apps[0].pm2_env.pmx_module)\n        process_list.push(apps[0].pm2_env);\n      apps.shift();\n      return saveProc(apps);\n    }\n    saveProc(apps);\n  };\n\n  /**\n   * Description\n   * @method ping\n   * @param {} env\n   * @param {} cb\n   * @return CallExpression\n   */\n  God.ping = function(env, cb) {\n    return cb(null, {msg : 'pong'});\n  };\n\n  /**\n   * Description\n   * @method notifyKillPM2\n   */\n  God.notifyKillPM2 = function() {\n    God.pm2_being_killed = true;\n  };\n\n  /**\n   * Duplicate a process\n   * @method duplicateProcessId\n   * @param {} id\n   * @param {} cb\n   * @return CallExpression\n   */\n  God.duplicateProcessId = function(id, cb) {\n    if (!(id in God.clusters_db))\n      return cb(God.logAndGenerateError(id + ' id unknown'), {});\n\n    if (!God.clusters_db[id] || !God.clusters_db[id].pm2_env)\n      return cb(God.logAndGenerateError('Error when getting proc || proc.pm2_env'), {});\n\n    var proc = Utility.clone(God.clusters_db[id].pm2_env);\n\n\n    delete proc.created_at;\n    delete proc.pm_id;\n    delete proc.unique_id;\n\n    // generate a new unique id for new process\n    proc.unique_id = Utility.generateUUID()\n\n    God.injectVariables(proc, function inject (_err, proc) {\n      return God.executeApp(Utility.clone(proc), function (err, clu) {\n        if (err) return cb(err);\n        God.notify('start', clu, true);\n        return cb(err, Utility.clone(clu));\n      });\n    });\n  };\n\n  /**\n   * Start a stopped process by ID\n   * @method startProcessId\n   * @param {} id\n   * @param {} cb\n   * @return CallExpression\n   */\n  God.startProcessId = function(id, cb) {\n    if (!(id in God.clusters_db))\n      return cb(God.logAndGenerateError(id + ' id unknown'), {});\n\n    var proc = God.clusters_db[id];\n    if (proc.pm2_env.status == cst.ONLINE_STATUS)\n      return cb(God.logAndGenerateError('process already online'), {});\n    if (proc.pm2_env.status == cst.LAUNCHING_STATUS)\n      return cb(God.logAndGenerateError('process already started'), {});\n    if (proc.process && proc.process.pid)\n      return cb(God.logAndGenerateError('Process with pid ' + proc.process.pid + ' already exists'), {});\n\n    return God.executeApp(God.clusters_db[id].pm2_env, function(err, proc) {\n      return cb(err, Utility.clone(proc));\n    });\n  };\n\n\n  /**\n   * Stop a process and set it on state 'stopped'\n   * @method stopProcessId\n   * @param {} id\n   * @param {} cb\n   * @return Literal\n   */\n  God.stopProcessId = function(id, cb) {\n    if (typeof id == 'object' && 'id' in id)\n      id = id.id;\n\n    if (!(id in God.clusters_db))\n      return cb(God.logAndGenerateError(id + ' : id unknown'), {});\n\n    var proc     = God.clusters_db[id];\n\n    //clear time-out restart task\n    clearTimeout(proc.pm2_env.restart_task);\n\n    if (proc.pm2_env.status == cst.STOPPED_STATUS) {\n      proc.process.pid = 0;\n      return cb(null, God.getFormatedProcess(id));\n    }\n    // state == 'none' means that the process is not online yet\n    if (proc.state && proc.state === 'none')\n      return setTimeout(function() { God.stopProcessId(id, cb); }, 250);\n\n    console.log('Stopping app:%s id:%s', proc.pm2_env.name, proc.pm2_env.pm_id);\n    proc.pm2_env.status = cst.STOPPING_STATUS;\n\n    if (!proc.process.pid) {\n      console.error('app=%s id=%d does not have a pid', proc.pm2_env.name, proc.pm2_env.pm_id);\n      proc.pm2_env.status = cst.STOPPED_STATUS;\n      return cb(null, { error : true, message : 'could not kill process w/o pid'});\n    }\n\n    God.killProcess(proc.process.pid, proc.pm2_env, function(err) {\n      proc.pm2_env.status = cst.STOPPED_STATUS;\n\n      God.notify('exit', proc);\n\n      if (err && err.type && err.type === 'timeout') {\n        console.error('app=%s id=%d pid=%s could not be stopped',\n                      proc.pm2_env.name,\n                      proc.pm2_env.pm_id,\n                      proc.process.pid);\n        proc.pm2_env.status = cst.ERRORED_STATUS;\n        return cb(null, God.getFormatedProcess(id));\n      }\n\n      if (proc.pm2_env.pm_id.toString().indexOf('_old_') !== 0) {\n        try {\n          fs.unlinkSync(proc.pm2_env.pm_pid_path);\n        } catch (e) {}\n      }\n\n      if (proc.pm2_env.axm_actions) proc.pm2_env.axm_actions = [];\n      if (proc.pm2_env.axm_monitor) proc.pm2_env.axm_monitor = {};\n\n      proc.process.pid = 0;\n      return cb(null, God.getFormatedProcess(id));\n    });\n  };\n\n  God.resetMetaProcessId = function(id, cb) {\n    if (!(id in God.clusters_db))\n      return cb(God.logAndGenerateError(id + ' id unknown'), {});\n\n    if (!God.clusters_db[id] || !God.clusters_db[id].pm2_env)\n      return cb(God.logAndGenerateError('Error when getting proc || proc.pm2_env'), {});\n\n    God.clusters_db[id].pm2_env.created_at = Utility.getDate();\n    God.clusters_db[id].pm2_env.unstable_restarts = 0;\n    God.clusters_db[id].pm2_env.restart_time = 0;\n\n    return cb(null, God.getFormatedProcesses());\n  };\n\n  /**\n   * Delete a process by id\n   * It will stop it and remove it from the database\n   * @method deleteProcessId\n   * @param {} id\n   * @param {} cb\n   * @return Literal\n   */\n  God.deleteProcessId = function(id, cb) {\n    God.deleteCron(id);\n\n    God.stopProcessId(id, function(err, proc) {\n      if (err) return cb(God.logAndGenerateError(err), {});\n      // ! transform to slow object\n      delete God.clusters_db[id];\n\n      if (Object.keys(God.clusters_db).length == 0)\n        God.next_id = 0;\n      return cb(null, proc);\n    });\n    return false;\n  };\n\n  /**\n   * Restart a process ID\n   * If the process is online it will not put it on state stopped\n   * but directly kill it and let God restart it\n   * @method restartProcessId\n   * @param {} id\n   * @param {} cb\n   * @return Literal\n   */\n  God.restartProcessId = function(opts, cb) {\n    var id = opts.id;\n    var env = opts.env || {};\n\n    if (typeof(id) === 'undefined')\n      return cb(God.logAndGenerateError('opts.id not passed to restartProcessId', opts));\n    if (!(id in God.clusters_db))\n      return cb(God.logAndGenerateError('God db process id unknown'), {});\n\n    var proc = God.clusters_db[id];\n\n    God.resetState(proc.pm2_env);\n    God.deleteCron(id);\n\n    /**\n     * Merge new application configuration on restart\n     * Same system in reloadProcessId and softReloadProcessId\n     */\n    Utility.extend(proc.pm2_env.env, env);\n    Utility.extendExtraConfig(proc, opts);\n\n    if (God.pm2_being_killed) {\n      return cb(God.logAndGenerateError('[RestartProcessId] PM2 is being killed, stopping restart procedure...'));\n    }\n    if (proc.pm2_env.status === cst.ONLINE_STATUS || proc.pm2_env.status === cst.LAUNCHING_STATUS) {\n      God.stopProcessId(id, function(err) {\n        if (God.pm2_being_killed)\n          return cb(God.logAndGenerateError('[RestartProcessId] PM2 is being killed, stopping restart procedure...'));\n        proc.pm2_env.restart_time += 1;\n        return God.startProcessId(id, cb);\n      });\n\n      return false;\n    }\n    else {\n      debug('[restart] process not online, starting it');\n      return God.startProcessId(id, cb);\n    }\n  };\n\n\n  /**\n   * Restart all process by name\n   * @method restartProcessName\n   * @param {} name\n   * @param {} cb\n   * @return Literal\n   */\n  God.restartProcessName = function(name, cb) {\n    var processes = God.findByName(name);\n\n    if (processes && processes.length === 0)\n      return cb(God.logAndGenerateError('Unknown process'), {});\n\n    eachLimit(processes, cst.CONCURRENT_ACTIONS, function(proc, next) {\n      if (God.pm2_being_killed)\n        return next('[Watch] PM2 is being killed, stopping restart procedure...');\n      if (proc.pm2_env.status === cst.ONLINE_STATUS)\n        return God.restartProcessId({id:proc.pm2_env.pm_id}, next);\n      else if (proc.pm2_env.status !== cst.STOPPING_STATUS\n               && proc.pm2_env.status !== cst.LAUNCHING_STATUS)\n        return God.startProcessId(proc.pm2_env.pm_id, next);\n      else\n        return next(util.format('[Watch] Process name %s is being stopped so I won\\'t restart it', name));\n    }, function(err) {\n      if (err) return cb(God.logAndGenerateError(err));\n      return cb(null, God.getFormatedProcesses());\n    });\n\n    return false;\n  };\n\n  /**\n   * Send system signal to process id\n   * @method sendSignalToProcessId\n   * @param {} opts\n   * @param {} cb\n   * @return CallExpression\n   */\n  God.sendSignalToProcessId = function(opts, cb) {\n    var id = opts.process_id;\n    var signal = opts.signal;\n\n    if (!(id in God.clusters_db))\n      return cb(God.logAndGenerateError(id + ' id unknown'), {});\n\n    var proc = God.clusters_db[id];\n\n    //God.notify('send signal ' + signal, proc, true);\n\n    try {\n      process.kill(God.clusters_db[id].process.pid, signal);\n    } catch(e) {\n      return cb(God.logAndGenerateError('Error when sending signal (signal unknown)'), {});\n    }\n    return cb(null, God.getFormatedProcesses());\n  };\n\n  /**\n   * Send system signal to all processes by name\n   * @method sendSignalToProcessName\n   * @param {} opts\n   * @param {} cb\n   * @return\n   */\n  God.sendSignalToProcessName = function(opts, cb) {\n    var processes = God.findByName(opts.process_name);\n    var signal    = opts.signal;\n\n    if (processes && processes.length === 0)\n      return cb(God.logAndGenerateError('Unknown process name'), {});\n\n    eachLimit(processes, cst.CONCURRENT_ACTIONS, function(proc, next) {\n      if (proc.pm2_env.status == cst.ONLINE_STATUS || proc.pm2_env.status == cst.LAUNCHING_STATUS) {\n        try {\n          process.kill(proc.process.pid, signal);\n        } catch(e) {\n          return next(e);\n        }\n      }\n      return setTimeout(next, 200);\n    }, function(err) {\n      if (err) return cb(God.logAndGenerateError(err), {});\n      return cb(null, God.getFormatedProcesses());\n    });\n\n  };\n\n  /**\n   * Stop watching daemon\n   * @method stopWatch\n   * @param {} method\n   * @param {} value\n   * @param {} fn\n   * @return\n   */\n  God.stopWatch = function(method, value, fn) {\n    var env = null;\n\n    if (method == 'stopAll' || method == 'deleteAll') {\n      var processes = God.getFormatedProcesses();\n\n      processes.forEach(function(proc) {\n        God.clusters_db[proc.pm_id].pm2_env.watch = false;\n        God.watch.disable(proc.pm2_env);\n      });\n\n    } else {\n\n      if (method.indexOf('ProcessId') !== -1) {\n        env = God.clusters_db[value];\n      } else if (method.indexOf('ProcessName') !== -1) {\n        env = God.clusters_db[God.findByName(value)];\n      }\n\n      if (env) {\n        God.watch.disable(env.pm2_env);\n        env.pm2_env.watch = false;\n      }\n    }\n    return fn(null, {success:true});\n  };\n\n\n  /**\n   * Toggle watching daemon\n   * @method toggleWatch\n   * @param {String} method\n   * @param {Object} application environment, should include id\n   * @param {Function} callback\n   */\n  God.toggleWatch = function(method, value, fn) {\n    var env = null;\n\n    if (method == 'restartProcessId') {\n      env = God.clusters_db[value.id];\n    } else if(method == 'restartProcessName') {\n      env = God.clusters_db[God.findByName(value)];\n    }\n\n    if (env) {\n      env.pm2_env.watch = !env.pm2_env.watch;\n      if (env.pm2_env.watch)\n        God.watch.enable(env.pm2_env);\n      else\n        God.watch.disable(env.pm2_env);\n    }\n\n    return fn(null, {success:true});\n  };\n\n  /**\n   * Start Watch\n   * @method startWatch\n   * @param {String} method\n   * @param {Object} application environment, should include id\n   * @param {Function} callback\n   */\n  God.startWatch = function(method, value, fn) {\n    var env = null;\n\n    if (method == 'restartProcessId') {\n      env = God.clusters_db[value.id];\n    } else if(method == 'restartProcessName') {\n      env = God.clusters_db[God.findByName(value)];\n    }\n\n    if (env) {\n      if (env.pm2_env.watch)\n        return fn(null, {success:true, notrestarted:true});\n\n      God.watch.enable(env.pm2_env);\n      //env.pm2_env.env.watch = true;\n      env.pm2_env.watch = true;\n    }\n\n    return fn(null, {success:true});\n  };\n\n  /**\n   * Description\n   * @method reloadLogs\n   * @param {} opts\n   * @param {} cb\n   * @return CallExpression\n   */\n  God.reloadLogs = function(opts, cb) {\n    console.log('Reloading logs...');\n    var processIds = Object.keys(God.clusters_db);\n\n    processIds.forEach(function (id) {\n      var cluster = God.clusters_db[id];\n\n      console.log('Reloading logs for process id %d', id);\n\n      if (cluster && cluster.pm2_env) {\n        // Cluster mode\n        if (cluster.send && cluster.pm2_env.exec_mode == 'cluster_mode') {\n          try {\n            cluster.send({\n              type:'log:reload'\n            });\n          } catch(e) {\n            console.error(e.message || e);\n          }\n        }\n        // Fork mode\n        else if (cluster._reloadLogs) {\n          cluster._reloadLogs(function(err) {\n            if (err) God.logAndGenerateError(err);\n          });\n        }\n      }\n    });\n\n    return cb(null, {});\n  };\n\n  /**\n   * Send Line To Stdin\n   * @method sendLineToStdin\n   * @param Object packet\n   * @param String pm_id Process ID\n   * @param String line  Line to send to process stdin\n   */\n  God.sendLineToStdin = function(packet, cb) {\n    if (typeof(packet.pm_id) == 'undefined' || !packet.line)\n      return cb(God.logAndGenerateError('pm_id or line field missing'), {});\n\n    var pm_id = packet.pm_id;\n    var line  = packet.line;\n\n    var proc = God.clusters_db[pm_id];\n\n    if (!proc)\n      return cb(God.logAndGenerateError('Process with ID <' + pm_id + '> unknown.'), {});\n\n    if (proc.pm2_env.exec_mode == 'cluster_mode')\n      return cb(God.logAndGenerateError('Cannot send line to processes in cluster mode'), {});\n\n    if (proc.pm2_env.status != cst.ONLINE_STATUS && proc.pm2_env.status != cst.LAUNCHING_STATUS)\n      return cb(God.logAndGenerateError('Process with ID <' + pm_id + '> offline.'), {});\n\n    try {\n      proc.stdin.write(line, function() {\n        return cb(null, {\n          pm_id : pm_id,\n          line : line\n        });\n      });\n    } catch(e) {\n      return cb(God.logAndGenerateError(e), {});\n    }\n  }\n\n  /**\n   * @param {object} packet\n   * @param {function} cb\n   */\n  God.sendDataToProcessId = function(packet, cb) {\n    if (typeof(packet.id) == 'undefined' ||\n        typeof(packet.data) == 'undefined' ||\n        !packet.topic)\n      return cb(God.logAndGenerateError('ID, DATA or TOPIC field is missing'), {});\n\n    var pm_id = packet.id;\n    var data  = packet.data;\n\n    var proc = God.clusters_db[pm_id];\n\n    if (!proc)\n      return cb(God.logAndGenerateError('Process with ID <' + pm_id + '> unknown.'), {});\n\n    if (proc.pm2_env.status != cst.ONLINE_STATUS && proc.pm2_env.status != cst.LAUNCHING_STATUS)\n      return cb(God.logAndGenerateError('Process with ID <' + pm_id + '> offline.'), {});\n\n    try {\n      proc.send(packet);\n    }\n    catch(e) {\n      return cb(God.logAndGenerateError(e), {});\n    }\n\n    return cb(null, {\n      success: true,\n      data   : packet\n    });\n  };\n\n  /**\n   * Send Message to Process by id or name\n   * @method msgProcess\n   * @param {} cmd\n   * @param {} cb\n   * @return Literal\n   */\n  God.msgProcess = function(cmd, cb) {\n    if ('id' in cmd) {\n      var id = cmd.id;\n      if (!(id in God.clusters_db))\n        return cb(God.logAndGenerateError(id + ' id unknown'), {});\n      var proc = God.clusters_db[id];\n\n      var action_exist = false;\n\n      proc.pm2_env.axm_actions.forEach(function(action) {\n        if (action.action_name == cmd.msg) {\n          action_exist = true;\n          // Reset output buffer\n          action.output = [];\n        }\n      });\n      if (action_exist == false) {\n        return cb(God.logAndGenerateError('Action doesn\\'t exist ' + cmd.msg + ' for ' + proc.pm2_env.name), {});\n      }\n\n      if (proc.pm2_env.status == cst.ONLINE_STATUS || proc.pm2_env.status == cst.LAUNCHING_STATUS) {\n        /*\n         * Send message\n         */\n        if (cmd.opts == null && !cmd.uuid)\n          proc.send(cmd.msg);\n        else\n          proc.send(cmd);\n\n        return cb(null, { process_count : 1, success : true });\n      }\n      else\n        return cb(God.logAndGenerateError(id + ' : id offline'), {});\n    }\n\n    else if ('name' in cmd) {\n      /*\n       * As names are not unique in case of cluster, this\n       * will send msg to all process matching  'name'\n       */\n      var name = cmd.name;\n      var arr = Object.keys(God.clusters_db);\n      var sent = 0;\n\n      (function ex(arr) {\n        if (arr[0] == null || !arr) {\n          return cb(null, {\n            process_count : sent,\n            success : true\n          });\n        }\n\n        var id = arr[0];\n\n        if (!God.clusters_db[id] || !God.clusters_db[id].pm2_env) {\n          arr.shift();\n          return ex(arr);\n        }\n\n        var proc_env = God.clusters_db[id].pm2_env;\n\n        const isActionAvailable = proc_env.axm_actions.find(action => action.action_name === cmd.msg) !== undefined\n\n        // if action doesn't exist for this app\n        // try with the next one\n        if (isActionAvailable === false) {\n          arr.shift();\n          return ex(arr);\n        }\n\n\n        if ((p.basename(proc_env.pm_exec_path) == name ||\n             proc_env.name == name ||\n             proc_env.namespace == name ||\n             name == 'all') &&\n            (proc_env.status == cst.ONLINE_STATUS ||\n             proc_env.status == cst.LAUNCHING_STATUS)) {\n\n          proc_env.axm_actions.forEach(function(action) {\n            if (action.action_name == cmd.msg) {\n              action_exist = true;\n            }\n          });\n\n          if (action_exist == false || proc_env.axm_actions.length == 0) {\n            arr.shift();\n            return ex(arr);\n          }\n\n          if (cmd.opts == null)\n            God.clusters_db[id].send(cmd.msg);\n          else\n            God.clusters_db[id].send(cmd);\n\n          sent++;\n          arr.shift();\n          return ex(arr);\n        }\n        else {\n          arr.shift();\n          return ex(arr);\n        }\n        return false;\n      })(arr);\n    }\n\n    else return cb(God.logAndGenerateError('method requires name or id field'), {});\n    return false;\n  };\n\n  /**\n   * Description\n   * @method getVersion\n   * @param {} env\n   * @param {} cb\n   * @return CallExpression\n   */\n  God.getVersion = function(env, cb) {\n    process.nextTick(function() {\n      return cb(null, pkg.version);\n    });\n  };\n\n  God.monitor = function Monitor(pm_id, cb) {\n    if (!God.clusters_db[pm_id] || !God.clusters_db[pm_id].pm2_env)\n      return cb(new Error('Unknown pm_id'));\n\n    God.clusters_db[pm_id].pm2_env._km_monitored = true;\n    return cb(null, { success : true, pm_id : pm_id });\n  }\n\n  God.unmonitor = function Monitor(pm_id, cb) {\n    if (!God.clusters_db[pm_id] || !God.clusters_db[pm_id].pm2_env)\n      return cb(new Error('Unknown pm_id'));\n\n    God.clusters_db[pm_id].pm2_env._km_monitored = false;\n    return cb(null, { success : true, pm_id : pm_id });\n  }\n\n  God.getReport = function(arg, cb) {\n    var report = {\n      pm2_version : pkg.version,\n      node_version : 'N/A',\n      node_path : process.env['_'] || 'not found',\n      argv0 : process.argv0,\n      argv : process.argv,\n      user : process.env.USER,\n      uid : (cst.IS_WINDOWS === false && process.geteuid) ? process.geteuid() : 'N/A',\n      gid : (cst.IS_WINDOWS === false && process.getegid) ? process.getegid() : 'N/A',\n      env : process.env,\n      managed_apps : Object.keys(God.clusters_db).length,\n      started_at : God.started_at\n    };\n\n    if (process.versions && process.versions.node) {\n      report.node_version = process.versions.node;\n    }\n\n    process.nextTick(function() {\n      return cb(null, report);\n    });\n  };\n};\n\nfunction filterBadProcess(pro) {\n  if (pro.pm2_env.status !== cst.ONLINE_STATUS) {\n    return false;\n  }\n\n  if (pro.pm2_env.axm_options && pro.pm2_env.axm_options.pid) {\n    if (isNaN(pro.pm2_env.axm_options.pid))  {\n      return false;\n    }\n  }\n\n  return true;\n}\n\nfunction getProcessId(pro) {\n  var pid = pro.pid\n\n  if (pro.pm2_env.axm_options && pro.pm2_env.axm_options.pid) {\n    pid = pro.pm2_env.axm_options.pid;\n  }\n\n  return pid\n}\n"
  },
  {
    "path": "lib/God/ClusterMode.js",
    "content": "/**\n * Copyright 2013-2022 the PM2 project authors. All rights reserved.\n * Use of this source code is governed by a license that\n * can be found in the LICENSE file.\n */\n'use strict';\n\n/**\n * @file Cluster execution functions related\n * @author Alexandre Strzelewicz <as@unitech.io>\n * @project PM2\n */\nvar cluster       = require('cluster');\nvar Utility       = require('../Utility.js');\nvar pkg           = require('../../package.json');\n\n/**\n * Description\n * @method exports\n * @param {} God\n * @return\n */\nmodule.exports = function ClusterMode(God) {\n\n  /**\n   * For Node apps - Cluster mode\n   * It will wrap the code and enable load-balancing mode\n   * @method nodeApp\n   * @param {} env_copy\n   * @param {} cb\n   * @return Literal\n   */\n  God.nodeApp = function nodeApp(env_copy, cb){\n    var clu = null;\n\n    console.log(`App [${env_copy.name}:${env_copy.pm_id}] starting in -cluster mode-`)\n    if (env_copy.node_args && Array.isArray(env_copy.node_args)) {\n      cluster.settings.execArgv = env_copy.node_args;\n    }\n\n    env_copy._pm2_version = pkg.version;\n\n    try {\n      // node.js cluster clients can not receive deep-level objects or arrays in the forked process, e.g.:\n      // { \"args\": [\"foo\", \"bar\"], \"env\": { \"foo1\": \"bar1\" }} will be parsed to\n      // { \"args\": \"foo, bar\", \"env\": \"[object Object]\"}\n      // So we passing a stringified JSON here.\n      clu = cluster.fork({pm2_env: JSON.stringify(env_copy), windowsHide: true});\n    } catch(e) {\n      God.logAndGenerateError(e);\n      return cb(e);\n    }\n\n    clu.pm2_env = env_copy;\n\n    /**\n     * Broadcast message to God\n     */\n    clu.on('message', function cluMessage(msg) {\n      /*********************************\n       * If you edit this function\n       * Do the same in ForkMode.js !\n       *********************************/\n      if (msg.data && msg.type) {\n        return God.bus.emit(msg.type ? msg.type : 'process:msg', {\n          at      : Utility.getDate(),\n          data    : msg.data,\n          process :  {\n            pm_id      : clu.pm2_env.pm_id,\n            name       : clu.pm2_env.name,\n            rev        : (clu.pm2_env.versioning && clu.pm2_env.versioning.revision) ? clu.pm2_env.versioning.revision : null,\n            namespace  : clu.pm2_env.namespace\n          }\n        });\n      }\n      else {\n\n        if (typeof msg == 'object' && 'node_version' in msg) {\n          clu.pm2_env.node_version = msg.node_version;\n          return false;\n        }\n\n        return God.bus.emit('process:msg', {\n          at      : Utility.getDate(),\n          raw     : msg,\n          process :  {\n            pm_id      : clu.pm2_env.pm_id,\n            name       : clu.pm2_env.name,\n            namespace  : clu.pm2_env.namespace\n          }\n        });\n      }\n    });\n\n    return cb(null, clu);\n  };\n};\n"
  },
  {
    "path": "lib/God/ForkMode.js",
    "content": "/**\n * Copyright 2013-2022 the PM2 project authors. All rights reserved.\n * Use of this source code is governed by a license that\n * can be found in the LICENSE file.\n */\n'use strict';\n\n/**\n * @file Fork execution related functions\n * @author Alexandre Strzelewicz <as@unitech.io>\n * @project PM2\n */\nvar log           = require('debug')('pm2:fork_mode');\nvar fs            = require('fs');\nvar Utility       = require('../Utility.js');\nvar path          = require('path');\nvar dayjs         = require('dayjs');\nvar semver  = require('semver')\nvar cst           = require('../../constants.js');\n\n/**\n * Description\n * @method exports\n * @param {} God\n * @return\n */\nmodule.exports = function ForkMode(God) {\n  /**\n   * For all apps - FORK MODE\n   * fork the app\n   * @method forkMode\n   * @param {} pm2_env\n   * @param {} cb\n   * @return\n   */\n  God.forkMode = function forkMode(pm2_env, cb) {\n    var command = '';\n    var args    = [];\n\n    console.log(`App [${pm2_env.name}:${pm2_env.pm_id}] starting in -fork mode-`)\n    var spawn = require('child_process').spawn;\n\n    var interpreter = pm2_env.exec_interpreter || process.execPath;\n    var pidFile     = pm2_env.pm_pid_path;\n\n    if (interpreter !== 'none') {\n      command = interpreter;\n\n      if (pm2_env.node_args && Array.isArray(pm2_env.node_args)) {\n        args = args.concat(pm2_env.node_args);\n      }\n\n      // Deprecated - to remove at some point\n      if (process.env.PM2_NODE_OPTIONS) {\n        args = args.concat(process.env.PM2_NODE_OPTIONS.split(' '));\n      }\n\n      if (interpreter === 'node' || RegExp('node$').test(interpreter)) {\n        args.push(path.resolve(path.dirname(module.filename), '..', 'ProcessContainerFork.js'));\n      }\n      else if (interpreter.includes('bun') === true) {\n        args.push(path.resolve(path.dirname(module.filename), '..', 'ProcessContainerForkBun.js'));\n      }\n      else\n        args.push(pm2_env.pm_exec_path);\n    }\n    else {\n      command = pm2_env.pm_exec_path;\n      args = [ ];\n    }\n\n    if (pm2_env.args) {\n      args = args.concat(pm2_env.args);\n    }\n\n    // piping stream o file\n    var stds = {\n      out: pm2_env.pm_out_log_path,\n      err: pm2_env.pm_err_log_path\n    };\n\n    // entire log std if necessary.\n    if ('pm_log_path' in pm2_env){\n      stds.std = pm2_env.pm_log_path;\n    }\n\n    log(\"stds: %j\", stds);\n\n    Utility.startLogging(stds, function(err, result) {\n      if (err) {\n        God.logAndGenerateError(err);\n        return cb(err);\n      };\n\n      try {\n        var options = {\n          env      : pm2_env,\n          detached : true,\n          cwd      : pm2_env.pm_cwd || process.cwd(),\n          stdio    : ['pipe', 'pipe', 'pipe', 'ipc'] //Same as fork() in node core\n        }\n\n        if (typeof(pm2_env.windowsHide) === \"boolean\") {\n          options.windowsHide = pm2_env.windowsHide;\n        } else {\n          options.windowsHide = true;\n        }\n\n        if (pm2_env.uid) {\n          options.uid = pm2_env.uid\n        }\n\n        if (pm2_env.gid) {\n          options.gid = pm2_env.gid\n        }\n\n        var cspr = spawn(command, args, options);\n      } catch(e) {\n        God.logAndGenerateError(e);\n        return cb(e);\n      }\n\n      if (!cspr || !cspr.stderr || !cspr.stdout) {\n        var fatalError = new Error('Process could not be forked properly, check your system health')\n        God.logAndGenerateError(fatalError);\n        return cb(fatalError);\n      }\n\n      cspr.process = {};\n      cspr.process.pid = cspr.pid;\n      cspr.pm2_env = pm2_env;\n\n      function transformLogToJson(pm2_env, type, data) {\n        return JSON.stringify({\n          message : data.toString(),\n          timestamp : pm2_env.log_date_format ? dayjs().format(pm2_env.log_date_format) : new Date().toISOString(),\n          type : type,\n          process_id : cspr.pm2_env.pm_id,\n          app_name : cspr.pm2_env.name\n        }) + '\\n'\n      }\n\n      function prefixLogWithDate(pm2_env, data) {\n        var log_data = []\n        log_data = data.toString().split('\\n')\n        if (log_data.length > 1)\n          log_data.pop()\n        log_data = log_data.map(line => `${dayjs().format(pm2_env.log_date_format)}: ${line}\\n`)\n        log_data = log_data.join('')\n        return log_data\n      }\n\n      cspr.stderr.on('data', function forkErrData(data) {\n        var log_data = null;\n\n        // via --out /dev/null --err /dev/null\n        if (pm2_env.disable_logs === true) return false;\n\n        if (pm2_env.log_type && pm2_env.log_type === 'json')\n          log_data = transformLogToJson(pm2_env, 'err', data)\n        else if (pm2_env.log_date_format)\n          log_data = prefixLogWithDate(pm2_env, data)\n        else\n          log_data = data.toString();\n\n        God.bus.emit('log:err', {\n          process : {\n            pm_id      : cspr.pm2_env.pm_id,\n            name       : cspr.pm2_env.name,\n            rev        : (cspr.pm2_env.versioning && cspr.pm2_env.versioning.revision) ? cspr.pm2_env.versioning.revision : null,\n            namespace  : cspr.pm2_env.namespace\n          },\n          at  : Utility.getDate(),\n          data : log_data\n        });\n\n        if (Utility.checkPathIsNull(pm2_env.pm_err_log_path) &&\n          (!pm2_env.pm_log_path || Utility.checkPathIsNull(pm2_env.pm_log_path))) {\n          return false;\n        }\n\n        stds.std && stds.std.write && stds.std.write(log_data);\n        stds.err && stds.err.write && stds.err.write(log_data);\n      });\n\n      cspr.stdout.on('data', function forkOutData(data) {\n        var log_data = null;\n\n        if (pm2_env.disable_logs === true)\n          return false;\n\n        if (pm2_env.log_type && pm2_env.log_type === 'json')\n          log_data = transformLogToJson(pm2_env, 'out', data)\n        else if (pm2_env.log_date_format)\n          log_data = prefixLogWithDate(pm2_env, data)\n        else\n          log_data = data.toString()\n\n        God.bus.emit('log:out', {\n          process : {\n            pm_id      : cspr.pm2_env.pm_id,\n            name       : cspr.pm2_env.name,\n            rev        : (cspr.pm2_env.versioning && cspr.pm2_env.versioning.revision) ? cspr.pm2_env.versioning.revision : null,\n            namespace  : cspr.pm2_env.namespace\n          },\n          at  : Utility.getDate(),\n          data : log_data\n        });\n\n        if (Utility.checkPathIsNull(pm2_env.pm_out_log_path) &&\n          (!pm2_env.pm_log_path || Utility.checkPathIsNull(pm2_env.pm_log_path)))\n          return false;\n\n        stds.std && stds.std.write && stds.std.write(log_data);\n        stds.out && stds.out.write && stds.out.write(log_data);\n      });\n\n      /**\n       * Broadcast message to God\n       */\n      cspr.on('message', function forkMessage(msg) {\n        /*********************************\n         * If you edit this function\n         * Do the same in ClusterMode.js !\n         *********************************/\n        if (msg.data && msg.type) {\n          process.nextTick(function() {\n            return God.bus.emit(msg.type ? msg.type : 'process:msg', {\n              at      : Utility.getDate(),\n              data    : msg.data,\n              process : {\n                pm_id      : cspr.pm2_env.pm_id,\n                name       : cspr.pm2_env.name,\n                versioning : cspr.pm2_env.versioning,\n                namespace  : cspr.pm2_env.namespace\n              }\n            });\n          });\n        }\n        else {\n\n          if (typeof msg == 'object' && 'node_version' in msg) {\n            cspr.pm2_env.node_version = msg.node_version;\n            return false;\n          }\n\n          return God.bus.emit('process:msg', {\n            at      : Utility.getDate(),\n            raw     : msg,\n            process :  {\n              pm_id      : cspr.pm2_env.pm_id,\n              name       : cspr.pm2_env.name,\n              namespace  : cspr.pm2_env.namespace\n            }\n          });\n        }\n      });\n\n      try {\n        var pid = cspr.pid\n        if (typeof(pid) !== 'undefined')\n          fs.writeFileSync(pidFile, pid.toString());\n      } catch (e) {\n        console.error(e.stack || e);\n      }\n\n      cspr.once('exit', function forkClose(status) {\n        try {\n          for(var k in stds){\n            if (stds[k] && stds[k].destroy) stds[k].destroy();\n            else if (stds[k] && stds[k].end) stds[k].end();\n            else if (stds[k] && stds[k].close) stds[k].close();\n            stds[k] = stds[k]._file;\n          }\n        } catch(e) { God.logAndGenerateError(e);}\n      });\n\n      cspr._reloadLogs = function(cb) {\n        try {\n          for (var k in stds){\n            if (stds[k] && stds[k].destroy) stds[k].destroy();\n            else if (stds[k] && stds[k].end) stds[k].end();\n            else if (stds[k] && stds[k].close) stds[k].close();\n            stds[k] = stds[k]._file;\n          }\n        } catch(e) { God.logAndGenerateError(e);}\n        //cspr.removeAllListeners();\n        Utility.startLogging(stds, cb);\n      };\n\n      cspr.unref();\n\n      return cb(null, cspr);\n    });\n\n  };\n};\n"
  },
  {
    "path": "lib/God/Methods.js",
    "content": "/**\n * Copyright 2013-2022 the PM2 project authors. All rights reserved.\n * Use of this source code is governed by a license that\n * can be found in the LICENSE file.\n */\n'use strict';\n\n/**\n * @file Utilities for PM2\n * @author Alexandre Strzelewicz <as@unitech.io>\n * @project PM2\n */\nvar p             = require('path');\nvar treekill      = require('../TreeKill');\nvar cst           = require('../../constants.js');\n\n/**\n * Description\n * @method exports\n * @param {} God\n * @return\n */\nmodule.exports = function(God) {\n\n  /**\n   * Description\n   * @method logAndGenerateError\n   * @param {} err\n   * @return NewExpression\n   */\n  God.logAndGenerateError = function(err) {\n    // Is an Error object\n    if (err instanceof Error) {\n      console.trace(err);\n      return err;\n    }\n    // Is a JSON or simple string\n    console.error(err);\n    return new Error(err);\n  };\n\n  /**\n   * Utility functions\n   * @method getProcesses\n   * @return MemberExpression\n   */\n  God.getProcesses = function() {\n    return God.clusters_db;\n  };\n\n  God.getFormatedProcess = function getFormatedProcesses(id) {\n    if (God.clusters_db[id])\n      return {\n        pid     : God.clusters_db[id].process.pid,\n        name    : God.clusters_db[id].pm2_env.name,\n        pm2_env : God.clusters_db[id].pm2_env,\n        pm_id   : God.clusters_db[id].pm2_env.pm_id\n      };\n    return {};\n  };\n\n  /**\n   * Get formated processes\n   * @method getFormatedProcesses\n   * @return {Array} formated processes\n   */\n  God.getFormatedProcesses = function getFormatedProcesses() {\n    var keys = Object.keys(God.clusters_db);\n    var arr  = new Array();\n    var kl   = keys.length;\n\n    for (var i = 0; i < kl; i++) {\n      var key = keys[i];\n\n      if (!God.clusters_db[key]) continue;\n      // Avoid _old type pm_ids\n      if (isNaN(God.clusters_db[key].pm2_env.pm_id)) continue;\n\n      arr.push({\n        pid     : God.clusters_db[key].process.pid,\n        name    : God.clusters_db[key].pm2_env.name,\n        pm2_env : God.clusters_db[key].pm2_env,\n        pm_id   : God.clusters_db[key].pm2_env.pm_id\n      })\n    }\n    return arr;\n  };\n\n  /**\n   * Description\n   * @method findProcessById\n   * @param {} id\n   * @return ConditionalExpression\n   */\n  God.findProcessById = function findProcessById(id) {\n    return God.clusters_db[id] ? God.clusters_db[id] : null;\n  };\n\n  /**\n   * Description\n   * @method findByName\n   * @param {} name\n   * @return arr\n   */\n  God.findByName = function(name) {\n    var db = God.clusters_db;\n    var arr = [];\n\n    if (name == 'all') {\n      for (var key in db) {\n        // Avoid _old_proc process style\n        if (typeof(God.clusters_db[key].pm2_env.pm_id) === 'number')\n          arr.push(db[key]);\n      }\n      return arr;\n    }\n\n    for (var key in db) {\n      if (God.clusters_db[key].pm2_env.name == name ||\n          God.clusters_db[key].pm2_env.pm_exec_path == p.resolve(name)) {\n        arr.push(db[key]);\n      }\n    }\n    return arr;\n  };\n\n  /**\n   * Check if a process is alive in system processes\n   * Return TRUE if process online\n   * @method checkProcess\n   * @param {} pid\n   * @return\n   */\n  God.checkProcess = function(pid) {\n    if (!pid) return false;\n\n    try {\n      // Sending 0 signal do not kill the process\n      process.kill(pid, 0);\n      return true;\n    }\n    catch (err) {\n      return false;\n    }\n  };\n\n  /**\n   * Description\n   * @method processIsDead\n   * @param {} pid\n   * @param {} cb\n   * @return Literal\n   */\n  God.processIsDead = function(pid, pm2_env, cb, sigkill) {\n    if (!pid) return cb({type : 'param:missing', msg : 'no pid passed'});\n\n    var timeout      = null;\n    var kill_timeout = (pm2_env && pm2_env.kill_timeout) ? pm2_env.kill_timeout : cst.KILL_TIMEOUT;\n    var mode         = pm2_env.exec_mode;\n\n    var timer = setInterval(function() {\n      if (God.checkProcess(pid) === false) {\n        console.log('pid=%d msg=process killed', pid);\n        clearTimeout(timeout);\n        clearInterval(timer);\n        return cb(null, true);\n      }\n      console.log('pid=%d msg=failed to kill - retrying in %dms', pid, pm2_env.kill_retry_time);\n      return false;\n    }, pm2_env.kill_retry_time);\n\n    timeout = setTimeout(function() {\n      clearInterval(timer);\n      if (sigkill) {\n        console.log('Process with pid %d could not be killed', pid);\n        return cb({type : 'timeout', msg : 'timeout'});\n      }\n      else {\n        console.log('Process with pid %d still alive after %sms, sending it SIGKILL now...', pid, kill_timeout);\n\n        if (pm2_env.treekill !== true) {\n          try {\n            process.kill(parseInt(pid), 'SIGKILL');\n          } catch(e) {\n            console.error('[SimpleKill][SIGKILL] %s pid can not be killed', pid, e.stack, e.message);\n          }\n          return God.processIsDead(pid, pm2_env, cb, true);\n        }\n        else {\n          treekill(parseInt(pid), 'SIGKILL', function(err) {\n            return God.processIsDead(pid, pm2_env, cb, true);\n          });\n        }\n      }\n    }, kill_timeout);\n    return false;\n  };\n\n  /**\n   * Description\n   * @method killProcess\n   * @param int pid\n   * @param Object pm2_env\n   * @param function cb\n   * @return CallExpression\n   */\n  God.killProcess = function(pid, pm2_env, cb) {\n    if (!pid) return cb({msg : 'no pid passed or null'});\n\n    if (typeof(pm2_env.pm_id) === 'number' &&\n        (cst.KILL_USE_MESSAGE || pm2_env.shutdown_with_message == true)) {\n      var proc = God.clusters_db[pm2_env.pm_id];\n\n      if (proc && proc.send) {\n        try {\n          proc.send('shutdown');\n        } catch (e) {\n          console.error(`[AppKill] Cannot send \"shutdown\" message to ${pid}`)\n          console.error(e.stack, e.message)\n        }\n        return God.processIsDead(pid, pm2_env, cb);\n      }\n      else {\n        console.log(`[AppKill] ${pid} pid cannot be notified with send()`)\n      }\n    }\n\n    if (pm2_env.treekill !== true) {\n      try {\n        process.kill(parseInt(pid), cst.KILL_SIGNAL);\n      } catch(e) {\n        console.error('[SimpleKill] %s pid can not be killed', pid, e.stack, e.message);\n      }\n      return God.processIsDead(pid, pm2_env, cb);\n    }\n    else {\n      treekill(parseInt(pid), cst.KILL_SIGNAL, function(err) {\n        return God.processIsDead(pid, pm2_env, cb);\n      });\n    }\n  };\n\n  /**\n   * Description\n   * @method getNewId\n   * @return UpdateExpression\n   */\n  God.getNewId = function() {\n    return God.next_id++;\n  };\n\n  /**\n   * When a process is restarted or reloaded reset fields\n   * to monitor unstable starts\n   * @method resetState\n   * @param {} pm2_env\n   * @return\n   */\n  God.resetState = function(pm2_env) {\n    pm2_env.created_at = Date.now();\n    pm2_env.unstable_restarts = 0;\n    pm2_env.prev_restart_delay = 0;\n  };\n\n};\n"
  },
  {
    "path": "lib/God/Reload.js",
    "content": "/**\n * Copyright 2013-2022 the PM2 project authors. All rights reserved.\n * Use of this source code is governed by a license that\n * can be found in the LICENSE file.\n */\n'use strict';\n\n/**\n * @file Reload functions related\n * @author Alexandre Strzelewicz <as@unitech.io>\n * @project PM2\n */\n\nvar cst           = require('../../constants.js');\nvar Utility       = require('../Utility.js');\n\n/**\n * softReload will wait permission from process to exit\n * @method softReload\n * @param {} God\n * @param {} id\n * @param {} cb\n * @return Literal\n */\nfunction softReload(God, id, cb) {\n  var t_key = '_old_' + id;\n\n  // Move old worker to tmp id\n  God.clusters_db[t_key] = God.clusters_db[id];\n\n  delete God.clusters_db[id];\n\n  var old_worker = God.clusters_db[t_key];\n\n  // Deep copy\n  var new_env = Utility.clone(old_worker.pm2_env);\n\n  // Reset created_at and unstable_restarts\n  God.resetState(new_env);\n\n  new_env.restart_time += 1;\n\n  old_worker.pm2_env.pm_id = t_key;\n  old_worker.pm_id = t_key;\n\n  God.executeApp(new_env, function(err, new_worker) {\n    if (err) return cb(err);\n\n    var timer = null;\n\n    var onListen = function () {\n      clearTimeout(timer);\n      softCleanDeleteProcess();\n      console.log('-softReload- New worker listening');\n    };\n\n    // Bind to know when the new process is up\n    new_worker.once('listening', onListen);\n\n    timer = setTimeout(function() {\n      new_worker.removeListener('listening', onListen);\n      softCleanDeleteProcess();\n    }, new_env.listen_timeout || cst.GRACEFUL_LISTEN_TIMEOUT);\n\n    // Remove old worker properly\n    var softCleanDeleteProcess = function () {\n      var cleanUp = function () {\n        clearTimeout(timer);\n        console.log('-softReload- Old worker disconnected');\n        return God.deleteProcessId(t_key, cb);\n      };\n\n      old_worker.once('disconnect', cleanUp);\n\n      try {\n        if (old_worker.state != 'dead' && old_worker.state != 'disconnected')\n          old_worker.send && old_worker.send('shutdown');\n        else {\n          clearTimeout(timer);\n          console.error('Worker %d is already disconnected', old_worker.pm2_env.pm_id);\n          return God.deleteProcessId(t_key, cb);\n        }\n      } catch(e) {\n        clearTimeout(timer);\n        console.error('Worker %d is already disconnected', old_worker.pm2_env.pm_id);\n        return God.deleteProcessId(t_key, cb);\n      }\n\n      timer = setTimeout(function () {\n        old_worker.removeListener('disconnect', cleanUp);\n        return God.deleteProcessId(t_key, cb);\n      }, cst.GRACEFUL_TIMEOUT);\n      return false;\n    };\n    return false;\n  });\n  return false;\n};\n\n/**\n * hardReload will reload without waiting permission from process\n * @method hardReload\n * @param {} God\n * @param {} id\n * @param {} cb\n * @return Literal\n */\nfunction hardReload(God, id, wait_msg, cb) {\n  var t_key = '_old_' + id;\n\n  // Move old worker to tmp id\n  God.clusters_db[t_key] = God.clusters_db[id];\n  delete God.clusters_db[id];\n\n  var old_worker = God.clusters_db[t_key];\n  // Deep copy\n  var new_env = Utility.clone(old_worker.pm2_env);\n  new_env.restart_time += 1;\n\n  // Reset created_at and unstable_restarts\n  God.resetState(new_env);\n\n  old_worker.pm2_env.pm_id = t_key;\n  old_worker.pm_id = t_key;\n  var timer = null;\n  var readySignalSent = false;\n  \n  var onListen = function () {\n    clearTimeout(timer);\n    readySignalSent = true;\n    console.log('-reload- New worker listening');\n    return God.deleteProcessId(t_key, cb);\n  };\n  \n  var listener = function (packet) {\n    if (packet.raw === 'ready' &&\n        packet.process.name === old_worker.pm2_env.name &&\n        packet.process.pm_id === id) {\n      God.bus.removeListener('process:msg', listener);\n      return onListen();\n    }\n  };\n  \n  if (wait_msg !== 'listening') {\n    God.bus.on('process:msg', listener);\n  }\n  \n  God.executeApp(new_env, function(err, new_worker) {\n    if (err) return cb(err);\n\n    // Bind to know when the new process is up\n    if (wait_msg === 'listening') {\n      new_worker.once('listening', onListen);\n    }\n\n    timer = setTimeout(function() {\n      if (readySignalSent) {\n        return;\n      }\n      \n      if (wait_msg === 'listening')\n        new_worker.removeListener(wait_msg, onListen);\n      else\n        God.bus.removeListener('process:msg', listener);\n\n      return God.deleteProcessId(t_key, cb);\n    }, new_env.listen_timeout || cst.GRACEFUL_LISTEN_TIMEOUT);\n\n    return false;\n  });\n  return false;\n};\n\n/**\n * Description\n * @method exports\n * @param {} God\n * @return\n */\nmodule.exports = function(God) {\n\n  /**\n   * Reload\n   * @method softReloadProcessId\n   * @param {} id\n   * @param {} cb\n   * @return CallExpression\n   */\n  God.softReloadProcessId = function(opts, cb) {\n    var id  = opts.id;\n    var env = opts.env || {};\n\n    if (!(id in God.clusters_db))\n      return cb(new Error(`pm_id ${id} not available in ${id}`));\n\n    if (God.clusters_db[id].pm2_env.status == cst.ONLINE_STATUS &&\n        God.clusters_db[id].pm2_env.exec_mode == 'cluster_mode' &&\n        !God.clusters_db[id].pm2_env.wait_ready) {\n\n      Utility.extend(God.clusters_db[id].pm2_env.env, opts.env);\n      Utility.extendExtraConfig(God.clusters_db[id], opts);\n\n      return softReload(God, id, cb);\n    }\n    else {\n      console.log('Process %s in a stopped status, starting it', id);\n      return God.restartProcessId(opts, cb);\n    }\n  };\n\n  /**\n   * Reload\n   * @method reloadProcessId\n   * @param {} id\n   * @param {} cb\n   * @return CallExpression\n   */\n  God.reloadProcessId = function(opts, cb) {\n    var id  = opts.id;\n    var env = opts.env || {};\n\n    if (!(id in God.clusters_db))\n      return cb(new Error('PM2 ID unknown'));\n\n    if (God.clusters_db[id].pm2_env.status == cst.ONLINE_STATUS &&\n        God.clusters_db[id].pm2_env.exec_mode == 'cluster_mode') {\n\n      Utility.extend(God.clusters_db[id].pm2_env.env, opts.env);\n      Utility.extendExtraConfig(God.clusters_db[id], opts);\n\n      var wait_msg = God.clusters_db[id].pm2_env.wait_ready ? 'ready' : 'listening';\n      return hardReload(God, id, wait_msg, cb);\n    }\n    else {\n      console.log('Process %s in a stopped status, starting it', id);\n      return God.restartProcessId(opts, cb);\n    }\n  };\n\n};\n"
  },
  {
    "path": "lib/God.js",
    "content": "/**\n * Copyright 2013-2022 the PM2 project authors. All rights reserved.\n * Use of this source code is governed by a license that\n * can be found in the LICENSE file.\n */\n\n/******************************\n *    ______ _______ ______\n *   |   __ \\   |   |__    |\n *   |    __/       |    __|\n *   |___|  |__|_|__|______|\n *\n *    Main Daemon side file\n *\n ******************************/\n\nvar cluster       = require('cluster');\nvar numCPUs       = require('os').cpus() ? require('os').cpus().length : 1;\nvar path          = require('path');\nvar EventEmitter2 = require('eventemitter2').EventEmitter2;\nvar fs            = require('fs');\nvar vizion        = require('vizion');\nvar debug         = require('debug')('pm2:god');\nvar Utility       = require('./Utility');\nvar cst           = require('../constants.js');\nvar timesLimit    = require('async/timesLimit');\nvar Configuration = require('./Configuration.js');\n\n/**\n * Override cluster module configuration\n */\n\nif (cst.IS_BUN == true) {\n  cluster.setupMaster({\n    windowsHide: true,\n    exec : path.resolve(path.dirname(module.filename), 'ProcessContainerBun.js')\n  });\n}\nelse {\n  cluster.setupMaster({\n    windowsHide: true,\n    exec : path.resolve(path.dirname(module.filename), 'ProcessContainer.js')\n  });\n}\n\n/**\n * Expose God\n */\nvar God = module.exports = {\n  next_id : 0,\n  clusters_db : {},\n  configuration: {},\n  started_at : Date.now(),\n  system_infos_proc: null,\n  system_infos: null,\n  bus : new EventEmitter2({\n    wildcard: true,\n    delimiter: ':',\n    maxListeners: 1000\n  })\n};\n\nUtility.overrideConsole(God.bus);\n\n/**\n * Populate God namespace\n */\nrequire('./Event.js')(God);\nrequire('./God/Methods.js')(God);\nrequire('./God/ForkMode.js')(God);\nrequire('./God/ClusterMode.js')(God);\nrequire('./God/Reload')(God);\nrequire('./God/ActionMethods')(God);\nrequire('./Watcher')(God);\n\nGod.init = function() {\n  require('./Worker.js')(this)\n  God.system_infos_proc = null\n\n  this.configuration = Configuration.getSync('pm2')\n\n  setTimeout(function() {\n    God.Worker.start()\n  }, 500)\n}\n\nGod.writeExitSeparator = function(pm2_env, code, signal) {\n  try {\n    var exit_sep = `[PM2][${new Date().toISOString()}] app exited`\n    if (code)\n      exit_sep += `itself with exit code: ${code}`\n    if (signal)\n      exit_sep += `by an external signal: ${signal}`\n    exit_sep += '\\n'\n\n    if (pm2_env.pm_out_log_path)\n      fs.writeFileSync(pm2_env.pm_out_log_path, exit_sep)\n    if (pm2_env.pm_err_log_path)\n      fs.writeFileSync(pm2_env.pm_err_log_path, exit_sep)\n    if (pm2_env.pm_log_path)\n      fs.writeFileSync(pm2_env.pm_log_path, exit_sep)\n  } catch(e) {\n  }\n}\n\n/**\n * Init new process\n */\nGod.prepare = function prepare (env, cb) {\n  // generate a new unique id for each processes\n  env.env.unique_id = Utility.generateUUID()\n\n  // if the app is standalone, no multiple instance\n  if (typeof env.instances === 'undefined') {\n    env.vizion_running = false;\n    if (env.env && env.env.vizion_running) env.env.vizion_running = false;\n\n    if (env.status == cst.STOPPED_STATUS) {\n      env.pm_id = God.getNewId()\n      var clu = {\n        pm2_env : env,\n        process: {\n        }\n      }\n      God.clusters_db[env.pm_id] = clu\n      God.registerCron(env)\n      return cb(null, [ God.clusters_db[env.pm_id] ])\n    }\n\n    return God.executeApp(env, function (err, clu) {\n      if (err) return cb(err);\n      God.notify('start', clu, true);\n      return cb(null, [ Utility.clone(clu) ]);\n    });\n  }\n\n  // find how many replicate the user want\n  env.instances = parseInt(env.instances);\n  if (env.instances === 0) {\n    env.instances = numCPUs;\n  } else if (env.instances < 0) {\n    env.instances += numCPUs;\n  }\n  if (env.instances <= 0) {\n    env.instances = 1;\n  }\n\n  timesLimit(env.instances, 1, function (n, next) {\n    env.vizion_running = false;\n    if (env.env && env.env.vizion_running) {\n      env.env.vizion_running = false;\n    }\n\n    God.injectVariables(env, function inject (err, _env) {\n      if (err) return next(err);\n      return God.executeApp(Utility.clone(_env), function (err, clu) {\n        if (err) return next(err);\n        God.notify('start', clu, true);\n        // here call next wihtout an array because\n        // async.times aggregate the result into an array\n        return next(null, Utility.clone(clu));\n      });\n    });\n  }, cb);\n};\n\n/**\n * Launch the specified script (present in env)\n * @api private\n * @method executeApp\n * @param {Mixed} env\n * @param {Function} cb\n * @return Literal\n */\nGod.executeApp = function executeApp(env, cb) {\n  var env_copy = Utility.clone(env);\n\n  Utility.extend(env_copy, env_copy.env);\n\n  env_copy['status']         = env.autostart ? cst.LAUNCHING_STATUS : cst.STOPPED_STATUS;\n  env_copy['pm_uptime']      = Date.now();\n  env_copy['axm_actions']    = [];\n  env_copy['axm_monitor']    = {};\n  env_copy['axm_options']    = {};\n  env_copy['axm_dynamic']    = {};\n  env_copy['vizion_running'] =\n    env_copy['vizion_running'] !== undefined ? env_copy['vizion_running'] : false;\n\n  if (!env_copy.created_at)\n    env_copy['created_at'] = Date.now();\n\n  /**\n   * Enter here when it's the first time that the process is created\n   * 1 - Assign a new id\n   * 2 - Reset restart time and unstable_restarts\n   * 3 - Assign a log file name depending on the id\n   * 4 - If watch option is set, look for changes\n   */\n  if (env_copy['pm_id'] === undefined) {\n    env_copy['pm_id']             = God.getNewId();\n    env_copy['restart_time']      = 0;\n    env_copy['unstable_restarts'] = 0;\n\n    // add -pm_id to pid file\n    env_copy.pm_pid_path = env_copy.pm_pid_path.replace(/-[0-9]+\\.pid$|\\.pid$/g, '-' + env_copy['pm_id'] + '.pid');\n\n    // If merge option, dont separate the logs\n    if (!env_copy['merge_logs']) {\n      ['', '_out', '_err'].forEach(function(k){\n        var key = 'pm' + k + '_log_path';\n        env_copy[key] && (env_copy[key] = env_copy[key].replace(/-[0-9]+\\.log$|\\.log$/g, '-' + env_copy['pm_id'] + '.log'));\n      });\n    }\n\n    // Initiate watch file\n    if (env_copy['watch']) {\n      God.watch.enable(env_copy);\n    }\n  }\n\n  God.registerCron(env_copy)\n\n  if (env_copy['autostart'] === false) {\n    var clu = {pm2_env: env_copy, process: {pid: 0}};\n    God.clusters_db[env_copy.pm_id] = clu;\n    return cb(null, clu);\n  }\n\n  var cb_called = false\n\n  /** Callback when application is launched */\n  var readyCb = function ready(proc) {\n    cb_called = true\n\n    proc.pm2_env.version = Utility.findPackageVersion(proc.pm2_env.pm_exec_path || proc.pm2_env.cwd);\n    // If vizion enabled run versioning retrieval system\n    if (cst.ENABLE_GIT_PARSING === true &&\n        proc.pm2_env.vizion !== false && proc.pm2_env.vizion !== \"false\") {\n      God.finalizeProcedure(proc);\n    }\n    else\n      God.notify('online', proc);\n\n    if (proc.pm2_env.status !== cst.ERRORED_STATUS)\n      proc.pm2_env.status = cst.ONLINE_STATUS\n\n    console.log(`App [${proc.pm2_env.name}:${proc.pm2_env.pm_id}] online`);\n    if (cb) cb(null, proc);\n  }\n\n  if (env_copy.exec_mode === 'cluster_mode') {\n    /**\n     * Cluster mode logic (for NodeJS apps)\n     */\n    God.nodeApp(env_copy, function nodeApp(err, clu) {\n      if (cb && err) return cb(err);\n      if (err) return false;\n\n      var old_env = God.clusters_db[clu.pm2_env.pm_id];\n\n      if (old_env) {\n        old_env = null;\n        God.clusters_db[clu.pm2_env.pm_id] = null;\n      }\n\n      God.clusters_db[clu.pm2_env.pm_id] = clu;\n\n\n      if (cst.IS_BUN) {\n        // When starting an app that does not listen on a port\n        // Bun do not call 'online' event\n        // This is a temporary workaround\n        var a = setTimeout(() => {\n          if (clu.pm2_env)\n            God.clusters_db[clu.pm2_env.pm_id].state = 'online'\n          return readyCb(clu)\n        }, 500)\n      }\n\n      clu.once('error', function(err) {\n        if (cst.IS_BUN)\n          clearTimeout(a)\n\n        console.error(err.stack || err);\n        try {\n          clu.destroy && clu.destroy();\n        }\n        catch (e) {\n          console.error(e.stack || e);\n          God.handleExit(clu, cst.ERROR_EXIT);\n        }\n      });\n\n      clu.once('disconnect', function() {\n        if (cst.IS_BUN)\n          clearTimeout(a)\n\n        console.log('App name:%s id:%s disconnected', clu.pm2_env.name, clu.pm2_env.pm_id);\n      });\n\n      clu.once('exit', function cluExit(code, signal) {\n        if (cst.IS_BUN) {\n          clearTimeout(a)\n          if (cb_called == false)\n            readyCb(clu);\n        }\n        //God.writeExitSeparator(clu.pm2_env, code, signal)\n\n        God.handleExit(clu, code || 0, signal || 'SIGINT');\n      });\n\n      return clu.once('online', function () {\n        if (cst.IS_BUN) {\n          clearTimeout(a);\n        }\n\n        if (!clu.pm2_env.wait_ready)\n          return readyCb(clu);\n\n        // Timeout if the ready message has not been sent before listen_timeout\n        var ready_timeout = setTimeout(function() {\n          God.bus.removeListener('process:msg', listener)\n          return readyCb(clu)\n        }, clu.pm2_env.listen_timeout || cst.GRACEFUL_LISTEN_TIMEOUT);\n\n        var listener = function (packet) {\n          if (packet.raw === 'ready' &&\n              packet.process.name === clu.pm2_env.name &&\n              packet.process.pm_id === clu.pm2_env.pm_id) {\n            clearTimeout(ready_timeout);\n            God.bus.removeListener('process:msg', listener)\n            return readyCb(clu)\n          }\n        }\n\n        God.bus.on('process:msg', listener);\n      });\n    });\n  }\n  else {\n    /**\n     * Fork mode logic\n     */\n    God.forkMode(env_copy, function forkMode(err, clu) {\n      if (cb && err) return cb(err);\n      if (err) return false;\n\n      var old_env = God.clusters_db[clu.pm2_env.pm_id];\n      if (old_env) old_env = null;\n\n      God.clusters_db[env_copy.pm_id] = clu;\n\n      clu.once('error', function cluError(err) {\n        console.error(err.stack || err);\n        try {\n          clu.kill && clu.kill();\n        }\n        catch (e) {\n          console.error(e.stack || e);\n          God.handleExit(clu, cst.ERROR_EXIT);\n        }\n      });\n\n      clu.once('exit', function cluClose(code, signal) {\n        //God.writeExitSeparator(clu.pm2_env, code, signal)\n\n        if (clu.connected === true)\n          clu.disconnect && clu.disconnect();\n        clu._reloadLogs = null;\n        return God.handleExit(clu, code || 0, signal);\n      });\n\n      if (!clu.pm2_env.wait_ready)\n        return readyCb(clu);\n\n      // Timeout if the ready message has not been sent before listen_timeout\n      var ready_timeout = setTimeout(function() {\n        God.bus.removeListener('process:msg', listener)\n        return readyCb(clu)\n      }, clu.pm2_env.listen_timeout || cst.GRACEFUL_LISTEN_TIMEOUT);\n\n      var listener = function (packet) {\n        if (packet.raw === 'ready' &&\n            packet.process.name === clu.pm2_env.name &&\n            packet.process.pm_id === clu.pm2_env.pm_id) {\n          clearTimeout(ready_timeout);\n          God.bus.removeListener('process:msg', listener)\n          return readyCb(clu)\n        }\n      }\n      God.bus.on('process:msg', listener);\n    });\n  }\n  return false;\n};\n\n/**\n * Handle logic when a process exit (Node or Fork)\n * @method handleExit\n * @param {} clu\n * @param {} exit_code\n * @return\n */\nGod.handleExit = function handleExit(clu, exit_code, kill_signal) {\n  console.log(`App [${clu.pm2_env.name}:${clu.pm2_env.pm_id}] exited with code [${exit_code}] via signal [${kill_signal || 'SIGINT'}]`)\n\n  var proc = this.clusters_db[clu.pm2_env.pm_id];\n\n  if (!proc) {\n    console.error('Process undefined ? with process id ', clu.pm2_env.pm_id);\n    return false;\n  }\n\n  var stopExitCodes = proc.pm2_env.stop_exit_codes !== undefined && proc.pm2_env.stop_exit_codes !== null ? proc.pm2_env.stop_exit_codes : [];\n  if (!Array.isArray(stopExitCodes)) {\n    stopExitCodes = [stopExitCodes];\n  }\n\n  var stopping = (proc.pm2_env.status == cst.STOPPING_STATUS\n                  || proc.pm2_env.status == cst.STOPPED_STATUS\n                  || proc.pm2_env.status == cst.ERRORED_STATUS)\n      || (proc.pm2_env.autorestart === false || proc.pm2_env.autorestart === \"false\")\n      || (stopExitCodes.map((strOrNum) => typeof strOrNum === 'string' ? parseInt(strOrNum, 10) : strOrNum)\n      .includes(exit_code));\n\n  var overlimit   = false;\n\n  if (stopping) proc.process.pid = 0;\n\n  // Reset probes and actions\n  if (proc.pm2_env.axm_actions) proc.pm2_env.axm_actions = [];\n  if (proc.pm2_env.axm_monitor) proc.pm2_env.axm_monitor = {};\n\n  if (proc.pm2_env.status != cst.ERRORED_STATUS &&\n      proc.pm2_env.status != cst.STOPPING_STATUS)\n    proc.pm2_env.status = cst.STOPPED_STATUS;\n\n  if (proc.pm2_env.pm_id.toString().indexOf('_old_') !== 0) {\n    try {\n      fs.unlinkSync(proc.pm2_env.pm_pid_path);\n    } catch (e) {\n      debug('Error when unlinking pid file', e);\n    }\n  }\n\n  /**\n   * Avoid infinite reloop if an error is present\n   */\n  // If the process has been created less than 15seconds ago\n\n  // And if the process has an uptime less than a second\n  var min_uptime = typeof(proc.pm2_env.min_uptime) !== 'undefined' ? proc.pm2_env.min_uptime : 1000;\n  var max_restarts = typeof(proc.pm2_env.max_restarts) !== 'undefined' ? proc.pm2_env.max_restarts : 16;\n\n  if ((Date.now() - proc.pm2_env.created_at) < (min_uptime * max_restarts)) {\n    if ((Date.now() - proc.pm2_env.pm_uptime) < min_uptime) {\n      // Increment unstable restart\n      proc.pm2_env.unstable_restarts += 1;\n    }\n  }\n\n\n  if (proc.pm2_env.unstable_restarts >= max_restarts) {\n    // Too many unstable restart in less than 15 seconds\n    // Set the process as 'ERRORED'\n    // And stop restarting it\n    proc.pm2_env.status = cst.ERRORED_STATUS;\n    proc.process.pid = 0;\n\n    console.log('Script %s had too many unstable restarts (%d). Stopped. %j',\n      proc.pm2_env.pm_exec_path,\n      proc.pm2_env.unstable_restarts,\n      proc.pm2_env.status);\n\n    God.notify('restart overlimit', proc);\n\n    proc.pm2_env.unstable_restarts = 0;\n    proc.pm2_env.created_at = null;\n    overlimit = true;\n  }\n\n  if (typeof(exit_code) !== 'undefined') proc.pm2_env.exit_code = exit_code;\n\n  God.notify('exit', proc);\n\n  if (God.pm2_being_killed) {\n    //console.log('[HandleExit] PM2 is being killed, stopping restart procedure...');\n    return false;\n  }\n\n  var restart_delay = 0;\n\n  if (proc.pm2_env.restart_delay !== undefined &&\n      !isNaN(parseInt(proc.pm2_env.restart_delay))) {\n    proc.pm2_env.status = cst.WAITING_RESTART;\n    restart_delay = parseInt(proc.pm2_env.restart_delay);\n  }\n\n  if (proc.pm2_env.exp_backoff_restart_delay !== undefined &&\n      !isNaN(parseInt(proc.pm2_env.exp_backoff_restart_delay))) {\n    proc.pm2_env.status = cst.WAITING_RESTART;\n    if (!proc.pm2_env.prev_restart_delay) {\n      proc.pm2_env.prev_restart_delay = proc.pm2_env.exp_backoff_restart_delay\n      restart_delay = proc.pm2_env.exp_backoff_restart_delay\n    }\n    else {\n      proc.pm2_env.prev_restart_delay = Math.floor(Math.min(15000, proc.pm2_env.prev_restart_delay * 1.5))\n      restart_delay = proc.pm2_env.prev_restart_delay\n    }\n    console.log(`App [${clu.pm2_env.name}:${clu.pm2_env.pm_id}] will restart in ${restart_delay}ms`)\n  }\n\n  if (!stopping && !overlimit) {\n    //make this property unenumerable\n    Object.defineProperty(proc.pm2_env, 'restart_task', {configurable: true, writable: true});\n    proc.pm2_env.restart_task = setTimeout(function() {\n      proc.pm2_env.restart_time += 1;\n      God.executeApp(proc.pm2_env);\n    }, restart_delay);\n  }\n\n  return false;\n};\n\n/**\n * @method finalizeProcedure\n * @param proc {Object}\n * @return\n */\nGod.finalizeProcedure = function finalizeProcedure(proc) {\n  var last_path    = '';\n  var current_path = proc.pm2_env.cwd || path.dirname(proc.pm2_env.pm_exec_path);\n  var proc_id      = proc.pm2_env.pm_id;\n\n  proc.pm2_env.version = Utility.findPackageVersion(proc.pm2_env.pm_exec_path || proc.pm2_env.cwd);\n\n  if (proc.pm2_env.vizion_running === true) {\n    debug('Vizion is already running for proc id: %d, skipping this round', proc_id);\n    return God.notify('online', proc);\n  }\n  proc.pm2_env.vizion_running = true;\n\n  vizion.analyze({folder : current_path}, function recur_path(err, meta){\n    var proc = God.clusters_db[proc_id];\n\n    if (err)\n      debug(err.stack || err);\n\n    if (!proc ||\n        !proc.pm2_env ||\n        proc.pm2_env.status == cst.STOPPED_STATUS ||\n        proc.pm2_env.status == cst.STOPPING_STATUS ||\n        proc.pm2_env.status == cst.ERRORED_STATUS) {\n      return console.error('Cancelling versioning data parsing');\n    }\n\n    proc.pm2_env.vizion_running = false;\n\n    if (!err) {\n      proc.pm2_env.versioning = meta;\n      proc.pm2_env.versioning.repo_path = current_path;\n      God.notify('online', proc);\n    }\n    else if (err && current_path === last_path) {\n      proc.pm2_env.versioning = null;\n      God.notify('online', proc);\n    }\n    else {\n      last_path = current_path;\n      current_path = path.dirname(current_path);\n      proc.pm2_env.vizion_running = true;\n      vizion.analyze({folder : current_path}, recur_path);\n    }\n    return false;\n  });\n};\n\n/**\n * Inject variables into processes\n * @param {Object} env environnement to be passed to the process\n * @param {Function} cb invoked with <err, env>\n */\nGod.injectVariables = function injectVariables (env, cb) {\n  // allow to override the key of NODE_APP_INSTANCE if wanted\n  var instanceKey = process.env.PM2_PROCESS_INSTANCE_VAR || env.instance_var;\n\n  // we need to find the last NODE_APP_INSTANCE used\n  var instances = Object.keys(God.clusters_db)\n    .map(function (procId) {\n      return God.clusters_db[procId];\n    }).filter(function (proc) {\n      return proc.pm2_env.name === env.name &&\n        typeof proc.pm2_env[instanceKey] !== 'undefined';\n    }).map(function (proc) {\n      return proc.pm2_env[instanceKey];\n    }).sort(function (a, b) {\n      return b - a;\n    });\n  // default to last one + 1\n  var instanceNumber = typeof instances[0] === 'undefined' ? 0 : instances[0] + 1;\n  // but try to find a one available\n  for (var i = 0; i < instances.length; i++) {\n    if (instances.indexOf(i) === -1) {\n      instanceNumber = i;\n      break;\n    }\n  }\n  env[instanceKey] = instanceNumber;\n\n  // if using increment_var, we need to increment it\n  if (env.increment_var) {\n    var lastIncrement = Object.keys(God.clusters_db)\n      .map(function (procId) {\n        return God.clusters_db[procId];\n      }).filter(function (proc) {\n        return proc.pm2_env.name === env.name &&\n          typeof proc.pm2_env[env.increment_var] !== 'undefined';\n      }).map(function (proc) {\n        return Number(proc.pm2_env[env.increment_var]);\n      }).sort(function (a, b) {\n        return b - a;\n      })[0];\n    // inject a incremental variable\n    var defaut = Number(env.env[env.increment_var]) || 0;\n    env[env.increment_var] = typeof lastIncrement === 'undefined' ? defaut : lastIncrement + 1;\n    env.env[env.increment_var] = env[env.increment_var];\n  }\n\n  return cb(null, env);\n};\n\nGod.init()\n"
  },
  {
    "path": "lib/HttpInterface.js",
    "content": "/**\n * Copyright 2013-2022 the PM2 project authors. All rights reserved.\n * Use of this source code is governed by a license that\n * can be found in the LICENSE file.\n */\nvar http   = require('http');\nvar os     = require('os');\nvar pm2    = require('../index.js');\nvar urlT   = require('url');\nvar cst    = require('../constants.js');\n\n// Default, attach to default local PM2\n\npm2.connect(function() {\n  startWebServer(pm2);\n});\n\nfunction startWebServer(pm2) {\n  http.createServer(function (req, res) {\n    // Add CORS headers to allow browsers to fetch data directly\n    res.setHeader('Access-Control-Allow-Origin', '*');\n    res.setHeader('Access-Control-Allow-Headers', 'Cache-Control, Pragma, Origin, Authorization, Content-Type, X-Requested-With');\n    res.setHeader('Access-Control-Allow-Methods', 'GET');\n\n    // We always send json\n    res.setHeader('Content-Type','application/json');\n\n    var path = urlT.parse(req.url).pathname;\n\n    if (path == '/') {\n      // Main monit route\n      pm2.list(function(err, list) {\n        if (err) {\n          return res.send(err);\n        }\n        var data = {\n          system_info: { hostname: os.hostname(),\n                         uptime: os.uptime()\n                       },\n          monit: { loadavg: os.loadavg(),\n                   total_mem: os.totalmem(),\n                   free_mem: os.freemem(),\n                   cpu: os.cpus(),\n                   interfaces: os.networkInterfaces()\n                 },\n          processes: list\n        };\n\n        if (cst.WEB_STRIP_ENV_VARS === true) {\n          for (var i = data.processes.length - 1; i >= 0; i--) {\n            var proc = data.processes[i];\n\n            // Strip important environment variables\n            if (typeof proc.pm2_env === 'undefined' && typeof proc.pm2_env.env === 'undefined') return;\n\n            delete proc.pm2_env.env;\n          }\n        }\n\n        res.statusCode = 200;\n        res.write(JSON.stringify(data));\n        return res.end();\n\n      })\n    }\n    else {\n      // 404\n      res.statusCode = 404;\n      res.write(JSON.stringify({err : '404'}));\n      return res.end();\n    }\n  }).listen(process.env.PM2_WEB_PORT || cst.WEB_PORT, cst.WEB_IPADDR, function() {\n    console.log('Web interface listening on  %s:%s', cst.WEB_IPADDR, cst.WEB_PORT);\n  });\n\n}\n"
  },
  {
    "path": "lib/ProcessContainer.js",
    "content": "/**\n * Copyright 2013-2022 the PM2 project authors. All rights reserved.\n * Use of this source code is governed by a license that\n * can be found in the LICENSE file.\n *\n * This file wrap target application\n * - redirect stdin, stderr to bus + log files\n * - rename process\n * - pid\n */\n\nvar p       = require('path');\nvar cst     = require('../constants');\nvar Utility = require('./Utility.js');\nvar ProcessUtils = require('./ProcessUtils');\nvar Url = require('url');\n\n// Load all env-vars from master.\nvar pm2_env = JSON.parse(process.env.pm2_env);\nfor(var k in pm2_env) {\n  process.env[k] = pm2_env[k];\n}\n\n// Rename process\nprocess.title = process.env.PROCESS_TITLE || 'node ' + pm2_env.pm_exec_path;\n\ndelete process.env.pm2_env;\n\n/**\n * Main entrance to wrap the desired code\n */\n(function ProcessContainer() {\n  var fs          = require('fs');\n\n  ProcessUtils.injectModules()\n\n  var stdFile     = pm2_env.pm_log_path;\n  var outFile     = pm2_env.pm_out_log_path;\n  var errFile     = pm2_env.pm_err_log_path;\n  var pidFile     = pm2_env.pm_pid_path;\n  var script      = pm2_env.pm_exec_path;\n\n  var original_send = process.send;\n\n  if (typeof(process.env.source_map_support) != 'undefined' &&\n      process.env.source_map_support !== 'false') {\n    require('source-map-support').install();\n  }\n\n  process.send = function() {\n    if (process.connected)\n      original_send.apply(this, arguments);\n  };\n\n  //send node version\n  if (process.versions && process.versions.node) {\n    process.send({\n      'node_version': process.versions.node\n    });\n  }\n\n  if (cst.MODIFY_REQUIRE)\n    require.main.filename = pm2_env.pm_exec_path;\n\n  // Resets global paths for require()\n  require('module')._initPaths();\n\n  try {\n    var pid = process.pid\n    if (typeof(pid) !== 'undefined')\n      fs.writeFileSync(pidFile, process.pid.toString());\n  } catch (e) {\n    console.error(e.stack || e);\n  }\n\n  // Add args to process if args specified on start\n  if (process.env.args != null)\n    process.argv = process.argv.concat(pm2_env.args);\n\n  // stdio, including: out, err and entire (both out and err if necessary).\n  var stds = {\n    out: outFile,\n    err: errFile\n  };\n  stdFile && (stds.std = stdFile);\n\n  // uid/gid management\n  if (pm2_env.uid || pm2_env.gid) {\n    try {\n      if (process.env.gid)\n        process.setgid(pm2_env.gid);\n      if (pm2_env.uid)\n        process.setuid(pm2_env.uid);\n    } catch(e) {\n      setTimeout(function() {\n        console.error('%s on call %s', e.message, e.syscall);\n        console.error('%s is not accessible', pm2_env.uid);\n        return process.exit(1);\n      }, 100);\n    }\n  }\n\n  exec(script, stds);\n})();\n\n/**\n * Description\n * @method exec\n * @param {} script\n * @param {} stds\n * @return\n */\nfunction exec(script, stds) {\n  if (p.extname(script) == '.ts' || p.extname(script) == '.tsx') {\n    try {\n      require('ts-node/register');\n    } catch (e) {\n      console.error('Failed to load Typescript interpreter:', e.message || e);\n    }\n  }\n\n  process.on('message', function (msg) {\n    if (msg.type === 'log:reload') {\n      for (var k in stds){\n        if (typeof stds[k] == 'object' && !isNaN(stds[k].fd)){\n          if (stds[k].destroy) stds[k].destroy();\n          else if (stds[k].end) stds[k].end();\n          else if (stds[k].close) stds[k].close();\n          stds[k] = stds[k]._file;\n        }\n      }\n      Utility.startLogging(stds, function (err) {\n        if (err)\n          return console.error('Failed to reload logs:', err.stack);\n        console.log('Reloading log...');\n      });\n    }\n  });\n\n  var dayjs = null;\n\n  if (pm2_env.log_date_format)\n    dayjs = require('dayjs');\n\n  Utility.startLogging(stds, function (err) {\n    if (err) {\n      process.send({\n        type    : 'process:exception',\n        data    : {\n          message: err.message,\n          syscall: 'ProcessContainer.startLogging'\n        }\n      });\n      throw err;\n      return;\n    }\n\n    process.stderr.write = (function(write) {\n      return function(string, encoding, cb) {\n        var log_data = null;\n\n        // Disable logs if specified\n        if (pm2_env.disable_logs === true) {\n          return cb ? cb() : false;\n        }\n\n        if (pm2_env.log_type && pm2_env.log_type === 'json') {\n          log_data = JSON.stringify({\n            message : string.toString(),\n            timestamp : pm2_env.log_date_format && dayjs ?\n              dayjs().format(pm2_env.log_date_format) : new Date().toISOString(),\n            type : 'err',\n            process_id : pm2_env.pm_id,\n            app_name : pm2_env.name\n          }) + '\\n';\n        }\n        else if (pm2_env.log_date_format && dayjs)\n          log_data = `${dayjs().format(pm2_env.log_date_format)}: ${string.toString()}`;\n        else\n          log_data = string.toString();\n\n        process.send({\n          type : 'log:err',\n          topic : 'log:err',\n          data : log_data\n        });\n\n        if (Utility.checkPathIsNull(pm2_env.pm_err_log_path) &&\n          (!pm2_env.pm_log_path || Utility.checkPathIsNull(pm2_env.pm_log_path)))\n          return cb ? cb() : false;\n\n        stds.std && stds.std.write && stds.std.write(log_data, encoding);\n        stds.err && stds.err.write && stds.err.write(log_data, encoding, cb);\n      };\n    })(process.stderr.write);\n\n    process.stdout.write = (function(write) {\n      return function(string, encoding, cb) {\n        var log_data = null;\n\n        // Disable logs if specified\n        if (pm2_env.disable_logs === true) {\n          return cb ? cb() : false;\n        }\n\n        if (pm2_env.log_type && pm2_env.log_type === 'json') {\n          log_data = JSON.stringify({\n            message : string.toString(),\n            timestamp : pm2_env.log_date_format && dayjs ?\n              dayjs().format(pm2_env.log_date_format) : new Date().toISOString(),\n            type : 'out',\n            process_id : pm2_env.pm_id,\n            app_name : pm2_env.name\n          }) + '\\n';\n        }\n        else if (pm2_env.log_date_format && dayjs)\n          log_data = `${dayjs().format(pm2_env.log_date_format)}: ${string.toString()}`;\n        else\n          log_data = string.toString();\n\n        process.send({\n          type : 'log:out',\n          data : log_data\n        });\n\n        if (Utility.checkPathIsNull(pm2_env.pm_out_log_path) &&\n          (!pm2_env.pm_log_path || Utility.checkPathIsNull(pm2_env.pm_log_path)))\n          return cb ? cb() : null;\n\n        stds.std && stds.std.write && stds.std.write(log_data, encoding);\n        stds.out && stds.out.write && stds.out.write(log_data, encoding, cb);\n      };\n    })(process.stdout.write);\n\n    function getUncaughtExceptionListener(listener) {\n      return function uncaughtListener(err) {\n        var error = err && err.stack ? err.stack : err;\n\n        if (listener === 'unhandledRejection') {\n          error = 'You have triggered an unhandledRejection, you may have forgotten to catch a Promise rejection:\\n' + error;\n        }\n\n        logError(['std', 'err'], error);\n\n        // Notify master that an uncaughtException has been catched\n        try {\n          if (err) {\n            var errObj = {};\n\n            Object.getOwnPropertyNames(err).forEach(function(key) {\n              errObj[key] = err[key];\n            });\n          }\n\n          process.send({\n            type : 'log:err',\n            topic : 'log:err',\n            data : '\\n' + error + '\\n'\n          });\n          process.send({\n            type    : 'process:exception',\n            data    : errObj !== undefined ? errObj : {message: 'No error but ' + listener + ' was caught!'}\n          });\n        } catch(e) {\n          logError(['std', 'err'], 'Channel is already closed can\\'t broadcast error:\\n' + e.stack);\n        }\n\n        if (!process.listeners(listener).filter(function (listener) {\n            return listener !== uncaughtListener;\n        }).length) {\n          if (listener == 'uncaughtException') {\n            process.emit('disconnect');\n            process.exit(cst.CODE_UNCAUGHTEXCEPTION);\n          }\n        }\n      }\n    }\n\n    process.on('uncaughtException', getUncaughtExceptionListener('uncaughtException'));\n    process.on('unhandledRejection', getUncaughtExceptionListener('unhandledRejection'));\n\n    // Change dir to fix process.cwd\n    process.chdir(pm2_env.pm_cwd || process.env.PWD || p.dirname(script));\n\n    if (ProcessUtils.isESModule(script) === true)\n      import(Url.pathToFileURL(process.env.pm_exec_path));\n    else {\n      if (cst.IS_BUN) {\n        require(script);\n      }\n      else {\n        require('module')._load(script, null, true);\n      }\n    }\n\n    function logError(types, error){\n      try {\n        types.forEach(function(type){\n          stds[type] && typeof stds[type].write == 'function' && stds[type].write(error + '\\n');\n        });\n      } catch(e) { }\n    }\n  });\n\n}\n"
  },
  {
    "path": "lib/ProcessContainerBun.js",
    "content": "/**\n * Copyright 2013-2022 the PM2 project authors. All rights reserved.\n * Use of this source code is governed by a license that\n * can be found in the LICENSE file.\n */\n\nvar p       = require('path');\nvar cst     = require('../constants');\nvar Utility = require('./Utility.js');\nvar Url     = require('url');\nvar util    = require('util')\n\n// Load all env-vars from master.\nvar pm2_env = JSON.parse(process.env.pm2_env);\nfor(var k in pm2_env) {\n  process.env[k] = pm2_env[k];\n}\n\n// Rename process\nprocess.title = process.env.PROCESS_TITLE || 'bun ' + pm2_env.pm_exec_path;\n\ndelete process.env.pm2_env;\n\n/**\n * Main entrance to wrap the desired code\n */\n(function ProcessContainer() {\n  var fs          = require('fs');\n\n  var stdFile     = pm2_env.pm_log_path;\n  var outFile     = pm2_env.pm_out_log_path;\n  var errFile     = pm2_env.pm_err_log_path;\n  var pidFile     = pm2_env.pm_pid_path;\n  var script      = pm2_env.pm_exec_path;\n\n  var original_send = process.send;\n\n  if (typeof(process.env.source_map_support) != 'undefined' &&\n      process.env.source_map_support !== 'false') {\n    require('source-map-support').install();\n  }\n\n  process.send = function() {\n    if (process.connected)\n      original_send.apply(this, arguments);\n  };\n\n  //send node version\n  if (process.versions && process.versions.node) {\n    process.send({\n      'node_version': process.versions.node\n    });\n  }\n\n  if (cst.MODIFY_REQUIRE)\n    require.main.filename = pm2_env.pm_exec_path;\n\n  // Resets global paths for require()\n  require('module')._initPaths();\n\n  try {\n    var pid = process.pid\n    if (typeof(pid) !== 'undefined')\n      fs.writeFileSync(pidFile, process.pid.toString());\n  } catch (e) {\n    console.error(e.stack || e);\n  }\n\n  // Add args to process if args specified on start\n  if (process.env.args != null)\n    process.argv = process.argv.concat(pm2_env.args);\n\n  // stdio, including: out, err and entire (both out and err if necessary).\n  var stds = {\n    out: outFile,\n    err: errFile\n  };\n  stdFile && (stds.std = stdFile);\n\n  // uid/gid management\n  if (pm2_env.uid || pm2_env.gid) {\n    try {\n      if (process.env.gid)\n        process.setgid(pm2_env.gid);\n      if (pm2_env.uid)\n        process.setuid(pm2_env.uid);\n    } catch(e) {\n      setTimeout(function() {\n        console.error('%s on call %s', e.message, e.syscall);\n        console.error('%s is not accessible', pm2_env.uid);\n        return process.exit(1);\n      }, 100);\n    }\n  }\n\n  exec(script, stds);\n})();\n\n/**\n * Description\n * @method exec\n * @param {} script\n * @param {} stds\n * @return\n */\nfunction exec(script, stds) {\n  process.on('message', function (msg) {\n    if (msg.type === 'log:reload') {\n      for (var k in stds){\n        if (typeof stds[k] == 'object' && !isNaN(stds[k].fd)){\n          if (stds[k].destroy) stds[k].destroy();\n          else if (stds[k].end) stds[k].end();\n          else if (stds[k].close) stds[k].close();\n          stds[k] = stds[k]._file;\n        }\n      }\n      Utility.startLogging(stds, function (err) {\n        if (err)\n          return console.error('Failed to reload logs:', err.stack);\n        console.log('Reloading log...');\n      });\n    }\n  });\n\n  var dayjs = null;\n\n  if (pm2_env.log_date_format)\n    dayjs = require('dayjs');\n\n  Utility.startLogging(stds, function (err) {\n    if (err) {\n      process.send({\n        type    : 'process:exception',\n        data    : {\n          message: err.message,\n          syscall: 'ProcessContainer.startLogging'\n        }\n      });\n      throw err;\n      return;\n    }\n\n    const originalConsole = { ...console };\n\n    ['warn', 'error'].forEach((method) => {\n      console[method] = (...args) => {\n        let log_data = null;\n\n        const msg = util.format(...args);\n        //const msg = args.map(arg => (typeof arg === 'object' ? JSON.stringify(arg) : String(arg))).join(' ');\n\n        // Disable logs if specified\n        if (pm2_env.disable_logs === true) {\n          return cb ? cb() : false;\n        }\n\n        if (pm2_env.log_type && pm2_env.log_type === 'json') {\n          log_data = JSON.stringify({\n            message : msg,\n            timestamp : pm2_env.log_date_format && dayjs ?\n              dayjs().format(pm2_env.log_date_format) : new Date().toISOString(),\n            type : 'err',\n            process_id : pm2_env.pm_id,\n            app_name : pm2_env.name\n          }) + '\\n';\n        }\n        else if (pm2_env.log_date_format && dayjs)\n          log_data = `${dayjs().format(pm2_env.log_date_format)}: ${msg}`;\n        else\n          log_data = msg.endsWith('\\n') ? msg : msg + '\\n';\n\n        // Send the log message to the master process\n        process.send({\n          type: 'log:err',\n          data: log_data,\n        });\n\n        stds.std && stds.std.write && stds.std.write(log_data);\n        stds.err && stds.err.write && stds.err.write(log_data);\n      };\n    });\n\n    ['log', 'info'].forEach((method) => {\n      console[method] = (...args) => {\n        let log_data = null;\n\n        const msg = util.format(...args);\n        //const msg = args.map(arg => (typeof arg === 'object' ? JSON.stringify(arg) : String(arg))).join(' ');\n\n        // Disable logs if specified\n        if (pm2_env.disable_logs === true) {\n          return cb ? cb() : false;\n        }\n\n        if (pm2_env.log_type && pm2_env.log_type === 'json') {\n          log_data = JSON.stringify({\n            message : msg,\n            timestamp : pm2_env.log_date_format && dayjs ?\n              dayjs().format(pm2_env.log_date_format) : new Date().toISOString(),\n            type : 'out',\n            process_id : pm2_env.pm_id,\n            app_name : pm2_env.name\n          }) + '\\n';\n        }\n        else if (pm2_env.log_date_format && dayjs)\n          log_data = `${dayjs().format(pm2_env.log_date_format)}: ${msg}`;\n        else\n          log_data = msg.endsWith('\\n') ? msg : msg + '\\n';\n\n        // Send the log message to the master process\n        process.send({\n          type: 'log:out',\n          data: log_data,\n        });\n\n        stds.std && stds.std.write && stds.std.write(log_data);\n        stds.out && stds.out.write && stds.out.write(log_data);\n      };\n    });\n\n    process.stderr.write = (function(write) {\n      return function(string, encoding, cb) {\n        var log_data = null;\n\n        // Disable logs if specified\n        if (pm2_env.disable_logs === true) {\n          return cb ? cb() : false;\n        }\n\n        if (pm2_env.log_type && pm2_env.log_type === 'json') {\n          log_data = JSON.stringify({\n            message : string.toString(),\n            timestamp : pm2_env.log_date_format && dayjs ?\n              dayjs().format(pm2_env.log_date_format) : new Date().toISOString(),\n            type : 'err',\n            process_id : pm2_env.pm_id,\n            app_name : pm2_env.name\n          }) + '\\n';\n        }\n        else if (pm2_env.log_date_format && dayjs)\n          log_data = `${dayjs().format(pm2_env.log_date_format)}: ${string.toString()}`;\n        else\n          log_data = string.toString();\n\n        process.send({\n          type : 'log:err',\n          topic : 'log:err',\n          data : log_data\n        });\n\n        if (Utility.checkPathIsNull(pm2_env.pm_err_log_path) &&\n          (!pm2_env.pm_log_path || Utility.checkPathIsNull(pm2_env.pm_log_path)))\n          return cb ? cb() : false;\n\n        stds.std && stds.std.write && stds.std.write(log_data, encoding);\n        stds.err && stds.err.write && stds.err.write(log_data, encoding, cb);\n      };\n    })(process.stderr.write);\n\n    process.stdout.write = (function(write) {\n      return function(string, encoding, cb) {\n        var log_data = null;\n\n        // Disable logs if specified\n        if (pm2_env.disable_logs === true) {\n          return cb ? cb() : false;\n        }\n\n        if (pm2_env.log_type && pm2_env.log_type === 'json') {\n          log_data = JSON.stringify({\n            message : string.toString(),\n            timestamp : pm2_env.log_date_format && dayjs ?\n              dayjs().format(pm2_env.log_date_format) : new Date().toISOString(),\n            type : 'out',\n            process_id : pm2_env.pm_id,\n            app_name : pm2_env.name\n          }) + '\\n';\n        }\n        else if (pm2_env.log_date_format && dayjs)\n          log_data = `${dayjs().format(pm2_env.log_date_format)}: ${string.toString()}`;\n        else\n          log_data = string.toString();\n\n        process.send({\n          type : 'log:out',\n          data : log_data\n        });\n\n        if (Utility.checkPathIsNull(pm2_env.pm_out_log_path) &&\n          (!pm2_env.pm_log_path || Utility.checkPathIsNull(pm2_env.pm_log_path)))\n          return cb ? cb() : null;\n\n        stds.std && stds.std.write && stds.std.write(log_data, encoding);\n        stds.out && stds.out.write && stds.out.write(log_data, encoding, cb);\n      };\n    })(process.stdout.write);\n\n    function getUncaughtExceptionListener(listener) {\n      return function uncaughtListener(err) {\n        var error = err && err.stack ? err.stack : err;\n\n        if (listener === 'unhandledRejection') {\n          error = 'You have triggered an unhandledRejection, you may have forgotten to catch a Promise rejection:\\n' + error;\n        }\n\n        logError(['std', 'err'], error);\n\n        // Notify master that an uncaughtException has been catched\n        try {\n          if (err) {\n            var errObj = {};\n\n            Object.getOwnPropertyNames(err).forEach(function(key) {\n              errObj[key] = err[key];\n            });\n          }\n\n          process.send({\n            type : 'log:err',\n            topic : 'log:err',\n            data : '\\n' + error + '\\n'\n          });\n\n          process.send({\n            type    : 'process:exception',\n            data    : errObj !== undefined ? errObj : {message: 'No error but ' + listener + ' was caught!'}\n          });\n        } catch(e) {\n          logError(['std', 'err'], 'Channel is already closed can\\'t broadcast error:\\n' + e.stack);\n        }\n\n        if (!process.listeners(listener).filter(function (listener) {\n            return listener !== uncaughtListener;\n        }).length) {\n          if (listener == 'uncaughtException') {\n            process.emit('disconnect');\n            process.exit(cst.CODE_UNCAUGHTEXCEPTION);\n          }\n        }\n      }\n    }\n\n    process.on('uncaughtException', getUncaughtExceptionListener('uncaughtException'));\n    process.on('unhandledRejection', getUncaughtExceptionListener('unhandledRejection'));\n\n    // Change dir to fix process.cwd\n    process.chdir(pm2_env.pm_cwd || process.env.PWD || p.dirname(script));\n\n    require(script);\n\n    function logError(types, error){\n      try {\n        types.forEach(function(type){\n          stds[type] && typeof stds[type].write == 'function' && stds[type].write(error + '\\n');\n        });\n      } catch(e) { }\n    }\n  });\n\n}\n"
  },
  {
    "path": "lib/ProcessContainerFork.js",
    "content": "  /**\n * Copyright 2013-2022 the PM2 project authors. All rights reserved.\n * Use of this source code is governed by a license that\n * can be found in the LICENSE file.\n */\nvar url = require('url');\n// Inject custom modules\nvar ProcessUtils = require('./ProcessUtils')\nProcessUtils.injectModules()\n\nif (typeof(process.env.source_map_support) != \"undefined\" &&\n    process.env.source_map_support !== \"false\") {\n  require('source-map-support').install();\n}\n\n// Rename the process\nprocess.title = process.env.PROCESS_TITLE || 'node ' + process.env.pm_exec_path;\n\nif (process.connected &&\n    process.send &&\n    process.versions &&\n    process.versions.node)\n  process.send({\n    'node_version': process.versions.node\n  });\n\n// Require the real application\nif (process.env.pm_exec_path) {\n  if (ProcessUtils.isESModule(process.env.pm_exec_path) === true) {\n    import(url.pathToFileURL(process.env.pm_exec_path));\n  }\n  else\n    require('module')._load(process.env.pm_exec_path, null, true);\n}\nelse\n  throw new Error('Could not _load() the script');\n\n// Change some values to make node think that the user's application\n// was started directly such as `node app.js`\nprocess.mainModule = process.mainModule || {};\nprocess.mainModule.loaded = false;\nrequire.main = process.mainModule;\n"
  },
  {
    "path": "lib/ProcessContainerForkBun.js",
    "content": "/**\n * Copyright 2013-2022 the PM2 project authors. All rights reserved.\n * Use of this source code is governed by a license that\n * can be found in the LICENSE file.\n */\nvar url = require('url');\n// Inject custom modules\nvar ProcessUtils = require('./ProcessUtils')\nProcessUtils.injectModules()\n\nif (typeof(process.env.source_map_support) != \"undefined\" &&\n    process.env.source_map_support !== \"false\") {\n  require('source-map-support').install();\n}\n\n// Rename the process\nprocess.title = process.env.PROCESS_TITLE || 'bun ' + process.env.pm_exec_path;\n\nif (process.connected &&\n    process.send &&\n    process.versions &&\n    process.versions.node)\n  process.send({\n    'node_version': process.versions.node\n  });\n\nrequire(process.env.pm_exec_path);\n\n// Change some values to make node think that the user's application\n// was started directly such as `node app.js`\nprocess.mainModule = process.mainModule || {};\nprocess.mainModule.loaded = false;\nrequire.main = process.mainModule;\n"
  },
  {
    "path": "lib/ProcessUtils.js",
    "content": "'use strict'\n\nmodule.exports = {\n  injectModules: function() {\n    if (process.env.pmx !== 'false') {\n      const pmx = require('@pm2/io')\n\n      let conf = {}\n      const hasSpecificConfig = typeof process.env.io === 'string' || process.env.trace === 'true'\n      // pmx is already init, no need to do it twice\n      if (hasSpecificConfig === false) return\n\n      if (process.env.io) {\n        const io = JSON.parse(process.env.io)\n        conf = io.conf ? io.conf : conf\n      }\n      pmx.init(Object.assign({\n        tracing: process.env.trace === 'true' || false\n      }, conf))\n    }\n  },\n  isESModule(exec_path) {\n    var fs = require('fs')\n    var path = require('path')\n    var semver = require('semver')\n    var data\n\n    var findPackageJson = function(directory) {\n      var file = path.join(directory, 'package.json')\n      if (fs.existsSync(file) && fs.statSync(file).isFile()) {\n        return file;\n      }\n      var parent = path.resolve(directory, '..')\n      if (parent === directory) {\n        return null;\n      }\n      return findPackageJson(parent)\n    }\n\n    if (semver.satisfies(process.version, '< 13.3.0'))\n      return false\n\n    if (path.extname(exec_path) === '.mjs')\n      return true\n\n    try {\n      data = JSON.parse(fs.readFileSync(findPackageJson(path.dirname(exec_path))))\n      if (data.type === 'module')\n        return true\n      else\n        return false\n    } catch(e) {\n    }\n  }\n}\n"
  },
  {
    "path": "lib/TreeKill.js",
    "content": "'use strict';\n\n// From https://raw.githubusercontent.com/pkrumins/node-tree-kill/master/index.js\n\nvar childProcess = require('child_process');\nvar spawn = childProcess.spawn;\nvar exec = childProcess.exec;\n\nmodule.exports = function (pid, signal, callback) {\n  var tree = {};\n  var pidsToProcess = {};\n  tree[pid] = [];\n  pidsToProcess[pid] = 1;\n\n  switch (process.platform) {\n  case 'win32':\n    exec('taskkill /pid ' + pid + ' /T /F', { windowsHide: true }, callback);\n    break;\n  case 'freebsd':\n  case 'darwin':\n    buildProcessTree(pid, tree, pidsToProcess, function (parentPid) {\n      return spawn('pgrep', ['-P', parentPid]);\n    }, function () {\n      killAll(tree, signal, callback);\n    });\n    break;\n    // case 'sunos':\n    //     buildProcessTreeSunOS(pid, tree, pidsToProcess, function () {\n    //         killAll(tree, signal, callback);\n    //     });\n    //     break;\n  default: // Linux\n    buildProcessTree(pid, tree, pidsToProcess, function (parentPid) {\n      return spawn('ps', ['-o', 'pid', '--no-headers', '--ppid', parentPid]);\n    }, function () {\n      killAll(tree, signal, callback);\n    });\n    break;\n  }\n};\n\nfunction killAll (tree, signal, callback) {\n  var killed = {};\n  try {\n    Object.keys(tree).forEach(function (pid) {\n      tree[pid].forEach(function (pidpid) {\n        if (!killed[pidpid]) {\n          killPid(pidpid, signal);\n          killed[pidpid] = 1;\n        }\n      });\n      if (!killed[pid]) {\n        killPid(pid, signal);\n        killed[pid] = 1;\n      }\n    });\n  } catch (err) {\n    if (callback) {\n      return callback(err);\n    } else {\n      console.error(err);\n    }\n  }\n  if (callback) {\n    return callback();\n  }\n}\n\nfunction killPid(pid, signal) {\n  try {\n    process.kill(parseInt(pid, 10), signal);\n  }\n  catch (err) {\n    if (err.code !== 'ESRCH')\n      console.error(err);\n  }\n}\n\nfunction buildProcessTree (parentPid, tree, pidsToProcess, spawnChildProcessesList, cb) {\n  var ps = spawnChildProcessesList(parentPid);\n  var allData = '';\n\n  ps.on('error', function(err) {\n    console.error(err);\n  });\n\n  if (ps.stdout) {\n    ps.stdout.on('data', function (data) {\n      data = data.toString('ascii');\n      allData += data;\n    });\n  }\n\n  var onClose = function (code) {\n    delete pidsToProcess[parentPid];\n\n    if (code !== 0) {\n      // no more parent processes\n      if (Object.keys(pidsToProcess).length == 0) {\n        cb();\n      }\n      return;\n    }\n    var pids = allData.match(/\\d+/g) || [];\n    if (pids.length === 0)\n      return cb();\n\n    pids.forEach(function (pid) {\n      pid = parseInt(pid, 10);\n      tree[parentPid].push(pid);\n      tree[pid] = [];\n      pidsToProcess[pid] = 1;\n      buildProcessTree(pid, tree, pidsToProcess, spawnChildProcessesList, cb);\n    });\n  };\n\n  ps.on('close', onClose);\n}\n"
  },
  {
    "path": "lib/Utility.js",
    "content": "/**\n * Copyright 2013-2022 the PM2 project authors. All rights reserved.\n * Use of this source code is governed by a license that\n * can be found in the LICENSE file.\n */\n\n/**\n * Common Utilities ONLY USED IN ->DAEMON<-\n */\n\nvar fclone    = require('fclone');\nvar fs        = require('fs');\nvar cst       = require('../constants.js');\nvar waterfall = require('async/waterfall');\nvar util      = require('util');\nvar url       = require('url');\nvar dayjs     = require('dayjs');\nvar findPackageJson = require('./tools/find-package-json')\n\nvar Utility = module.exports = {\n  findPackageVersion : function(fullpath) {\n    var version\n\n    try {\n      version = findPackageJson(fullpath).next().value.version\n    } catch(e) {\n      version = 'N/A'\n    }\n    return version\n  },\n  getDate : function() {\n    return Date.now();\n  },\n  extendExtraConfig : function(proc, opts) {\n    if (opts.env && opts.env.current_conf) {\n      if (opts.env.current_conf.env &&\n          typeof(opts.env.current_conf.env) === 'object' &&\n          Object.keys(opts.env.current_conf.env).length === 0)\n        delete opts.env.current_conf.env\n\n      Utility.extendMix(proc.pm2_env, opts.env.current_conf);\n      delete opts.env.current_conf;\n    }\n  },\n  formatCLU : function(process) {\n    if (!process.pm2_env) {\n      return process;\n    }\n\n    var obj = Utility.clone(process.pm2_env);\n    delete obj.env;\n\n    return obj;\n  },\n  extend : function(destination, source){\n    if (!source || typeof source != 'object') return destination;\n\n      Object.keys(source).forEach(function(new_key) {\n        if (source[new_key] != '[object Object]')\n          destination[new_key] = source[new_key];\n      });\n\n    return destination;\n  },\n  // Same as extend but drop value with 'null'\n  extendMix : function(destination, source){\n    if (!source || typeof source != 'object') return destination;\n\n    Object.keys(source).forEach(function(new_key) {\n      if (source[new_key] == 'null')\n        delete destination[new_key];\n      else\n        destination[new_key] = source[new_key]\n    });\n\n    return destination;\n  },\n\n  whichFileExists : function(file_arr) {\n    var f = null;\n\n    file_arr.some(function(file) {\n      try {\n        fs.statSync(file);\n      } catch(e) {\n        return false;\n      }\n      f = file;\n      return true;\n    });\n    return f;\n  },\n  clone     : function(obj) {\n    if (obj === null || obj === undefined) return {};\n    return fclone(obj);\n  },\n  overrideConsole : function(bus) {\n    if (cst.PM2_LOG_DATE_FORMAT && typeof cst.PM2_LOG_DATE_FORMAT == 'string') {\n      // Generate timestamp prefix\n      function timestamp(){\n        return `${dayjs(Date.now()).format(cst.PM2_LOG_DATE_FORMAT)}:`;\n      }\n\n      var hacks = ['info', 'log', 'error', 'warn'], consoled = {};\n\n      // store console functions.\n      hacks.forEach(function(method){\n        consoled[method] = console[method];\n      });\n\n      hacks.forEach(function(k){\n        console[k] = function(){\n          if (bus) {\n            bus.emit('log:PM2', {\n              process : {\n                pm_id      : 'PM2',\n                name       : 'PM2',\n                rev        : null\n              },\n              at  : Utility.getDate(),\n              data : util.format.apply(this, arguments) + '\\n'\n            });\n          }\n          // do not destroy variable insertion\n          arguments[0] && (arguments[0] = timestamp() + ' PM2 ' + k + ': ' + arguments[0]);\n          consoled[k].apply(console, arguments);\n        };\n      });\n    }\n  },\n  startLogging : function(stds, callback) {\n    /**\n     * Start log outgoing messages\n     * @method startLogging\n     * @param {} callback\n     * @return\n     */\n    // Make sure directories of `logs` and `pids` exist.\n    // try {\n    //   ['logs', 'pids'].forEach(function(n){\n    //     console.log(n);\n    //     (function(_path){\n    //       !fs.existsSync(_path) && fs.mkdirSync(_path, '0755');\n    //     })(path.resolve(cst.PM2_ROOT_PATH, n));\n    //   });\n    // } catch(err) {\n    //   return callback(new Error('can not create directories (logs/pids):' + err.message));\n    // }\n\n    // waterfall.\n    var flows = [];\n    // types of stdio, should be sorted as `std(entire log)`, `out`, `err`.\n    var types = Object.keys(stds).sort(function(x, y){\n      return -x.charCodeAt(0) + y.charCodeAt(0);\n    });\n\n    // Create write streams.\n    (function createWS(io){\n      if(io.length != 1){\n        return false;\n      }\n      io = io[0];\n\n      // If `std` is a Stream type, try next `std`.\n      // compatible with `pm2 reloadLogs`\n      if(typeof stds[io] == 'object' && !isNaN(stds[io].fd)){\n        return createWS(types.splice(0, 1));\n      }\n\n      flows.push(function(next){\n        var file = stds[io];\n\n        // if file contains ERR or /dev/null, dont try to create stream since he dont want logs\n        if (!file || file.indexOf('NULL') > -1 || file.indexOf('/dev/null') > -1)\n          return next();\n\n        stds[io] = fs.createWriteStream(file, {flags: 'a'})\n          .once('error', next)\n          .on('open', function(){\n            stds[io].removeListener('error', next);\n\n            stds[io].on('error', function(err) {\n              console.error(err);\n            });\n\n            next();\n          });\n        stds[io]._file = file;\n      });\n      return createWS(types.splice(0, 1));\n    })(types.splice(0, 1));\n\n    waterfall(flows, callback);\n  },\n\n  /**\n   * Function parse the module name and returns it as canonic:\n   * - Makes the name based on installation filename.\n   * - Removes the Github author, module version and git branch from original name.\n   *\n   * @param {string} module_name\n   * @returns {string} Canonic module name (without trimed parts).\n   * @example Always returns 'pm2-slack' for inputs 'ma-zal/pm2-slack', 'ma-zal/pm2-slack#own-branch',\n   *          'pm2-slack-1.0.0.tgz' or 'pm2-slack@1.0.0'.\n   */\n  getCanonicModuleName: function(module_name) {\n    if (typeof module_name !== 'string') return null;\n    var canonic_module_name = module_name;\n\n    // Returns the module name from a .tgz package name (or the original name if it is not a valid pkg).\n    // Input: The package name (e.g. \"foo.tgz\", \"foo-1.0.0.tgz\", \"folder/foo.tgz\")\n    // Output: The module name\n    if (canonic_module_name.match(/\\.tgz($|\\?)/)) {\n      if (canonic_module_name.match(/^(.+\\/)?([^\\/]+)\\.tgz($|\\?)/)) {\n        canonic_module_name = canonic_module_name.match(/^(.+\\/)?([^\\/]+)\\.tgz($|\\?)/)[2];\n        if (canonic_module_name.match(/^(.+)-[0-9]+\\.[0-9]+\\.[0-9]+(-[a-zA-Z0-9_]+\\.[0-9]+)?$/)) {\n          canonic_module_name = canonic_module_name.match(/^(.+)-[0-9]+\\.[0-9]+\\.[0-9]+(-[a-zA-Z0-9_]+\\.[0-9]+)?$/)[1];\n        }\n      }\n    }\n\n    //pm2 install git+https://github.com/user/module\n    if(canonic_module_name.indexOf('git+') !== -1) {\n      canonic_module_name = canonic_module_name.split('/').pop();\n    }\n\n    //pm2 install https://github.com/user/module\n    if(canonic_module_name.indexOf('http') !== -1) {\n      var uri = url.parse(canonic_module_name);\n      canonic_module_name = uri.pathname.split('/').pop();\n    }\n\n    //pm2 install file:///home/user/module\n    else if(canonic_module_name.indexOf('file://') === 0) {\n      canonic_module_name = canonic_module_name.replace(/\\/$/, '').split('/').pop();\n    }\n\n    //pm2 install username/module\n    else if(canonic_module_name.indexOf('/') !== -1) {\n      if (canonic_module_name.charAt(0) !== \"@\"){\n        canonic_module_name = canonic_module_name.split('/')[1];\n      }\n    }\n\n    //pm2 install @somescope/module@2.1.0-beta\n    if(canonic_module_name.lastIndexOf('@') > 0) {\n      canonic_module_name = canonic_module_name.substr(0,canonic_module_name.lastIndexOf(\"@\"));\n    }\n\n    //pm2 install module#some-branch\n    if(canonic_module_name.indexOf('#') !== -1) {\n      canonic_module_name = canonic_module_name.split('#')[0];\n    }\n\n    if (canonic_module_name.indexOf('.git') !== -1) {\n      canonic_module_name = canonic_module_name.replace('.git', '');\n    }\n\n    return canonic_module_name;\n  },\n\n  checkPathIsNull: function(path) {\n    return path === 'NULL' || path === '/dev/null' || path === '\\\\\\\\.\\\\NUL';\n  },\n\n  generateUUID: function () {\n    var s = [];\n    var hexDigits = \"0123456789abcdef\";\n    for (var i = 0; i < 36; i++) {\n      s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);\n    }\n    s[14] = \"4\";\n    s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1);\n    s[8] = s[13] = s[18] = s[23] = \"-\";\n    return s.join(\"\");\n  }\n\n};\n"
  },
  {
    "path": "lib/VersionCheck.js",
    "content": "\nvar vCheck = require('@pm2/pm2-version-check')\nvar semver = require('semver')\nvar fs     = require('fs')\nvar os     = require('os')\n\nfunction hasDockerEnv() {\n\ttry {\n\t\tfs.statSync('/.dockerenv');\n\t\treturn true;\n\t} catch (_) {\n\t\treturn false;\n\t}\n}\n\nfunction hasDockerCGroup() {\n\ttry {\n\t\treturn fs.readFileSync('/proc/self/cgroup', 'utf8').includes('docker');\n\t} catch (_) {\n\t\treturn false;\n\t}\n}\n\nmodule.exports = function (opts) {\n  var params = {\n    state: opts.state,\n    version: opts.version\n  }\n\n  try {\n    params.os = os.type()\n    params.uptime = Math.floor(process.uptime())\n    params.nodev = process.versions.node\n    params.docker = hasDockerEnv() || hasDockerCGroup()\n  } catch(e) {\n  }\n\n  vCheck.runCheck(params, (err, pkg) => {\n    if (err) return false\n    if (!pkg.current_version) return false\n    if (opts.version && semver.lt(opts.version, pkg.current_version)) {\n      console.log('[PM2] This PM2 is not UP TO DATE')\n      console.log('[PM2] Upgrade to version %s', pkg.current_version)\n    }\n  })\n}\n"
  },
  {
    "path": "lib/Watcher.js",
    "content": "/**\n * Copyright 2013-2022 the PM2 project authors. All rights reserved.\n * Use of this source code is governed by a license that\n * can be found in the LICENSE file.\n */\nvar chokidar = require('chokidar');\nvar util     = require('util');\nvar log      = require('debug')('pm2:watch');\n\nmodule.exports = function ClusterMode(God) {\n  /**\n   * Watch folder for changes and restart\n   * @method watch\n   * @param {Object} pm2_env pm2 app environnement\n   * @return MemberExpression\n   */\n  God.watch = {};\n\n  God.watch._watchers = {};\n\n  God.watch.enable = function(pm2_env) {\n    if (God.watch._watchers[pm2_env.pm_id]) {\n      God.watch._watchers[pm2_env.pm_id].close();\n      God.watch._watchers[pm2_env.pm_id] = null;\n      delete God.watch._watchers[pm2_env.pm_id];\n    }\n\n    log('Initial watch ', pm2_env.watch)\n\n    var watch = pm2_env.watch\n\n    if(typeof watch == 'boolean' || Array.isArray(watch) && watch.length === 0)\n      watch = pm2_env.pm_cwd;\n\n    log('Watching %s', watch);\n\n    var watch_options = {\n      ignored       : pm2_env.ignore_watch || /[\\/\\\\]\\.|node_modules/,\n      persistent    : true,\n      ignoreInitial : true,\n      cwd: pm2_env.pm_cwd\n    };\n\n    if (pm2_env.watch_options) {\n      watch_options = Object.assign(watch_options, pm2_env.watch_options);\n    }\n\n    log('Watch opts', watch_options);\n\n    var watcher = chokidar.watch(watch, watch_options);\n\n    console.log('[Watch] Start watching', pm2_env.name);\n\n    watcher.on('all', function(event, path) {\n      var self = this;\n\n      if (self.restarting === true) {\n        log('Already restarting, skipping');\n        return false;\n      }\n\n      self.restarting = true;\n\n      console.log('Change detected on path %s for app %s - restarting', path, pm2_env.name);\n\n      setTimeout(function() {\n        God.restartProcessName(pm2_env.name, function(err, list) {\n          self.restarting = false;\n\n          if (err) {\n            log('Error while restarting', err);\n            return false;\n          }\n\n          return log('Process restarted');\n        });\n      }, (pm2_env.watch_delay || 0));\n\n      return false;\n    });\n\n    watcher.on('error', function(e) {\n      console.error(e.stack || e);\n    });\n\n    God.watch._watchers[pm2_env.pm_id] = watcher;\n\n    //return God.watch._watchers[pm2_env.name];\n  },\n  /**\n   * Description\n   * @method close\n   * @param {} id\n   * @return\n   */\n  God.watch.disableAll = function() {\n    var watchers = God.watch._watchers;\n\n    console.log('[Watch] PM2 is being killed. Watch is disabled to avoid conflicts');\n    for (var i in watchers) {\n      watchers[i].close && watchers[i].close();\n      watchers.splice(i, 1);\n    }\n  },\n\n  God.watch.disable = function(pm2_env) {\n    var watcher = God.watch._watchers[pm2_env.pm_id]\n    if (watcher) {\n      console.log('[Watch] Stop watching', pm2_env.name);\n      watcher.close();\n      delete God.watch._watchers[pm2_env.pm_id];\n      return true;\n    } else {\n      return false;\n    }\n  }\n};\n"
  },
  {
    "path": "lib/Worker.js",
    "content": "/**\n * Copyright 2013-2022 the PM2 project authors. All rights reserved.\n * Use of this source code is governed by a license that\n * can be found in the LICENSE file.\n */\nconst eachLimit = require('async/eachLimit');\nconst debug     = require('debug')('pm2:worker');\nconst domain    = require('domain');\nconst Cron      = require('croner');\nconst pkg       = require('../package.json');\n\nvar cst    = require('../constants.js');\nvar vCheck = require('./VersionCheck.js')\n\nmodule.exports = function(God) {\n  var timer = null;\n\n  God.CronJobs = new Map();\n  God.Worker = {};\n  God.Worker.is_running = false;\n\n  God.getCronID = function(pm_id) {\n    return `cron-${pm_id}`\n  }\n\n  God.registerCron = function(pm2_env) {\n    if (!pm2_env ||\n        pm2_env.pm_id === undefined ||\n        !pm2_env.cron_restart ||\n        pm2_env.cron_restart == '0' ||\n        God.CronJobs.has(God.getCronID(pm2_env.pm_id)))\n      return;\n\n    var pm_id = pm2_env.pm_id\n    console.log('[PM2][WORKER] Registering a cron job on:', pm_id);\n\n    var job = Cron(pm2_env.cron_restart, function() {\n      God.restartProcessId({id: pm_id}, function(err, data) {\n        if (err)\n          console.error(err.stack || err);\n        return;\n      });\n    });\n\n    God.CronJobs.set(God.getCronID(pm_id), job);\n  }\n\n\n  /**\n   * Deletes the cron job on deletion of process\n   */\n  God.deleteCron = function(id) {\n    if (typeof(id) !== 'undefined' && God.CronJobs.has(God.getCronID(id)) === false)\n      return;\n    console.log('[PM2] Deregistering a cron job on:', id);\n    var job = God.CronJobs.get(God.getCronID(id));\n\n    if (job)\n      job.stop();\n\n    God.CronJobs.delete(God.getCronID(id));\n  };\n\n  var _getProcessById = function(pm_id) {\n    var proc = God.clusters_db[pm_id];\n    return proc ? proc : null;\n  };\n\n\n  var maxMemoryRestart = function(proc_key, cb) {\n    var proc = _getProcessById(proc_key.pm2_env.pm_id);\n\n    if (!(proc &&\n          proc.pm2_env &&\n          proc_key.monit))\n      return cb();\n\n    if (proc_key.monit.memory !== undefined &&\n        proc.pm2_env.max_memory_restart !== undefined &&\n        proc.pm2_env.max_memory_restart < proc_key.monit.memory &&\n        proc.pm2_env.axm_options &&\n        proc.pm2_env.axm_options.pid === undefined) {\n      console.log('[PM2][WORKER] Process %s restarted because it exceeds --max-memory-restart value (current_memory=%s max_memory_limit=%s [octets])', proc.pm2_env.pm_id, proc_key.monit.memory, proc.pm2_env.max_memory_restart);\n      God.reloadProcessId({\n        id : proc.pm2_env.pm_id\n      }, function(err, data) {\n        if (err)\n          console.error(err.stack || err);\n        return cb();\n      });\n    }\n    else {\n      return cb();\n    }\n  };\n\n  var tasks = function() {\n    if (God.Worker.is_running === true) {\n      debug('[PM2][WORKER] Worker is already running, skipping this round');\n      return false;\n    }\n    God.Worker.is_running = true;\n\n    God.getMonitorData(null, function(err, data) {\n      if (err || !data || typeof(data) !== 'object') {\n        God.Worker.is_running = false;\n        return console.error(err);\n      }\n\n      eachLimit(data, 1, function(proc, next) {\n        if (!proc || !proc.pm2_env || proc.pm2_env.pm_id === undefined)\n          return next();\n\n        debug('[PM2][WORKER] Processing proc id:', proc.pm2_env.pm_id);\n\n        // Reset restart delay if application has an uptime of more > 30secs\n        if (proc.pm2_env.exp_backoff_restart_delay !== undefined &&\n            proc.pm2_env.prev_restart_delay && proc.pm2_env.prev_restart_delay > 0) {\n          var app_uptime = Date.now() - proc.pm2_env.pm_uptime\n          if (app_uptime > cst.EXP_BACKOFF_RESET_TIMER) {\n            var ref_proc = _getProcessById(proc.pm2_env.pm_id);\n            ref_proc.pm2_env.prev_restart_delay = 0\n            console.log(`[PM2][WORKER] Reset the restart delay, as app ${proc.name} has been up for more than ${cst.EXP_BACKOFF_RESET_TIMER}ms`)\n          }\n        }\n\n        // Check if application has reached memory threshold\n        maxMemoryRestart(proc, function() {\n          return next();\n        });\n      }, function(err) {\n        God.Worker.is_running = false;\n        debug('[PM2][WORKER] My job here is done, next job in %d seconds', parseInt(cst.WORKER_INTERVAL / 1000));\n      });\n    });\n  };\n\n  var wrappedTasks = function() {\n    var d = domain.create();\n\n    d.once('error', function(err) {\n      console.error('[PM2][WORKER] Error caught by domain:\\n' + (err.stack || err));\n      God.Worker.is_running = false;\n    });\n\n    d.run(function() {\n      tasks();\n    });\n  };\n\n\n  God.Worker.start = function() {\n    timer = setInterval(wrappedTasks, cst.WORKER_INTERVAL);\n\n    if (!process.env.PM2_DISABLE_VERSION_CHECK) {\n      setInterval(() => {\n        vCheck({\n          state: 'check',\n          version: pkg.version,\n        });\n      }, 1000 * 60 * 60 * 24);\n    }\n  };\n\n  God.Worker.stop = function() {\n    if (timer !== null)\n      clearInterval(timer);\n  };\n};\n"
  },
  {
    "path": "lib/binaries/CLI.js",
    "content": "'use strict';\n\nprocess.env.PM2_USAGE = 'CLI';\n\nvar cst          = require('../../constants.js');\n\nvar commander    = require('commander');\nvar chalk        = require('ansis');\nvar forEachLimit = require('async/forEachLimit');\n\nvar debug        = require('debug')('pm2:cli');\nvar PM2          = require('../API.js');\nvar pkg          = require('../../package.json');\nvar tabtab       = require('../completion.js');\nvar Common       = require('../Common.js');\nvar PM2ioHandler = require('../API/pm2-plus/PM2IO');\n\nvar semver = require('semver')\n\nif (cst.IS_BUN === true && semver.lt(process.versions.bun, '1.1.25')) {\n  throw new Error('PM2 cannot run on Bun version < 1.1.25 (cluster support)')\n}\n\nCommon.determineSilentCLI();\nCommon.printVersion();\n\nvar pm2 = new PM2();\n\nPM2ioHandler.usePM2Client(pm2)\n\ncommander.version(pkg.version)\n  .option('-v --version', 'print pm2 version')\n  .option('-s --silent', 'hide all messages', false)\n  .option('--ext <extensions>', 'watch only this file extensions')\n  .option('-n --name <name>', 'set a name for the process in the process list')\n  .option('-m --mini-list', 'display a compacted list without formatting')\n  .option('--interpreter <interpreter>', 'set a specific interpreter to use for executing app, default: node')\n  .option('--interpreter-args <arguments>', 'set arguments to pass to the interpreter (alias of --node-args)')\n  .option('--node-args <node_args>', 'space delimited arguments to pass to node')\n  .option('-o --output <path>', 'specify log file for stdout')\n  .option('-e --error <path>', 'specify log file for stderr')\n  .option('-l --log [path]', 'specify log file which gathers both stdout and stderr')\n  .option('--filter-env [envs]', 'filter out outgoing global values that contain provided strings', function(v, m) { m.push(v); return m;}, [])\n  .option('--log-type <type>', 'specify log output style (raw by default, json optional)')\n  .option('--log-date-format <date format>', 'add custom prefix timestamp to logs')\n  .option('--time', 'enable time logging')\n  .option('--disable-logs', 'disable all logs storage')\n  .option('--env <environment_name>', 'specify which set of environment variables from ecosystem file must be injected')\n  .option('-a --update-env', 'force an update of the environment with restart/reload (-a <=> apply)')\n  .option('-f --force', 'force actions')\n  .option('-i --instances <number>', 'launch [number] instances (for networked app)(load balanced)')\n  .option('--parallel <number>', 'number of parallel actions (for restart/reload)')\n  .option('--shutdown-with-message', 'shutdown an application with process.send(\\'shutdown\\') instead of process.kill(pid, SIGINT)')\n  .option('-p --pid <pid>', 'specify pid file')\n  .option('-k --kill-timeout <delay>', 'delay before sending final SIGKILL signal to process')\n  .option('--listen-timeout <delay>', 'listen timeout on application reload')\n  .option('--max-memory-restart <memory>', 'Restart the app if an amount of memory is exceeded (in bytes)')\n  .option('--restart-delay <delay>', 'specify a delay between restarts (in milliseconds)')\n  .option('--exp-backoff-restart-delay <delay>', 'specify a delay between restarts (in milliseconds)')\n  .option('-x --execute-command', 'execute a program using fork system')\n  .option('--max-restarts [count]', 'only restart the script COUNT times')\n  .option('-u --user <username>', 'define user when generating startup script')\n  .option('--uid <uid>', 'run target script with <uid> rights')\n  .option('--gid <gid>', 'run target script with <gid> rights')\n  .option('--namespace <ns>', 'start application within specified namespace')\n  .option('--cwd <path>', 'run target script from path <cwd>')\n  .option('--hp <home path>', 'define home path when generating startup script')\n  .option('--wait-ip', 'override systemd script to wait for full internet connectivity to launch pm2')\n  .option('--service-name <name>', 'define service name when generating startup script')\n  .option('-c --cron <cron_pattern>', 'restart a running process based on a cron pattern')\n  .option('-c --cron-restart <cron_pattern>', '(alias) restart a running process based on a cron pattern')\n  .option('-w --write', 'write configuration in local folder')\n  .option('--no-daemon', 'run pm2 daemon in the foreground if it doesn\\'t exist already')\n  .option('--source-map-support', 'force source map support')\n  .option('--only <application-name>', 'with json declaration, allow to only act on one application')\n  .option('--disable-source-map-support', 'force source map support')\n  .option('--wait-ready', 'ask pm2 to wait for ready event from your app')\n  .option('--merge-logs', 'merge logs from different instances but keep error and out separated')\n  .option('--watch [paths]', 'watch application folder for changes', function(v, m) { m.push(v); return m;}, [])\n  .option('--ignore-watch <folders|files>', 'List of paths to ignore (name or regex)')\n  .option('--watch-delay <delay>', 'specify a restart delay after changing files (--watch-delay 4 (in sec) or 4000ms)')\n  .option('--no-color', 'skip colors')\n  .option('--no-vizion', 'start an app without vizion feature (versioning control)')\n  .option('--no-autostart', 'add an app without automatic start')\n  .option('--no-autorestart', 'start an app without automatic restart')\n  .option('--stop-exit-codes <exit_codes...>', 'specify a list of exit codes that should skip automatic restart')\n  .option('--no-treekill', 'Only kill the main process, not detached children')\n  .option('--no-pmx', 'start an app without pmx')\n  .option('--no-automation', 'start an app without pmx')\n  .option('--trace', 'enable transaction tracing with km')\n  .option('--disable-trace', 'disable transaction tracing with km')\n  .option('--sort <field_name:sort>', 'sort process according to field\\'s name')\n  .option('--attach', 'attach logging after your start/restart/stop/reload')\n  .option('--v8', 'enable v8 data collecting')\n  .option('--event-loop-inspector', 'enable event-loop-inspector dump in pmx')\n  .option('--deep-monitoring', 'enable all monitoring tools (equivalent to --v8 --event-loop-inspector --trace)')\n  .usage('[cmd] app');\n\nfunction displayUsage() {\n  console.log('usage: pm2 [options] <command>')\n  console.log('');\n  console.log('pm2 -h, --help             all available commands and options');\n  console.log('pm2 examples               display pm2 usage examples');\n  console.log('pm2 <command> -h           help on a specific command');\n  console.log('');\n  console.log('Access pm2 files in ~/.pm2');\n}\n\nfunction displayExamples() {\n  console.log('- Start and add a process to the pm2 process list:')\n  console.log('');\n  console.log(chalk.cyan('  $ pm2 start app.js --name app'));\n  console.log('');\n  console.log('- Show the process list:');\n  console.log('');\n  console.log(chalk.cyan('  $ pm2 ls'));\n  console.log('');\n  console.log('- Stop and delete a process from the pm2 process list:');\n  console.log('');\n  console.log(chalk.cyan('  $ pm2 delete app'));\n  console.log('');\n  console.log('- Stop, start and restart a process from the process list:');\n  console.log('');\n  console.log(chalk.cyan('  $ pm2 stop app'));\n  console.log(chalk.cyan('  $ pm2 start app'));\n  console.log(chalk.cyan('  $ pm2 restart app'));\n  console.log('');\n  console.log('- Clusterize an app to all CPU cores available:');\n  console.log('');\n  console.log(chalk.cyan('  $ pm2 start -i max'));\n  console.log('');\n  console.log('- Update pm2 :');\n  console.log('');\n  console.log(chalk.cyan('  $ npm install pm2 -g && pm2 update'));\n  console.log('');\n  console.log('- Install pm2 auto completion:')\n  console.log('');\n  console.log(chalk.cyan('  $ pm2 completion install'))\n  console.log('');\n  console.log('Check the full documentation on https://pm2.keymetrics.io/');\n  console.log('');\n}\n\nfunction beginCommandProcessing() {\n  pm2.getVersion(function(err, remote_version) {\n    if (!err && (pkg.version != remote_version)) {\n      console.log('');\n      console.log(chalk.red.bold('>>>> In-memory PM2 is out-of-date, do:\\n>>>> $ pm2 update'));\n      console.log('In memory PM2 version:', chalk.blue.bold(remote_version));\n      console.log('Local PM2 version:', chalk.blue.bold(pkg.version));\n      console.log('');\n    }\n  });\n  commander.parse(process.argv);\n}\n\nfunction checkCompletion(){\n  return tabtab.complete('pm2', function(err, data) {\n    if(err || !data) return;\n    if(/^--\\w?/.test(data.last)) return tabtab.log(commander.options.map(function (data) {\n      return data.long;\n    }), data);\n    if(/^-\\w?/.test(data.last)) return tabtab.log(commander.options.map(function (data) {\n      return data.short;\n    }), data);\n    // array containing commands after which process name should be listed\n    var cmdProcess = ['stop', 'restart', 'scale', 'reload', 'delete', 'reset', 'pull', 'forward', 'backward', 'logs', 'describe', 'desc', 'show'];\n\n    if (cmdProcess.indexOf(data.prev) > -1) {\n      pm2.list(function(err, list){\n        tabtab.log(list.map(function(el){ return el.name }), data);\n        pm2.disconnect();\n      });\n    }\n    else if (data.prev == 'pm2') {\n      tabtab.log(commander.commands.map(function (data) {\n        return data._name;\n      }), data);\n      pm2.disconnect();\n    }\n    else\n      pm2.disconnect();\n  });\n};\n\nvar _arr = process.argv.indexOf('--') > -1 ? process.argv.slice(0, process.argv.indexOf('--')) : process.argv;\n\nif (_arr.indexOf('log') > -1) {\n  process.argv[_arr.indexOf('log')] = 'logs';\n}\n\nif (_arr.indexOf('--no-daemon') > -1) {\n  //\n  // Start daemon if it does not exist\n  //\n  // Function checks if --no-daemon option is present,\n  // and starts daemon in the same process if it does not exist\n  //\n  console.log('pm2 launched in no-daemon mode (you can add DEBUG=\"*\" env variable to get more messages)');\n\n  var pm2NoDaeamon = new PM2({\n    daemon_mode : false\n  });\n\n  pm2NoDaeamon.connect(function() {\n    pm2 = pm2NoDaeamon;\n    beginCommandProcessing();\n  });\n\n}\nelse if (_arr.indexOf('startup') > -1 || _arr.indexOf('unstartup') > -1) {\n  setTimeout(function() {\n    commander.parse(process.argv);\n  }, 100);\n}\nelse {\n  // HERE we instanciate the Client object\n  pm2.connect(function() {\n    debug('Now connected to daemon');\n    if (process.argv.slice(2)[0] === 'completion') {\n      checkCompletion();\n      //Close client if completion related installation\n      var third = process.argv.slice(3)[0];\n      if ( third == null || third === 'install' || third === 'uninstall')\n        pm2.disconnect();\n    }\n    else {\n      beginCommandProcessing();\n    }\n  });\n}\n\n//\n// Helper function to fail when unknown command arguments are passed\n//\nfunction failOnUnknown(fn) {\n  return function(arg) {\n    if (arguments.length > 1) {\n      console.log(cst.PREFIX_MSG + '\\nUnknown command argument: ' + arg);\n      commander.outputHelp();\n      process.exit(cst.ERROR_EXIT);\n    }\n    return fn.apply(this, arguments);\n  };\n}\n\n/**\n * @todo to remove at some point once it's fixed in official commander.js\n * https://github.com/tj/commander.js/issues/475\n *\n * Patch Commander.js Variadic feature\n */\nfunction patchCommanderArg(cmd) {\n  var argsIndex;\n  if ((argsIndex = commander.rawArgs.indexOf('--')) >= 0) {\n    var optargs = commander.rawArgs.slice(argsIndex + 1);\n    cmd = cmd.slice(0, cmd.indexOf(optargs[0]));\n  }\n  return cmd;\n}\n\n//\n// Start command\n//\ncommander.command('start [name|namespace|file|ecosystem|id...]')\n  .option('--watch', 'Watch folder for changes')\n  .option('--fresh', 'Rebuild Dockerfile')\n  .option('--daemon', 'Run container in Daemon mode (debug purposes)')\n  .option('--container', 'Start application in container mode')\n  .option('--dist', 'with --container; change local Dockerfile to containerize all files in current directory')\n  .option('--image-name [name]', 'with --dist; set the exported image name')\n  .option('--node-version [major]', 'with --container, set a specific major Node.js version')\n  .option('--dockerdaemon', 'for debugging purpose')\n  .description('start and daemonize an app')\n  .action(function(cmd, opts) {\n    if (opts.container == true && opts.dist == true)\n      return pm2.dockerMode(cmd, opts, 'distribution');\n    else if (opts.container == true)\n      return pm2.dockerMode(cmd, opts, 'development');\n\n    if (cmd == \"-\") {\n      process.stdin.resume();\n      process.stdin.setEncoding('utf8');\n      process.stdin.on('data', function (cmd) {\n        process.stdin.pause();\n        pm2._startJson(cmd, commander, 'restartProcessId', 'pipe');\n      });\n    }\n    else {\n      // Commander.js patch\n      cmd = patchCommanderArg(cmd);\n      if (cmd.length === 0) {\n        cmd = [cst.APP_CONF_DEFAULT_FILE];\n      }\n      let acc = []\n      forEachLimit(cmd, 1, function(script, next) {\n        pm2.start(script, commander, (err, apps) => {\n          acc = acc.concat(apps)\n          next(err)\n        });\n      }, function(err, dt) {\n        if (err && err.message &&\n            (err.message.includes('Script not found') === true ||\n             err.message.includes('NOT AVAILABLE IN PATH') === true)) {\n          pm2.exitCli(1)\n        }\n        else\n          pm2.speedList(err ? 1 : 0, acc);\n      });\n    }\n  });\n\ncommander.command('trigger <id|proc_name|namespace|all> <action_name> [params]')\n  .description('trigger process action')\n  .action(function(pm_id, action_name, params) {\n    pm2.trigger(pm_id, action_name, params);\n  });\n\ncommander.command('deploy <file|environment>')\n  .description('deploy your json')\n  .action(function(cmd) {\n    pm2.deploy(cmd, commander);\n  });\n\ncommander.command('startOrRestart <json>')\n  .description('start or restart JSON file')\n  .action(function(file) {\n    pm2._startJson(file, commander, 'restartProcessId');\n  });\n\ncommander.command('startOrReload <json>')\n  .description('start or gracefully reload JSON file')\n  .action(function(file) {\n    pm2._startJson(file, commander, 'reloadProcessId');\n  });\n\ncommander.command('pid [app_name]')\n  .description('return pid of [app_name] or all')\n  .action(function(app) {\n    pm2.getPID(app);\n  });\n\ncommander.command('create')\n  .description('return pid of [app_name] or all')\n  .action(function() {\n    pm2.boilerplate()\n  });\n\ncommander.command('startOrGracefulReload <json>')\n  .description('start or gracefully reload JSON file')\n  .action(function(file) {\n    pm2._startJson(file, commander, 'reloadProcessId');\n  });\n\n//\n// Stop specific id\n//\ncommander.command('stop <id|name|namespace|all|json|stdin...>')\n  .option('--watch', 'Stop watching folder for changes')\n  .description('stop a process')\n  .action(function(param) {\n    forEachLimit(param, 1, function(script, next) {\n      pm2.stop(script, next);\n    }, function(err) {\n      pm2.speedList(err ? 1 : 0);\n    });\n  });\n\n//\n// Stop All processes\n//\ncommander.command('restart <id|name|namespace|all|json|stdin...>')\n  .option('--watch', 'Toggle watching folder for changes')\n  .description('restart a process')\n  .action(function(param) {\n    // Commander.js patch\n    param = patchCommanderArg(param);\n    let acc = []\n    forEachLimit(param, 1, function(script, next) {\n      pm2.restart(script, commander, (err, apps) => {\n        acc = acc.concat(apps)\n        next(err)\n      });\n    }, function(err) {\n      pm2.speedList(err ? 1 : 0, acc);\n    });\n  });\n\n//\n// Scale up/down a process in cluster mode\n//\ncommander.command('scale <app_name> <number>')\n  .description('scale up/down a process in cluster mode depending on total_number param')\n  .action(function(app_name, number) {\n    pm2.scale(app_name, number);\n  });\n\n//\n// snapshot PM2\n//\ncommander.command('profile:mem [time]')\n  .description('Sample PM2 heap memory')\n  .action(function(time) {\n    pm2.profile('mem', time);\n  });\n\n//\n// snapshot PM2\n//\ncommander.command('profile:cpu [time]')\n  .description('Profile PM2 cpu')\n  .action(function(time) {\n    pm2.profile('cpu', time);\n  });\n\n//\n// Reload process(es)\n//\ncommander.command('reload <id|name|namespace|all>')\n  .description('reload processes (note that its for app using HTTP/HTTPS)')\n  .action(function(pm2_id) {\n    pm2.reload(pm2_id, commander);\n  });\n\ncommander.command('id <name>')\n  .description('get process id by name')\n  .action(function(name) {\n    pm2.getProcessIdByName(name);\n  });\n\n// Inspect a process\ncommander.command('inspect <name>')\n  .description('inspect a process')\n  .action(function(cmd) {\n    pm2.inspect(cmd, commander);\n  });\n\n//\n// Stop and delete a process by name from database\n//\ncommander.command('delete <name|id|namespace|script|all|json|stdin...>')\n  .alias('del')\n  .description('stop and delete a process from pm2 process list')\n  .action(function(name) {\n    if (name == \"-\") {\n      process.stdin.resume();\n      process.stdin.setEncoding('utf8');\n      process.stdin.on('data', function (param) {\n        process.stdin.pause();\n        pm2.delete(param, 'pipe');\n      });\n    } else\n      forEachLimit(name, 1, function(script, next) {\n        pm2.delete(script,'', next);\n      }, function(err) {\n        pm2.speedList(err ? 1 : 0);\n      });\n  });\n\n//\n// Send system signal to process\n//\ncommander.command('sendSignal <signal> <pm2_id|name>')\n  .description('send a system signal to the target process')\n  .action(function(signal, pm2_id) {\n    if (isNaN(parseInt(pm2_id))) {\n      console.log(cst.PREFIX_MSG + 'Sending signal to process name ' + pm2_id);\n      pm2.sendSignalToProcessName(signal, pm2_id);\n    } else {\n      console.log(cst.PREFIX_MSG + 'Sending signal to process id ' + pm2_id);\n      pm2.sendSignalToProcessId(signal, pm2_id);\n    }\n  });\n\n//\n// Stop and delete a process by name from database\n//\ncommander.command('ping')\n  .description('ping pm2 daemon - if not up it will launch it')\n  .action(function() {\n    pm2.ping();\n  });\n\ncommander.command('updatePM2')\n  .description('update in-memory PM2 with local PM2')\n  .action(function() {\n    pm2.update();\n  });\ncommander.command('update')\n  .description('(alias) update in-memory PM2 with local PM2')\n  .action(function() {\n    pm2.update();\n  });\n\n/**\n * Module specifics\n */\ncommander.command('install <module|git:// url>')\n  .alias('module:install')\n  .option('--tarball', 'is local tarball')\n  .option('--install', 'run yarn install before starting module')\n  .option('--docker', 'is docker container')\n  .option('--v1', 'install module in v1 manner (do not use it)')\n  .option('--safe [time]', 'keep module backup, if new module fail = restore with previous')\n  .description('install or update a module and run it forever')\n  .action(function(plugin_name, opts) {\n    require('util')._extend(commander, opts);\n    pm2.install(plugin_name, commander);\n  });\n\ncommander.command('module:update <module|git:// url>')\n  .option('--tarball', 'is local tarball')\n  .description('update a module and run it forever')\n  .action(function(plugin_name, opts) {\n    require('util')._extend(commander, opts);\n    pm2.install(plugin_name, commander);\n  });\n\n\ncommander.command('module:generate [app_name]')\n  .description('Generate a sample module in current folder')\n  .action(function(app_name) {\n    pm2.generateModuleSample(app_name);\n  });\n\ncommander.command('uninstall <module>')\n  .alias('module:uninstall')\n  .description('stop and uninstall a module')\n  .action(function(plugin_name) {\n    pm2.uninstall(plugin_name);\n  });\n\ncommander.command('package [target]')\n  .description('Check & Package TAR type module')\n  .action(function(target) {\n    pm2.package(target);\n  });\n\ncommander.command('publish [folder]')\n  .option('--npm', 'publish on npm')\n  .alias('module:publish')\n  .description('Publish the module you are currently on')\n  .action(function(folder, opts) {\n    pm2.publish(folder, opts);\n  });\n\ncommander.command('set [key] [value]')\n  .description('sets the specified config <key> <value>')\n  .action(function(key, value) {\n    pm2.set(key, value);\n  });\n\ncommander.command('multiset <value>')\n  .description('multiset eg \"key1 val1 key2 val2')\n  .action(function(str) {\n    pm2.multiset(str);\n  });\n\ncommander.command('get [key]')\n  .description('get value for <key>')\n  .action(function(key) {\n    pm2.get(key);\n  });\n\ncommander.command('conf [key] [value]')\n  .description('get / set module config values')\n  .action(function(key, value) {\n    pm2.get()\n  });\n\ncommander.command('config <key> [value]')\n  .description('get / set module config values')\n  .action(function(key, value) {\n    pm2.conf(key, value);\n  });\n\ncommander.command('unset <key>')\n  .description('clears the specified config <key>')\n  .action(function(key) {\n    pm2.unset(key);\n  });\n\ncommander.command('report')\n  .description('give a full pm2 report for https://github.com/Unitech/pm2/issues')\n  .action(function(key) {\n    pm2.report();\n  });\n\n//\n// PM2 I/O\n//\ncommander.command('link [secret] [public] [name]')\n  .option('--info-node [url]', 'set url info node')\n  .description('link with the pm2 monitoring dashboard')\n  .action(pm2.linkManagement.bind(pm2));\n\ncommander.command('unlink')\n  .description('unlink with the pm2 monitoring dashboard')\n  .action(function() {\n    pm2.unlink();\n  });\n\ncommander.command('monitor [name]')\n  .description('monitor target process')\n  .action(function(name) {\n    if (name === undefined) {\n      return plusHandler()\n    }\n    pm2.monitorState('monitor', name);\n  });\n\ncommander.command('unmonitor [name]')\n  .description('unmonitor target process')\n  .action(function(name) {\n    pm2.monitorState('unmonitor', name);\n  });\n\ncommander.command('open')\n  .description('open the pm2 monitoring dashboard')\n  .action(function(name) {\n    pm2.openDashboard();\n  });\n\nfunction plusHandler (command, opts) {\n  if (opts && opts.infoNode) {\n    process.env.KEYMETRICS_NODE = opts.infoNode\n  }\n\n  return PM2ioHandler.launch(command, opts)\n}\n\ncommander.command('plus [command] [option]')\n  .alias('register')\n  .option('--info-node [url]', 'set url info node for on-premise pm2 plus')\n  .option('-d --discrete', 'silent mode')\n  .option('-a --install-all', 'install all modules (force yes)')\n  .description('enable pm2 plus')\n  .action(plusHandler);\n\ncommander.command('login')\n  .description('Login to pm2 plus')\n  .action(function() {\n    return plusHandler('login')\n  });\n\ncommander.command('logout')\n  .description('Logout from pm2 plus')\n  .action(function() {\n    return plusHandler('logout')\n  });\n\n//\n// Save processes to file\n//\ncommander.command('dump')\n  .alias('save')\n  .option('--force', 'force deletion of dump file, even if empty')\n  .description('dump all processes for resurrecting them later')\n  .action(failOnUnknown(function(opts) {\n    pm2.dump(commander.force)\n  }));\n\n//\n// Delete dump file\n//\ncommander.command('cleardump')\n  .description('Create empty dump file')\n  .action(failOnUnknown(function() {\n    pm2.clearDump();\n  }));\n\n//\n// Save processes to file\n//\ncommander.command('send <pm_id> <line>')\n  .description('send stdin to <pm_id>')\n  .action(function(pm_id, line) {\n    pm2.sendLineToStdin(pm_id, line);\n  });\n\n//\n// Attach to stdin/stdout\n// Not TTY ready\n//\ncommander.command('attach <pm_id> [command separator]')\n  .description('attach stdin/stdout to application identified by <pm_id>')\n  .action(function(pm_id, separator) {\n    pm2.attach(pm_id, separator);\n  });\n\n//\n// Resurrect\n//\ncommander.command('resurrect')\n  .description('resurrect previously dumped processes')\n  .action(failOnUnknown(function() {\n    console.log(cst.PREFIX_MSG + 'Resurrecting');\n    pm2.resurrect();\n  }));\n\n//\n// Set pm2 to startup\n//\ncommander.command('unstartup [platform]')\n  .description('disable the pm2 startup hook')\n  .action(function(platform) {\n    pm2.uninstallStartup(platform, commander);\n  });\n\n//\n// Set pm2 to startup\n//\ncommander.command('startup [platform]')\n  .description('enable the pm2 startup hook')\n  .action(function(platform) {\n    pm2.startup(platform, commander);\n  });\n\n//\n// Logrotate\n//\ncommander.command('logrotate')\n  .description('copy default logrotate configuration')\n  .action(function(cmd) {\n    pm2.logrotate(commander);\n  });\n\n//\n// Sample generate\n//\n\ncommander.command('ecosystem [mode]')\n  .alias('init')\n  .description('generate a process conf file. (mode = null or simple)')\n  .action(function(mode) {\n    pm2.generateSample(mode);\n  });\n\ncommander.command('reset <name|id|all>')\n  .description('reset counters for process')\n  .action(function(proc_id) {\n    pm2.reset(proc_id);\n  });\n\ncommander.command('describe <name|id>')\n  .description('describe all parameters of a process')\n  .action(function(proc_id) {\n    pm2.describe(proc_id);\n  });\n\ncommander.command('desc <name|id>')\n  .description('(alias) describe all parameters of a process')\n  .action(function(proc_id) {\n    pm2.describe(proc_id);\n  });\n\ncommander.command('info <name|id>')\n  .description('(alias) describe all parameters of a process')\n  .action(function(proc_id) {\n    pm2.describe(proc_id);\n  });\n\ncommander.command('show <name|id>')\n  .description('(alias) describe all parameters of a process')\n  .action(function(proc_id) {\n    pm2.describe(proc_id);\n  });\n\ncommander.command('env <id>')\n  .description('list all environment variables of a process id')\n  .action(function(proc_id) {\n    pm2.env(proc_id);\n  });\n\n//\n// List command\n//\ncommander\n  .command('list')\n  .alias('ls')\n  .description('list all processes')\n  .action(function() {\n    pm2.list(commander)\n  });\n\ncommander.command('l')\n  .description('(alias) list all processes')\n  .action(function() {\n    pm2.list()\n  });\n\ncommander.command('ps')\n  .description('(alias) list all processes')\n  .action(function() {\n    pm2.list()\n  });\n\ncommander.command('status')\n  .description('(alias) list all processes')\n  .action(function() {\n    pm2.list()\n  });\n\n\n// List in raw json\ncommander.command('jlist')\n  .description('list all processes in JSON format')\n  .action(function() {\n    pm2.jlist()\n  });\n\ncommander.command('sysmonit')\n  .description('start system monitoring daemon')\n  .action(function() {\n    pm2.launchSysMonitoring()\n  })\n\ncommander.command('slist')\n  .alias('sysinfos')\n  .option('-t --tree', 'show as tree')\n  .description('list system infos in JSON')\n  .action(function(opts) {\n    pm2.slist(opts.tree)\n  })\n\n// List in prettified Json\ncommander.command('prettylist')\n  .description('print json in a prettified JSON')\n  .action(failOnUnknown(function() {\n    pm2.jlist(true);\n  }));\n\n//\n// Dashboard command\n//\ncommander.command('monit')\n  .description('launch termcaps monitoring')\n  .action(function() {\n    pm2.dashboard();\n  });\n\ncommander.command('imonit')\n  .description('launch legacy termcaps monitoring')\n  .action(function() {\n    pm2.monit();\n  });\n\ncommander.command('dashboard')\n  .alias('dash')\n  .description('launch dashboard with monitoring and logs')\n  .action(function() {\n    pm2.dashboard();\n  });\n\n\n//\n// Flushing command\n//\n\ncommander.command('flush [api]')\n.description('flush logs')\n.action(function(api) {\n  pm2.flush(api);\n});\n\n/* old version\ncommander.command('flush')\n  .description('flush logs')\n  .action(failOnUnknown(function() {\n    pm2.flush();\n  }));\n*/\n//\n// Reload all logs\n//\ncommander.command('reloadLogs')\n  .description('reload all logs')\n  .action(function() {\n    pm2.reloadLogs();\n  });\n\n//\n// Log streaming\n//\ncommander.command('logs [id|name|namespace]')\n  .option('--json', 'json log output')\n  .option('--format', 'formated log output')\n  .option('--raw', 'raw output')\n  .option('--err', 'only shows error output')\n  .option('--out', 'only shows standard output')\n  .option('--lines <n>', 'output the last N lines, instead of the last 15 by default')\n  .option('--timestamp [format]', 'add timestamps (default format YYYY-MM-DD-HH:mm:ss)')\n  .option('--nostream', 'print logs without launching the log stream')\n  .option('--highlight [value]', 'highlights the given value')\n  .description('stream logs file. Default stream all logs')\n  .action(function(id, cmd) {\n    var Logs = require('../API/Log.js');\n\n    if (!id) id = 'all';\n\n    var line = 15;\n    var raw  = false;\n    var exclusive = false;\n    var timestamp = false;\n    var highlight = false;\n\n    if(!isNaN(parseInt(cmd.lines))) {\n      line = parseInt(cmd.lines);\n    }\n\n    if (cmd.parent.rawArgs.indexOf('--raw') !== -1)\n      raw = true;\n\n    if (cmd.timestamp)\n      timestamp = typeof cmd.timestamp === 'string' ? cmd.timestamp : 'YYYY-MM-DD-HH:mm:ss';\n\n    if (cmd.highlight)\n      highlight = typeof cmd.highlight === 'string' ? cmd.highlight : false;\n\n    if (cmd.out === true)\n      exclusive = 'out';\n\n    if (cmd.err === true)\n      exclusive = 'err';\n\n    if (cmd.nostream === true)\n      pm2.printLogs(id, line, raw, timestamp, exclusive);\n    else if (cmd.json === true)\n      Logs.jsonStream(pm2.Client, id);\n    else if (cmd.format === true)\n      Logs.formatStream(pm2.Client, id, false, 'YYYY-MM-DD-HH:mm:ssZZ', exclusive, highlight);\n    else\n      pm2.streamLogs(id, line, raw, timestamp, exclusive, highlight);\n  });\n\n\n//\n// Kill\n//\ncommander.command('kill')\n  .description('kill daemon')\n  .action(failOnUnknown(function(arg) {\n    pm2.killDaemon(function() {\n      process.exit(cst.SUCCESS_EXIT);\n    });\n  }));\n\n//\n// Update repository for a given app\n//\n\ncommander.command('pull <name> [commit_id]')\n  .description('updates repository for a given app')\n  .action(function(pm2_name, commit_id) {\n\n    if (commit_id !== undefined) {\n      pm2._pullCommitId({\n        pm2_name: pm2_name,\n        commit_id: commit_id\n      });\n    }\n    else\n      pm2.pullAndRestart(pm2_name);\n  });\n\n//\n// Update repository to the next commit for a given app\n//\ncommander.command('forward <name>')\n  .description('updates repository to the next commit for a given app')\n  .action(function(pm2_name) {\n    pm2.forward(pm2_name);\n  });\n\n//\n// Downgrade repository to the previous commit for a given app\n//\ncommander.command('backward <name>')\n  .description('downgrades repository to the previous commit for a given app')\n  .action(function(pm2_name) {\n    pm2.backward(pm2_name);\n  });\n\n//\n// Perform a deep update of PM2\n//\ncommander.command('deepUpdate')\n  .description('performs a deep update of PM2')\n  .action(function() {\n    pm2.deepUpdate();\n  });\n\n//\n// Launch a http server that expose a given path on given port\n//\ncommander.command('serve [path] [port]')\n  .alias('expose')\n  .option('--port [port]', 'specify port to listen to')\n  .option('--spa', 'always serving index.html on inexistant sub path')\n  .option('--basic-auth-username [username]', 'set basic auth username')\n  .option('--basic-auth-password [password]', 'set basic auth password')\n  .option('--monitor [frontend-app]', 'frontend app monitoring (auto integrate snippet on html files)')\n  .description('serve a directory over http via port')\n  .action(function (path, port, cmd) {\n    pm2.serve(path, port || cmd.port, cmd, commander);\n  });\n\ncommander.command('autoinstall')\n  .action(function() {\n    pm2.autoinstall()\n  })\n\ncommander.command('examples')\n  .description('display pm2 usage examples')\n  .action(() => {\n    console.log(cst.PREFIX_MSG + chalk.gray('pm2 usage examples:\\n'));\n    displayExamples();\n    process.exit(cst.SUCCESS_EXIT);\n  })\n\n//\n// Catch all\n//\ncommander.command('*')\n  .action(function() {\n    console.log(cst.PREFIX_MSG_ERR + chalk.bold('Command not found\\n'));\n    displayUsage();\n    // Check if it does not forget to close fds from RPC\n    process.exit(cst.ERROR_EXIT);\n  });\n\n//\n// Display help if 0 arguments passed to pm2\n//\nif (process.argv.length == 2) {\n  commander.parse(process.argv);\n  displayUsage();\n  // Check if it does not forget to close fds from RPC\n  process.exit(cst.ERROR_EXIT);\n}\n"
  },
  {
    "path": "lib/binaries/DevCLI.js",
    "content": "\n'use strict';\n\nprocess.env.PM2_NO_INTERACTION = 'true';\n// Do not print banner\nprocess.env.PM2_DISCRETE_MODE = true;\n\nvar commander = require('commander');\n\nvar PM2       = require('../..');\nvar Log       = require('../API/Log');\nvar cst       = require('../../constants.js');\nvar pkg       = require('../../package.json');\nvar chalk     = require('ansis');\nvar path      = require('path');\nvar fmt       = require('../tools/fmt.js');\nvar exec      = require('child_process').exec;\nvar os        = require('os');\n\ncommander.version(pkg.version)\n  .description('pm2-dev monitor for any file changes and automatically restart it')\n  .option('--raw', 'raw log output')\n  .option('--timestamp', 'print timestamp')\n  .option('--node-args <node_args>', 'space delimited arguments to pass to node in cluster mode - e.g. --node-args=\"--debug=7001 --trace-deprecation\"')\n  .option('--ignore [files]', 'files to ignore while watching')\n  .option('--post-exec [cmd]', 'execute extra command after change detected')\n  .option('--silent-exec', 'do not output result of post command', false)\n  .option('--test-mode', 'debug mode for test suit')\n  .option('--interpreter <interpreter>', 'the interpreter pm2 should use for executing app (bash, python...)')\n  .option('--env [name]', 'select env_[name] env variables in process config file')\n  .option('--auto-exit', 'exit if all processes are errored/stopped or 0 apps launched')\n  .usage('pm2-dev app.js');\n\nvar pm2 = new PM2.custom({\n  pm2_home : path.join(os.homedir ? os.homedir() : (process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE), '.pm2-dev')\n});\n\npm2.connect(function() {\n  commander.parse(process.argv);\n});\n\nfunction postExecCmd(command, cb) {\n  var exec_cmd = exec(command);\n\n  if (commander.silentExec !== true) {\n    exec_cmd.stdout.on('data', function(data) {\n      process.stdout.write(data);\n    });\n\n    exec_cmd.stderr.on('data', function(data) {\n      process.stderr.write(data);\n    });\n  }\n\n  exec_cmd.on('close', function done() {\n    if (cb) cb(null);\n  });\n\n  exec_cmd.on('error', function (err) {\n    console.error(err.stack || err);\n  });\n};\n\nfunction run(cmd, opts) {\n  var timestamp = opts.timestamp;\n\n  opts.watch = true;\n  opts.autostart = true;\n  opts.autorestart = true;\n  opts.restart_delay = 1000\n  if (opts.autoExit)\n    autoExit();\n\n  if (opts.ignore) {\n    opts.ignore_watch = opts.ignore.split(',')\n    opts.ignore_watch.push('node_modules');\n  }\n\n  if (timestamp === true)\n    timestamp = 'YYYY-MM-DD-HH:mm:ss';\n\n  pm2.start(cmd, opts, function(err, procs) {\n\n    if (err) {\n      console.error(err);\n      pm2.destroy(function() {\n        process.exit(0);\n      });\n      return false;\n    }\n\n    if (opts.testMode) {\n      return pm2.disconnect(function() {\n        console.log('disconnected succesfully from pm2-dev')\n      });\n    }\n\n    fmt.title('PM2 development mode');\n    fmt.field('Apps started', procs.map(function(p) { return p.pm2_env.name } ));\n    fmt.field('Processes started', chalk.bold(procs.length));\n    fmt.field('Watch and Restart', chalk.green('Enabled'));\n    fmt.field('Ignored folder', opts.ignore_watch || 'node_modules');\n    if (opts.postExec)\n      fmt.field('Post restart cmd', opts.postExec);\n    fmt.sep();\n\n    setTimeout(function() {\n      pm2.Client.launchBus(function(err, bus) {\n        bus.on('process:event', function(packet) {\n          if (packet.event == 'online') {\n            if (opts.postExec)\n              postExecCmd(opts.postExec);\n          }\n        });\n      });\n    }, 1000);\n\n    Log.devStream(pm2.Client, 'all', opts.raw, timestamp, false);\n\n    process.on('SIGINT', function() {\n      console.log('>>>>> [PM2 DEV] Stopping current development session');\n      pm2.delete('all', function() {\n        pm2.destroy(function() {\n          process.exit(0);\n        });\n      });\n    });\n\n  });\n}\n\ncommander.command('*')\n  .action(function(cmd, opts){\n    run(cmd, commander);\n  });\n\ncommander.command('start <file|json_file>')\n  .description('start target config file/script in development mode')\n  .action(function(cmd, opts) {\n    run(cmd, commander);\n  });\n\nfunction exitPM2() {\n  if (pm2 && pm2.connected == true) {\n    console.log(chalk.green.bold('>>> Exiting PM2'));\n    pm2.kill(function() {\n      process.exit(0);\n    });\n  }\n  else\n    process.exit(0);\n}\n\nfunction autoExit(final) {\n  setTimeout(function() {\n    pm2.list(function(err, apps) {\n      if (err) console.error(err.stack || err);\n\n      var online_count = 0;\n\n      apps.forEach(function(app) {\n        if (app.pm2_env.status == cst.ONLINE_STATUS ||\n            app.pm2_env.status == cst.LAUNCHING_STATUS)\n          online_count++;\n      });\n\n      if (online_count == 0) {\n        console.log('0 application online, exiting');\n        if (final == true)\n          process.exit(1);\n        else\n          autoExit(true);\n        return false;\n      }\n      autoExit(false);\n    });\n  }, 3000);\n}\n\nif (process.argv.length == 2) {\n  commander.outputHelp();\n  exitPM2();\n}\n"
  },
  {
    "path": "lib/binaries/Runtime.js",
    "content": "\n'use strict';\n\nvar commander = require('commander');\n\nvar PM2       = require('../..');\nvar Log       = require('../../lib/API/Log');\nvar cst       = require('../../constants.js');\nvar pkg       = require('../../package.json');\nvar path      = require('path');\n\nvar pm2;\n\n// Do not print banner\nprocess.env.PM2_DISCRETE_MODE = true;\n\ncommander.version(pkg.version)\n  .description('pm2-runtime is an automatic pmx injection that runs in simulated no-daemon environment')\n  .option('--auto-manage', 'keep application online after command exit')\n  .option('--fast-boot', 'boot app faster by keeping pm2 runtime online in background (effective at second exit/start)')\n  .option('--web [port]', 'launch process web api on [port] default to 9615')\n  .option('--secret [key]', 'PM2 plus secret key')\n  .option('--public [key]', 'PM2 plus public key')\n  .option('--machine-name [name]', 'PM2 plus machine name')\n  .option('--env [name]', 'select env_[name] env variables in process config file')\n  .option('--watch', 'Watch and Restart')\n  .option('-i --instances <number>', 'launch [number] instances with load-balancer')\n  .usage('pm2-runtime app.js');\n\ncommander.command('*')\n  .action(function(cmd){\n    pm2 = new PM2.custom({\n      pm2_home : path.join(process.env.HOME, '.pm3'),\n      secret_key : cst.SECRET_KEY || commander.secret,\n      public_key : cst.PUBLIC_KEY || commander.public,\n      machine_name : cst.MACHINE_NAME || commander.machineName\n    });\n\n    pm2.connect(function() {\n      if (commander.web) {\n        var port = commander.web === true ? cst.WEB_PORT : commander.web;\n        pm2.web(port);\n      }\n\n      pm2.start(cmd, commander, function(err, obj) {\n        if (process.env.PM2_RUNTIME_DEBUG) {\n          return pm2.disconnect(function() {});\n        }\n\n        if (err) {\n          console.error(err);\n          return process.exit(1);\n        }\n\n        var pm_id = obj[0].pm2_env.pm_id;\n\n        if (commander.instances == undefined) {\n          return pm2.attach(pm_id, function() {\n            exitPM2();\n          });\n        }\n\n        if (commander.json === true)\n          Log.jsonStream(pm2.Client, pm_id);\n        else if (commander.format === true)\n          Log.formatStream(pm2.Client, pm_id, false, 'YYYY-MM-DD-HH:mm:ssZZ');\n        else\n          Log.stream(pm2.Client, 'all', true);\n      });\n    });\n  });\n\nif (process.argv.length == 2) {\n  commander.outputHelp();\n  process.exit(1);\n}\n\nprocess.on('SIGINT', function() {\n  exitPM2();\n});\n\nprocess.on('SIGTERM', function() {\n  exitPM2();\n});\n\ncommander.parse(process.argv);\n\nfunction exitPM2() {\n  console.log('Exited at %s', new Date());\n  if (commander.autoManage)\n    return process.exit(0);\n\n  if (commander.fastBoot) {\n    return pm2.delete('all', function() {\n      process.exit(0);\n    });\n  }\n  pm2.kill(function() {\n    process.exit(0);\n  });\n}\n"
  },
  {
    "path": "lib/binaries/Runtime4Docker.js",
    "content": "'use strict';\n\n/**\n * Specialized PM2 CLI for Containers\n */\nvar commander = require('commander');\nvar PM2       = require('../..');\nvar Log       = require('../../lib/API/Log');\nvar cst       = require('../../constants.js');\nvar pkg       = require('../../package.json');\nvar path      = require('path');\nvar DEFAULT_FAIL_COUNT = 3;\n\nprocess.env.PM2_DISCRETE_MODE = true;\n\ncommander.version(pkg.version)\n  .description('pm2-runtime is a drop-in replacement Node.js binary for containers')\n  .option('-i --instances <number>', 'launch [number] of processes automatically load-balanced. Increase overall performances and performance stability.')\n  .option('--secret [key]', '[MONITORING] PM2 plus secret key')\n  .option('--no-autostart', 'add an app without automatic start')\n  .option('--no-autorestart', 'start an app without automatic restart')\n  .option('--stop-exit-codes <exit_codes...>', 'specify a list of exit codes that should skip automatic restart')\n  .option('--node-args <node_args>', 'space delimited arguments to pass to node in cluster mode - e.g. --node-args=\"--debug=7001 --trace-deprecation\"')\n  .option('-n --name <name>', 'set a <name> for script')\n  .option('--max-memory-restart <memory>', 'specify max memory amount used to autorestart (in octet or use syntax like 100M)')\n  .option('-c --cron <cron_pattern>', 'restart a running process based on a cron pattern')\n  .option('--interpreter <interpreter>', 'the interpreter pm2 should use for executing app (bash, python...)')\n  .option('--public [key]', '[MONITORING] PM2 plus public key')\n  .option('--machine-name [name]', '[MONITORING] PM2 plus machine name')\n  .option('--trace', 'enable transaction tracing with km')\n  .option('--v8', 'enable v8 data collecting')\n  .option('--format', 'output logs formated like key=val')\n  .option('--raw', 'raw output (default mode)')\n  .option('--formatted', 'formatted log output |id|app|log')\n  .option('--json', 'output logs in json format')\n  .option('--delay <seconds>', 'delay start of configuration file by <seconds>', 0)\n  .option('--web [port]', 'launch process web api on [port] (default to 9615)')\n  .option('--only <application-name>', 'only act on one application of configuration')\n  .option('--no-auto-exit', 'do not exit if all processes are errored/stopped or 0 apps launched')\n  .option('--env [name]', 'inject env_[name] env variables in process config file')\n  .option('--watch', 'watch and restart application on file change')\n  .option('--error <path>', 'error log file destination (default disabled)', '/dev/null')\n  .option('--output <path>', 'output log file destination (default disabled)', '/dev/null')\n  .option('--deep-monitoring', 'enable all monitoring tools (equivalent to --v8 --event-loop-inspector --trace)')\n  .allowUnknownOption()\n  .usage('app.js');\n\ncommander.command('*')\n  .action(function(cmd){\n    Runtime.instanciate(cmd);\n  });\n\ncommander.command('start <app.js|json_file>')\n  .description('start an application or json ecosystem file')\n  .action(function(cmd) {\n    Runtime.instanciate(cmd);\n  });\n\nif (process.argv.length == 2) {\n  commander.outputHelp();\n  process.exit(1);\n}\n\nvar Runtime = {\n  pm2 : null,\n  instanciate : function(cmd) {\n    this.pm2 = new PM2.custom({\n      pm2_home : process.env.PM2_HOME || path.join(process.env.HOME, '.pm2'),\n      secret_key : cst.SECRET_KEY || commander.secret,\n      public_key : cst.PUBLIC_KEY || commander.public,\n      machine_name : cst.MACHINE_NAME || commander.machineName,\n      daemon_mode : process.env.PM2_RUNTIME_DEBUG || false\n    });\n\n    this.pm2.connect(function(err, pm2_meta) {\n      process.on('SIGINT', function() {\n        Runtime.exit();\n      });\n\n      process.on('SIGTERM', function() {\n        Runtime.exit();\n      });\n\n      Runtime.startLogStreaming();\n      Runtime.startApp(cmd, function(err) {\n        if (err) {\n          console.error(err.message || err);\n          return Runtime.exit();\n        }\n      });\n    });\n  },\n\n  /**\n   * Log Streaming Management\n   */\n  startLogStreaming : function() {\n    if (commander.json === true)\n      Log.jsonStream(this.pm2.Client, 'all');\n    else if (commander.format === true)\n      Log.formatStream(this.pm2.Client, 'all', false, 'YYYY-MM-DD-HH:mm:ssZZ');\n    else\n      Log.stream(this.pm2.Client, 'all', !commander.formatted, commander.timestamp, true);\n  },\n\n  /**\n   * Application Startup\n   */\n  startApp : function(cmd, cb) {\n    function exec() {\n      this.pm2.start(cmd, commander, function(err, obj) {\n        if (err)\n          return cb(err);\n        if (obj && obj.length == 0)\n          return cb(new Error(`0 application started (no apps to run on ${cmd})`))\n\n        if (commander.web) {\n          var port = commander.web === true ? cst.WEB_PORT : commander.web;\n          Runtime.pm2.web(port);\n        }\n\n        if (commander.autoExit) {\n          setTimeout(function() {\n            Runtime.autoExitWorker();\n          }, 4000);\n        }\n\n        // For Testing purpose (allow to auto exit CLI)\n        if (process.env.PM2_RUNTIME_DEBUG)\n          Runtime.pm2.disconnect(function() {});\n\n        return cb(null, obj);\n      });\n    }\n    // via --delay <seconds> option\n    setTimeout(exec.bind(this), commander.delay * 1000);\n  },\n\n  /**\n   * Exit runtime mgmt\n   */\n  exit : function(code) {\n    if (!this.pm2) return process.exit(1);\n\n    this.pm2.kill(function() {\n      process.exit(code || 0);\n    });\n  },\n\n  /**\n   * Exit current PM2 instance if 0 app is online\n   * function activated via --auto-exit\n   */\n  autoExitWorker : function(fail_count) {\n    var interval = 2000;\n\n    if (typeof(fail_count) =='undefined')\n      fail_count = DEFAULT_FAIL_COUNT;\n\n    var timer = setTimeout(function () {\n      Runtime.pm2.list(function (err, apps) {\n        if (err) {\n          console.error('Could not run pm2 list');\n          return Runtime.autoExitWorker();\n        }\n\n        var appOnline = 0;\n\n        apps.forEach(function (app) {\n          if (!app.pm2_env.pmx_module &&\n            (app.pm2_env.status === cst.ONLINE_STATUS ||\n              app.pm2_env.status === cst.LAUNCHING_STATUS)) {\n            appOnline++;\n          }\n        });\n\n        if (appOnline === 0) {\n          console.log('0 application online, retry =', fail_count);\n          if (fail_count <= 0)\n            return Runtime.exit(2);\n          return Runtime.autoExitWorker(--fail_count);\n        }\n\n        Runtime.autoExitWorker();\n      });\n    }, interval);\n\n    timer.unref();\n  }\n}\n\ncommander.parse(process.argv);\n"
  },
  {
    "path": "lib/completion.js",
    "content": "/**\n * Copyright 2013-2022 the PM2 project authors. All rights reserved.\n * Use of this source code is governed by a license that\n * can be found in the LICENSE file.\n */\nvar fs = require('fs'),\n  pth = require('path');\n\n//  hacked from node-tabtab 0.0.4 https://github.com/mklabs/node-tabtab.git\n//  Itself based on npm completion by @isaac\n\nexports.complete = function complete(name, completer, cb) {\n\n  // cb not there, assume callback is completer and\n  // the completer is the executable itself\n  if(!cb) {\n    cb = completer;\n    completer = name;\n  }\n\n  var env = parseEnv();\n\n  // if not a complete command, return here.\n  if(!env.complete) return cb();\n\n  // if install cmd, add complete script to either ~/.bashrc or ~/.zshrc\n  if(env.install) return install(name, completer, function(err, state) {\n    console.log(state || err.message);\n    if(err) return cb(err);\n    cb(null, null, state);\n  });\n\n  // if install cmd, add complete script to either ~/.bashrc or ~/.zshrc\n  if(env.uninstall) return uninstall(name, completer, function(err, state) {\n    console.log(state || err.message);\n    if(err) return cb(err);\n    cb(null, null, state);\n  });\n\n  // if the COMP_* are not in the env, then dump the install script.\n  if(!env.words || !env.point || !env.line) return script(name, completer, function(err, content) {\n    if(err) return cb(err);\n    process.stdout.write(content, function (n) { cb(null, null, content); });\n    process.stdout.on(\"error\", function (er) {\n      // Darwin is a real dick sometimes.\n      //\n      // This is necessary because the \"source\" or \".\" program in\n      // bash on OS X closes its file argument before reading\n      // from it, meaning that you get exactly 1 write, which will\n      // work most of the time, and will always raise an EPIPE.\n      //\n      // Really, one should not be tossing away EPIPE errors, or any\n      // errors, so casually.  But, without this, `. <(npm completion)`\n      // can never ever work on OS X.\n      //      -- isaacs\n      // https://github.com/isaacs/npm/blob/master/lib/completion.js#L162\n      if (er.errno === \"EPIPE\") er = null\n      cb(er, null, content);\n    });\n    cb(null, null, content);\n  });\n\n  var partial = env.line.substr(0, env.point),\n  last = env.line.split(' ').slice(-1).join(''),\n  lastPartial = partial.split(' ').slice(-1).join(''),\n  prev = env.line.split(' ').slice(0, -1).slice(-1)[0];\n\n  cb(null, {\n    line: env.line,\n    words: env.words,\n    point: env.point,\n    partial: partial,\n    last: last,\n    prev: prev,\n    lastPartial: lastPartial\n  });\n};\n\n// simple helper function to know if the script is run\n// in the context of a completion command. Also mapping the\n// special `<pkgname> completion` cmd.\nexports.isComplete = function isComplete() {\n  var env = parseEnv();\n  return env.complete || (env.words && env.point && env.line);\n};\n\nexports.parseOut = function parseOut(str) {\n  var shorts = str.match(/\\s-\\w+/g);\n  var longs = str.match(/\\s--\\w+/g);\n\n  return {\n    shorts: shorts.map(trim).map(cleanPrefix),\n    longs: longs.map(trim).map(cleanPrefix)\n  };\n};\n\n// specific to cake case\nexports.parseTasks = function(str, prefix, reg) {\n  var tasks = str.match(reg || new RegExp('^' + prefix + '\\\\s[^#]+', 'gm')) || [];\n  return tasks.map(trim).map(function(s) {\n    return s.replace(prefix + ' ', '');\n  });\n};\n\nexports.log = function log(arr, o, prefix) {\n  prefix = prefix || '';\n  arr = Array.isArray(arr) ? arr : [arr];\n  arr.filter(abbrev(o)).forEach(function(v) {\n    console.log(prefix + v);\n  });\n}\n\nfunction trim (s) {\n  return s.trim();\n}\n\nfunction cleanPrefix(s) {\n  return s.replace(/-/g, '');\n}\n\nfunction abbrev(o) { return function(it) {\n  return new RegExp('^' + o.last.replace(/^--?/g, '')).test(it);\n}}\n\n// output the completion.sh script to the console for install instructions.\n// This is actually a 'template' where the package name is used to setup\n// the completion on the right command, and properly name the bash/zsh functions.\nfunction script(name, completer, cb) {\n  var p = pth.join(__dirname, 'completion.sh');\n\n  fs.readFile(p, 'utf8', function (er, d) {\n    if (er) return cb(er);\n    cb(null, d);\n  });\n}\n\nfunction install(name, completer, cb) {\n  var markerIn = '###-begin-' + name + '-completion-###',\n    markerOut = '###-end-' + name + '-completion-###';\n\n  var rc, scriptOutput;\n\n  readRc(completer, function(err, file) {\n    if(err) return cb(err);\n\n    var part = file.split(markerIn)[1];\n    if(part) {\n      return cb(null, ' ✗ ' + completer + ' tab-completion has been already installed. Do nothing.');\n    }\n\n    rc = file;\n    next();\n  });\n\n  script(name, completer, function(err, file) {\n    scriptOutput = file;\n    next();\n  });\n\n  function next() {\n    if(!rc || !scriptOutput) return;\n\n    writeRc(rc + scriptOutput, function(err) {\n      if(err) return cb(err);\n      return cb(null, ' ✓ ' + completer + ' tab-completion installed.');\n    });\n  }\n}\n\nfunction uninstall(name, completer, cb) {\n  var markerIn = '\\n\\n###-begin-' + name + '-completion-###',\n    markerOut = '###-end-' + name + '-completion-###\\n';\n\n  readRc(completer, function(err, file) {\n    if(err) return cb(err);\n\n    var part = file.split(markerIn)[1];\n    if(!part) {\n      return cb(null, ' ✗ ' + completer + ' tab-completion has been already uninstalled. Do nothing.');\n    }\n\n    part = markerIn + part.split(markerOut)[0] + markerOut;\n    writeRc(file.replace(part, ''), function(err) {\n      if(err) return cb(err);\n      return cb(null, ' ✓ ' + completer + ' tab-completion uninstalled.');\n    });\n  });\n}\n\nfunction readRc(completer, cb) {\n  var file = '.' + process.env.SHELL.match(/\\/bin\\/(\\w+)/)[1] + 'rc',\n  filepath = pth.join(process.env.HOME, file);\n  fs.lstat(filepath, function (err, stats) {\n    if(err) return cb(new Error(\"No \" + file + \" file. You'll have to run instead: \" + completer + \" completion >> ~/\" + file));\n    fs.readFile(filepath, 'utf8', cb);\n  });\n}\n\nfunction writeRc(content, cb) {\n  var file = '.' + process.env.SHELL.match(/\\/bin\\/(\\w+)/)[1] + 'rc',\n  filepath = pth.join(process.env.HOME, file);\n  fs.lstat(filepath, function (err, stats) {\n    if(err) return cb(new Error(\"No \" + file + \" file. You'll have to run instead: \" + completer + \" completion >> ~/\" + file));\n    fs.writeFile(filepath, content, cb);\n  });\n}\n\nfunction installed (marker, completer, cb) {\n  readRc(completer, function(err, file) {\n    if(err) return cb(err);\n    var installed = file.match(marker);\n    return cb(!!installed);\n  });\n}\n\nfunction parseEnv() {\n  var args = process.argv.slice(2),\n  complete = args[0] === 'completion';\n\n  return {\n    args: args,\n    complete: complete,\n    install: complete && args[1] === 'install',\n    uninstall: complete && args[1] === 'uninstall',\n    words: +process.env.COMP_CWORD,\n    point: +process.env.COMP_POINT,\n    line: process.env.COMP_LINE\n  }\n};\n"
  },
  {
    "path": "lib/completion.sh",
    "content": "###-begin-pm2-completion-###\n### credits to npm for the completion file model\n#\n# Installation: pm2 completion >> ~/.bashrc  (or ~/.zshrc)\n#\n\nCOMP_WORDBREAKS=${COMP_WORDBREAKS/=/}\nCOMP_WORDBREAKS=${COMP_WORDBREAKS/@/}\nexport COMP_WORDBREAKS\n\nif type complete &>/dev/null; then\n  _pm2_completion () {\n    local si=\"$IFS\"\n    IFS=$'\\n' COMPREPLY=($(COMP_CWORD=\"$COMP_CWORD\" \\\n                           COMP_LINE=\"$COMP_LINE\" \\\n                           COMP_POINT=\"$COMP_POINT\" \\\n                           pm2 completion -- \"${COMP_WORDS[@]}\" \\\n                           2>/dev/null)) || return $?\n    IFS=\"$si\"\n  }\n  complete -o default -F _pm2_completion pm2\nelif type compctl &>/dev/null; then\n  _pm2_completion () {\n    local cword line point words si\n    read -Ac words\n    read -cn cword\n    let cword-=1\n    read -l line\n    read -ln point\n    si=\"$IFS\"\n    IFS=$'\\n' reply=($(COMP_CWORD=\"$cword\" \\\n                       COMP_LINE=\"$line\" \\\n                       COMP_POINT=\"$point\" \\\n                       pm2 completion -- \"${words[@]}\" \\\n                       2>/dev/null)) || return $?\n    IFS=\"$si\"\n  }\n  compctl -K _pm2_completion + -f + pm2\nfi\n###-end-pm2-completion-###\n"
  },
  {
    "path": "lib/motd",
    "content": "\n                        -------------\n\n__/\\\\\\\\\\\\\\\\\\\\\\\\\\____/\\\\\\\\____________/\\\\\\\\____/\\\\\\\\\\\\\\\\\\_____\n _\\/\\\\\\/////////\\\\\\_\\/\\\\\\\\\\\\________/\\\\\\\\\\\\__/\\\\\\///////\\\\\\___\n  _\\/\\\\\\_______\\/\\\\\\_\\/\\\\\\//\\\\\\____/\\\\\\//\\\\\\_\\///______\\//\\\\\\__\n   _\\/\\\\\\\\\\\\\\\\\\\\\\\\\\/__\\/\\\\\\\\///\\\\\\/\\\\\\/_\\/\\\\\\___________/\\\\\\/___\n    _\\/\\\\\\/////////____\\/\\\\\\__\\///\\\\\\/___\\/\\\\\\________/\\\\\\//_____\n     _\\/\\\\\\_____________\\/\\\\\\____\\///_____\\/\\\\\\_____/\\\\\\//________\n      _\\/\\\\\\_____________\\/\\\\\\_____________\\/\\\\\\___/\\\\\\/___________\n       _\\/\\\\\\_____________\\/\\\\\\_____________\\/\\\\\\__/\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\_\n        _\\///______________\\///______________\\///__\\///////////////__\n\n\n                          Runtime Edition\n\n        PM2 is a Production Process Manager for Node.js applications\n                     with a built-in Load Balancer.\n\n                Start and Daemonize any application:\n                $ pm2 start app.js\n\n                Load Balance 4 instances of api.js:\n                $ pm2 start api.js -i 4\n\n                Monitor in production:\n                $ pm2 monitor\n\n                Make pm2 auto-boot at server restart:\n                $ pm2 startup\n\n                To go further checkout:\n                http://pm2.io/\n\n\n                        -------------\n"
  },
  {
    "path": "lib/templates/Dockerfiles/Dockerfile-java.tpl",
    "content": "FROM anapsix/alpine-java:latest\n\nRUN apk update && apk add git && rm -rf /var/cache/apk/*\nRUN npm install pm2@next -g\nRUN mkdir -p /var/app\n\nWORKDIR /var/app\n"
  },
  {
    "path": "lib/templates/Dockerfiles/Dockerfile-nodejs.tpl",
    "content": "FROM keymetrics/pm2:latest\n\nRUN mkdir -p /var/app\n\nWORKDIR /var/app\n\nCOPY ./package.json /var/app\nRUN npm install\n"
  },
  {
    "path": "lib/templates/Dockerfiles/Dockerfile-ruby.tpl",
    "content": "FROM anapsix/alpine-ruby:latest\n\nRUN apk update && apk add git && rm -rf /var/cache/apk/*\nRUN npm install pm2@next -g\nRUN mkdir -p /var/app\n\nWORKDIR /var/app\n"
  },
  {
    "path": "lib/templates/ecosystem-es.tpl",
    "content": "const config = {\n  apps : [{\n    script: 'index.js',\n    watch: '.'\n  }, {\n    script: './service-worker/',\n    watch: ['./service-worker']\n  }],\n\n  deploy : {\n    production : {\n      user : 'SSH_USERNAME',\n      host : 'SSH_HOSTMACHINE',\n      ref  : 'origin/master',\n      repo : 'GIT_REPOSITORY',\n      path : 'DESTINATION_PATH',\n      'pre-deploy-local': '',\n      'post-deploy' : 'npm install && pm2 reload ecosystem.config.js --env production',\n      'pre-setup': ''\n    }\n  }\n};\n\nexport default config;\n"
  },
  {
    "path": "lib/templates/ecosystem-simple-es.tpl",
    "content": "const config = {\n  apps : [{\n    name   : \"app1\",\n    script : \"./app.js\"\n  }]\n}\n\nexport default config;\n"
  },
  {
    "path": "lib/templates/ecosystem-simple.tpl",
    "content": "module.exports = {\n  apps : [{\n    name   : \"app1\",\n    script : \"./app.js\"\n  }]\n}\n"
  },
  {
    "path": "lib/templates/ecosystem.tpl",
    "content": "module.exports = {\n  apps : [{\n    script: 'index.js',\n    watch: '.'\n  }, {\n    script: './service-worker/',\n    watch: ['./service-worker']\n  }],\n\n  deploy : {\n    production : {\n      user : 'SSH_USERNAME',\n      host : 'SSH_HOSTMACHINE',\n      ref  : 'origin/master',\n      repo : 'GIT_REPOSITORY',\n      path : 'DESTINATION_PATH',\n      'pre-deploy-local': '',\n      'post-deploy' : 'npm install && pm2 reload ecosystem.config.js --env production',\n      'pre-setup': ''\n    }\n  }\n};\n"
  },
  {
    "path": "lib/templates/init-scripts/launchd.tpl",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n  <dict>\n\t  <key>Label</key>\n\t  <string>com.PM2</string>\n\t  <key>UserName</key>\n\t  <string>%USER%</string>\n    <key>KeepAlive</key>\n    <true/>\n\t  <key>ProgramArguments</key>\n\t  <array>\n\t\t  <string>/bin/sh</string>\n\t\t  <string>-c</string>\n\t\t  <string>%PM2_PATH% resurrect</string>\n\t  </array>\n\t  <key>RunAtLoad</key>\n\t  <true/>\n\t  <key>OnDemand</key>\n\t  <false/>\n\t  <key>LaunchOnlyOnce</key>\n\t  <true/>\n\t  <key>EnvironmentVariables</key>\n    <dict>\n      <key>PATH</key>\n      <string>%NODE_PATH%</string>\n      <key>PM2_HOME</key>\n      <string>%HOME_PATH%</string>\n    </dict>\n\t  <key>StandardErrorPath</key>\n\t  <string>/tmp/com.PM2.err</string>\n\t  <key>StandardOutPath</key>\n\t  <string>/tmp/com.PM2.out</string>\n  </dict>\n</plist>\n"
  },
  {
    "path": "lib/templates/init-scripts/openrc.tpl",
    "content": "#!/sbin/openrc-run\n# Copyright 2013-2022 the PM2 project authors. All rights reserved.\n# Init script automatically generated by pm2 startup\n\ndescription=\"Production process manager for Node.js apps with a built-in load balancer.\"\n\nextra_started_commands=\"reload\"\n\nPM2=\"%PM2_PATH%\"\nuser=${PM2_USER:-%USER%}\nexport PM2_HOME=$(eval echo ~${user})\"/.pm2/\"\n# Options for start-stop-daemon (default start function)\ncommand=${PM2}\ncommand_user=${user}\ncommand_args=\"resurrect\"\npidfile=${PM2_HOME}/pm2.pid\n\nrun_pm2_as_user() {\n\teinfo \"${PM2} $@\"\n\teval su -l ${user} -c \\'${PM2} $@\\'\n}\n\ndepend() {\n\tneed net\n\tneed localmount\n\tafter bootmisc\n}\n\nstart_post() {\n\tif [ \"${user}\" == \"root\" ]; then\n\t\tewarn \"PM2: Better run this daemon as a non root user. To set this user create\"\n\t\tewarn \"PM2: /etc/conf.d/pm2 file and define 'PM2_USER=user' there.\"\n\t\tewarn \"PM2: Note user MUST have home directory for PM2 logs/state/etc...\"\n\tfi\n\teinfo \"PM2: Process Manager started. To start services run:\"\n\teinfo \"PM2: # su -l ${user} -c '$PM2 start /path/to/app'\"\n}\n\nstop() {\n\tebegin \"Stopping PM2 process manager...\"\n\trun_pm2_as_user dump\n\trun_pm2_as_user kill\n\teend $?\n}\n\nreload() {\n\tebegin \"Reloading pm2\"\n\trun_pm2_as_user reload all\n\teend $?\n}\n\n# vim: ts=4\n"
  },
  {
    "path": "lib/templates/init-scripts/pm2-init-amazon.sh",
    "content": "#!/bin/bash\n#\n# pm2 Process manager for NodeJS\n#\n# chkconfig: 345 80 20\n#\n# description: PM2 next gen process manager for Node.js\n# processname: pm2\n#\n### BEGIN INIT INFO\n# Provides:          pm2\n# Required-Start: $local_fs $remote_fs\n# Required-Stop: $local_fs $remote_fs\n# Should-Start: $network\n# Should-Stop: $network\n# Default-Start:        2 3 4 5\n# Default-Stop:         0 1 6\n# Short-Description: PM2 init script\n# Description: PM2 is the next gen process manager for Node.js\n### END INIT INFO\n\nNAME=pm2\nPM2=%PM2_PATH%\nUSER=%USER%\n\nexport PATH=%NODE_PATH%:$PATH\nexport PM2_HOME=\"%HOME_PATH%\"\n\nlockfile=\"/var/lock/subsys/pm2-init.sh\"\n\nsuper() {\n    su - $USER -c \"PATH=$PATH; PM2_HOME=$PM2_HOME $*\"\n}\n\nstart() {\n    echo \"Starting $NAME\"\n    super $PM2 resurrect\n    retval=$?\n    [ $retval -eq 0 ] && touch $lockfile\n}\n\nstop() {\n    echo \"Stopping $NAME\"\n    super $PM2 kill\n    rm -f $lockfile\n}\n\nrestart() {\n    echo \"Restarting $NAME\"\n    stop\n    start\n}\n\nreload() {\n    echo \"Reloading $NAME\"\n    super $PM2 reload all\n}\n\nstatus() {\n    echo \"Status for $NAME:\"\n    super $PM2 list\n    RETVAL=$?\n}\n\ncase \"$1\" in\n    start)\n        start\n        ;;\n    stop)\n        stop\n        ;;\n    status)\n        status\n        ;;\n    restart)\n        restart\n        ;;\n    reload)\n        reload\n        ;;\n    *)\n        echo \"Usage: {start|stop|status|restart|reload}\"\n        exit 1\n        ;;\nesac\nexit $RETVAL\n"
  },
  {
    "path": "lib/templates/init-scripts/rcd-openbsd.tpl",
    "content": "#!/bin/sh\n#\n# from /usr/ports/infrastructure/templates/rc.template\n\ndaemon=\"/usr/local/bin/pm2\"\n#daemon_flags=\n#daemon_rtable=0\n#daemon_timeout=\"30\"\ndaemon_user=\"%USER%\"\n\n. /etc/rc.d/rc.subr\n\npexp=\"node: PM2.*God Daemon.*\"\n#rc_bg= # (undefined)\n#rc_reload= # (undefined)\n#rc_usercheck=YES\n\n#rc_pre() {\n#}\n\nrc_start() {\n\t${rcexec} \"${daemon} ${daemon_flags} resurrect\"\n}\n\n#rc_check() {\n#\tpgrep -T \"${daemon_rtable}\" -q -xf \"${pexp}\"\n#}\n\nrc_reload() {\n\t${rcexec} \"${daemon} reload all\"\n\t#pkill -HUP -T \"${daemon_rtable}\" -xf \"${pexp}\"\n}\n\n#rc_stop() {\n#\tpkill -T \"${daemon_rtable}\" -xf \"${pexp}\"\n#}\n\n#rc_post() {\n#}\n\nrc_cmd $1\n"
  },
  {
    "path": "lib/templates/init-scripts/rcd.tpl",
    "content": "#!/bin/sh\n\n# PROVIDE: pm2\n# REQUIRE: LOGIN\n# KEYWORD: shutdown\n\n. /etc/rc.subr\n\nname=\"%SERVICE_NAME%\"\nrcvar=\"%SERVICE_NAME%_enable\"\n\nstart_cmd=\"pm2_start\"\nstop_cmd=\"pm2_stop\"\nreload_cmd=\"pm2_reload\"\nstatus_cmd=\"pm2_status\"\nextra_commands=\"reload status\"\n\npm2()\n{\n  env PATH=\"$PATH:%NODE_PATH%\" PM2_HOME=\"%HOME_PATH%\" su -m \"%USER%\" -c \"%PM2_PATH% $*\"\n}\n\npm2_start()\n{\n  pm2 resurrect\n}\n\npm2_stop()\n{\n  pm2 kill\n}\n\npm2_reload()\n{\n  pm2 reload all\n}\n\npm2_status()\n{\n  pm2 list\n}\n\nload_rc_config $name\nrun_rc_command \"$1\"\n"
  },
  {
    "path": "lib/templates/init-scripts/smf.tpl",
    "content": "<?xml version=\"1.0\"?>\n<!DOCTYPE service_bundle SYSTEM \"/usr/share/lib/xml/dtd/service_bundle.dtd.1\">\n<service_bundle type=\"manifest\" name=\"%SERVICE_NAME%\">\n    <service name=\"application/%SERVICE_NAME%\" type=\"service\" version=\"1\">\n        <create_default_instance enabled=\"false\"/>\n        <single_instance/>\n        \n        <dependency name=\"network\" grouping=\"require_all\" restart_on=\"error\" type=\"service\">\n            <service_fmri value=\"svc:/milestone/network:default\"/>\n        </dependency>\n        \n        <dependency name=\"filesystem\" grouping=\"require_all\" restart_on=\"error\" type=\"service\">\n            <service_fmri value=\"svc:/system/filesystem/local\"/>\n        </dependency>\n        \n        <method_context>\n            <method_environment>\n                <envvar name='PATH' value=\"%NODE_PATH%:/usr/local/sbin:/usr/local/bin:/opt/local/sbin:/opt/local/bin:/usr/sbin:/usr/bin:/sbin\"/>\n                <envvar name='PM2_HOME' value=\"%HOME_PATH%\"/>\n            </method_environment>\n        </method_context>\n\n        <exec_method type=\"method\" name=\"start\" exec=\"%PM2_PATH% resurrect\" timeout_seconds=\"60\"/>\n        <exec_method type=\"method\" name=\"refresh\" exec=\"%PM2_PATH% reload all\" timeout_seconds=\"60\"/>\n        <exec_method type=\"method\" name=\"stop\" exec=\"%PM2_PATH% kill\" timeout_seconds=\"60\"/>\n        \n        <property_group name=\"startd\" type=\"framework\">\n            <propval name=\"duration\" type=\"astring\" value=\"contract\"/>\n            <propval name=\"ignore_error\" type=\"astring\" value=\"core,signal\"/>\n        </property_group>\n        \n        <property_group name=\"application\" type=\"application\"></property_group>\n        <stability value=\"Evolving\"/>\n        \n        <template>\n            <common_name>\n                <loctext xml:lang=\"C\">\n                    PM2 process manager\n                </loctext>\n            </common_name>\n        </template>\n    </service>\n</service_bundle>"
  },
  {
    "path": "lib/templates/init-scripts/systemd-online.tpl",
    "content": "[Unit]\nDescription=PM2 process manager\nDocumentation=https://pm2.keymetrics.io/\nAfter=network-online.target\nRestart=on-failure\n\n[Service]\nType=forking\nUser=%USER%\nLimitNOFILE=infinity\nLimitNPROC=infinity\nLimitCORE=infinity\nEnvironment=PATH=%NODE_PATH%:/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin\nEnvironment=PM2_HOME=%HOME_PATH%\nPIDFile=%HOME_PATH%/pm2.pid\n\nExecStart=%PM2_PATH% resurrect\nExecReload=%PM2_PATH% reload all\nExecStop=%PM2_PATH% kill\n\n[Install]\nWantedBy=network-online.target\n"
  },
  {
    "path": "lib/templates/init-scripts/systemd.tpl",
    "content": "[Unit]\nDescription=PM2 process manager\nDocumentation=https://pm2.keymetrics.io/\nAfter=network.target\n\n[Service]\nType=forking\nUser=%USER%\nLimitNOFILE=infinity\nLimitNPROC=infinity\nLimitCORE=infinity\nEnvironment=PATH=%NODE_PATH%:/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin\nEnvironment=PM2_HOME=%HOME_PATH%\nPIDFile=%HOME_PATH%/pm2.pid\nRestart=on-failure\n\nExecStart=%PM2_PATH% resurrect\nExecReload=%PM2_PATH% reload all\nExecStop=%PM2_PATH% kill\n\n[Install]\nWantedBy=multi-user.target\n"
  },
  {
    "path": "lib/templates/init-scripts/upstart.tpl",
    "content": "#!/bin/bash\n### BEGIN INIT INFO\n# Provides:        pm2\n# Required-Start:  $local_fs $remote_fs $network\n# Required-Stop:   $local_fs $remote_fs $network\n# Default-Start:   2 3 4 5\n# Default-Stop:    0 1 6\n# Short-Description: PM2 Init script\n# Description: PM2 process manager\n### END INIT INFO\n\nNAME=pm2\nPM2=%PM2_PATH%\nUSER=%USER%\nDEFAULT=/etc/default/$NAME\n\nexport PATH=%NODE_PATH%:$PATH\nexport PM2_HOME=\"%HOME_PATH%\"\n\n# The following variables can be overwritten in $DEFAULT\n\n# maximum number of open files\nMAX_OPEN_FILES=\n\n# overwrite settings from default file\nif [ -f \"$DEFAULT\" ]; then\n    . \"$DEFAULT\"\nfi\n\n# set maximum open files if set\nif [ -n \"$MAX_OPEN_FILES\" ]; then\n    ulimit -n $MAX_OPEN_FILES\nfi\n\nget_user_shell() {\n    local shell\n    shell=$(getent passwd \"${1:-$(whoami)}\" | cut -d: -f7 | sed -e 's/[[:space:]]*$//')\n\n    if [[ $shell == *\"/sbin/nologin\" ]] || [[ $shell == \"/bin/false\" ]] || [[ -z \"$shell\" ]];\n    then\n      shell=\"/bin/bash\"\n    fi\n\n    echo \"$shell\"\n}\n\nsuper() {\n    local shell\n    shell=$(get_user_shell $USER)\n    su - \"$USER\" -s \"$shell\" -c \"PATH=$PATH; PM2_HOME=$PM2_HOME $*\"\n}\n\nstart() {\n    echo \"Starting $NAME\"\n    super $PM2 resurrect\n}\n\nstop() {\n    super $PM2 kill\n}\n\nrestart() {\n    echo \"Restarting $NAME\"\n    stop\n    start\n}\n\nreload() {\n    echo \"Reloading $NAME\"\n    super $PM2 reload all\n}\n\nstatus() {\n    echo \"Status for $NAME:\"\n    super $PM2 list\n    RETVAL=$?\n}\n\ncase \"$1\" in\n    start)\n        start\n        ;;\n    stop)\n        stop\n        ;;\n    status)\n        status\n        ;;\n    restart)\n        restart\n        ;;\n    reload)\n        reload\n        ;;\n    force-reload)\n        reload\n        ;;\n    *)\n        echo \"Usage: {start|stop|status|restart|reload|force-reload}\"\n        exit 1\n        ;;\nesac\nexit $RETVAL\n"
  },
  {
    "path": "lib/templates/logrotate.d/pm2",
    "content": "%HOME_PATH%/pm2.log %HOME_PATH%/logs/*.log {\n        rotate 12\n        weekly\n        missingok\n        notifempty\n        compress\n        delaycompress\n        copytruncate\n        create 0640 %USER% %USER%\n}\n"
  },
  {
    "path": "lib/templates/sample-apps/http-server/README.md",
    "content": "\n# Basic HTTP Server and Cluster mode\n\nIn this boilerplate it will start an http server in cluster mode.\n\nYou can check the content of the ecosystem.config.js on how to start mutliple instances of the same HTTP application in order to get the most from your working system.\n\n## Via CLI\n\nVia CLI you can start any HTTP/TCP application in cluster mode with:\n\n```bash\n$ pm2 start api.js -i max\n```\n"
  },
  {
    "path": "lib/templates/sample-apps/http-server/api.js",
    "content": "\nvar http = require('http');\n\nvar server = http.createServer(function(req, res) {\n  res.writeHead(200);\n  res.end('hey');\n}).listen(process.env.PORT || 8000, function() {\n  console.log('App listening on port %d', server.address().port);\n});\n"
  },
  {
    "path": "lib/templates/sample-apps/http-server/ecosystem.config.js",
    "content": "module.exports = {\n  apps : [{\n    name: 'API',\n    script: 'api.js',\n    instances: 4,\n    max_memory_restart: '1G',\n    env: {\n      NODE_ENV: 'development'\n    },\n    env_production: {\n      NODE_ENV: 'production'\n    }\n  }]\n};\n"
  },
  {
    "path": "lib/templates/sample-apps/http-server/package.json",
    "content": "{\n  \"name\": \"simple-http-server\",\n  \"version\": \"1.0.0\",\n  \"description\": \"Simple HTTP server that can be used in cluster mode\",\n  \"main\": \"index.js\",\n  \"scripts\": {\n    \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n  },\n  \"author\": \"\",\n  \"license\": \"ISC\"\n}\n"
  },
  {
    "path": "lib/templates/sample-apps/pm2-plus-metrics-actions/README.md",
    "content": "\n# pm2 custom metrics boilerplate\n\nIn this boilerplate you will discover a working example of custom metrics feature.\n\nMetrics covered are:\n- io.metric\n- io.counter\n- io.meter\n- io.histogram\n\n## What is Custom Metrics?\n\nCustom metrics is a powerfull way to get more visibility from a running application. It will allow you to monitor in realtime the current value of variables, know the number of actions being processed, measure latency and much more.\n\nOnce you have plugged in some custom metrics you will be able to monitor their value in realtime with\n\n`pm2 monit`\n\nOr\n\n`pm2 describe`\n\nOr on the PM2+ Web interface\n\n`pm2 open`\n\n## Example\n\n```javascript\nconst io = require('@pm2/io')\n\nconst currentReq = io.counter({\n  name: 'CM: Current Processing',\n  type: 'counter'\n})\n\nsetInterval(() => {\n  currentReq.inc()\n}, 1000)\n```\n\n## Documentation\n\nhttps://doc.pm2.io/en/plus/guide/custom-metrics/\n"
  },
  {
    "path": "lib/templates/sample-apps/pm2-plus-metrics-actions/custom-metrics.js",
    "content": "\nconst io = require('@pm2/io')\n\n// Straight Metric\nvar user_count = 10\n\nconst users = io.metric({\n  name: 'CM: Realtime user',\n  value: () => {\n    return user_count\n  }\n})\n\n// or users.set(user_count)\n\n// Counter (.inc() .dec())\nconst currentReq = io.counter({\n  name: 'CM: Current Processing',\n  type: 'counter'\n})\n\nsetInterval(() => {\n  currentReq.inc()\n}, 1000)\n\n// Meter\nconst reqsec = io.meter({\n  name: 'CM: req/sec'\n})\n\nsetInterval(() => {\n  reqsec.mark()\n}, 100)\n\n\n// Histogram\nconst latency = io.histogram({\n  name: 'CM: latency'\n});\n\nvar latencyValue = 0;\n\nsetInterval(() => {\n  latencyValue = Math.round(Math.random() * 100);\n  latency.update(latencyValue);\n}, 100)\n\n\n////////////////////\n// Custom Actions //\n////////////////////\n\nio.action('add user', (done) => {\n  user_count++\n  done({success:true})\n})\n\nio.action('remove user', (done) => {\n  user_count++\n  done({success:true})\n})\n\nio.action('with params', (arg, done) => {\n  console.log(arg)\n  done({success:arg})\n})\n"
  },
  {
    "path": "lib/templates/sample-apps/pm2-plus-metrics-actions/ecosystem.config.js",
    "content": "module.exports = {\n  apps : [{\n    name: 'Custom Metrics',\n    script: 'custom-metrics.js',\n    env: {\n      NODE_ENV: 'development'\n    },\n    env_production: {\n      NODE_ENV: 'production'\n    }\n  }]\n};\n"
  },
  {
    "path": "lib/templates/sample-apps/pm2-plus-metrics-actions/package.json",
    "content": "{\n  \"name\": \"pm2-plus-custom-metrics\",\n  \"version\": \"1.0.0\",\n  \"description\": \"Example that shows how to use pm2+ custom metrics\",\n  \"main\": \"index.js\",\n  \"scripts\": {\n    \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n  },\n  \"author\": \"\",\n  \"license\": \"ISC\"\n}\n"
  },
  {
    "path": "lib/templates/sample-apps/python-app/README.md",
    "content": "\n# Managing a Python Application\n\nOn this boilerlate you will see how you can start a Python application with PM2.\n"
  },
  {
    "path": "lib/templates/sample-apps/python-app/echo.py",
    "content": "#!/usr/bin/python\nimport time\n\nwhile 1:\n    print(\"Start : %s\" % time.ctime())\n    print(\"second line\")\n    time.sleep(1)\n"
  },
  {
    "path": "lib/templates/sample-apps/python-app/ecosystem.config.js",
    "content": "module.exports = {\n  apps : [{\n    name: 'API',\n    script: 'echo.py',\n    env: {\n      NODE_ENV: 'development'\n    },\n    env_production: {\n      NODE_ENV: 'production'\n    }\n  }]\n};\n"
  },
  {
    "path": "lib/templates/sample-apps/python-app/package.json",
    "content": "{\n  \"name\": \"python-app\",\n  \"version\": \"1.0.0\",\n  \"description\": \"Example on how to manage a Python application with PM2\",\n  \"main\": \"index.js\",\n  \"scripts\": {\n    \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n  },\n  \"author\": \"\",\n  \"license\": \"ISC\"\n}\n"
  },
  {
    "path": "lib/tools/Config.js",
    "content": "/**\n * Copyright 2013-2022 the PM2 project authors. All rights reserved.\n * Use of this source code is governed by a license that\n * can be found in the LICENSE file.\n */\nvar util    = require('util');\n\n/**\n * Validator of configured file / commander options.\n */\nvar Config = module.exports = {\n  _errMsgs: {\n    'require': '\"%s\" is required',\n    'type'   : 'Expect \"%s\" to be a typeof %s, but now is %s',\n    'regex'  : 'Verify \"%s\" with regex failed, %s',\n    'max'    : 'The maximum of \"%s\" is %s, but now is %s',\n    'min'    : 'The minimum of \"%s\" is %s, but now is %s'\n  },\n  /**\n   * Schema definition.\n   * @returns {exports|*}\n   */\n  get schema(){\n    // Cache.\n    if (this._schema) {\n      return this._schema;\n    }\n    // Render aliases.\n    this._schema = require('../API/schema');\n    for (var k in this._schema) {\n      if (k.indexOf('\\\\') > 0) {\n        continue;\n      }\n      var aliases = [\n        k.split('_').map(function(n, i){\n          if (i != 0 && n && n.length > 1) {\n            return n[0].toUpperCase() + n.slice(1);\n          }\n          return n;\n        }).join('')\n      ];\n\n      if (this._schema[k].alias && Array.isArray(this._schema[k].alias)) {\n        // If multiple aliases, merge\n        this._schema[k].alias.forEach(function(alias) {\n          aliases.splice(0, 0, alias);\n        });\n      }\n      else if (this._schema[k].alias)\n        aliases.splice(0, 0, this._schema[k].alias);\n\n      this._schema[k].alias = aliases;\n    }\n    return this._schema;\n  }\n};\n\n/**\n * Filter / Alias options\n */\nConfig.filterOptions = function(cmd) {\n  var conf = {};\n  var schema = this.schema;\n\n  for (var key in schema) {\n    var aliases = schema[key].alias;\n    aliases && aliases.forEach(function(alias){\n      if (typeof(cmd[alias]) !== 'undefined') {\n        conf[key] || (conf[key] = cmd[alias]);\n      }\n    });\n  }\n\n  return conf;\n};\n\n/**\n * Verify JSON configurations.\n * @param {Object} json\n * @returns {{errors: Array, config: {}}}\n */\nConfig.validateJSON = function(json){\n  // clone config\n  var conf = Object.assign({}, json),\n      res = {};\n  this._errors = [];\n\n  var regexKeys = {}, defines = this.schema;\n\n  for (var sk in defines) {\n    // Pick up RegExp keys.\n    if (sk.indexOf('\\\\') >= 0) {\n      regexKeys[sk] = false;\n      continue;\n    }\n\n    var aliases = defines[sk].alias;\n\n    aliases && aliases.forEach(function(alias){\n      conf[sk] || (conf[sk] = json[alias]);\n    })\n\n    var val = conf[sk];\n    delete conf[sk];\n\n    // Validate key-value pairs.\n    if (val === undefined ||\n        val === null ||\n        ((val = this._valid(sk, val)) === null)) {\n\n      // If value is not defined\n      // Set default value (via schema.json)\n      if (typeof(defines[sk].default) !== 'undefined')\n        res[sk] = defines[sk].default;\n      continue;\n    }\n    //console.log(sk, val, val === null, val === undefined);\n    res[sk] = val;\n  }\n\n  // Validate RegExp values.\n  var hasRegexKey = false;\n  for (var k in regexKeys) {\n    hasRegexKey = true;\n    regexKeys[k] = new RegExp(k);\n  }\n  if (hasRegexKey) {\n    for (var k in conf) {\n      for (var rk in regexKeys) {\n        if (regexKeys[rk].test(k))\n          if (this._valid(k, conf[k], defines[rk])) {\n            res[k] = conf[k];\n            delete conf[k];\n          }\n      }\n    }\n  }\n\n  return {errors: this._errors, config: res};\n};\n\n/**\n * Validate key-value pairs by specific schema\n * @param {String} key\n * @param {Mixed} value\n * @param {Object} sch\n * @returns {*}\n * @private\n */\nConfig._valid = function(key, value, sch){\n  var sch = sch || this.schema[key],\n      scht = typeof sch.type == 'string' ? [sch.type] : sch.type;\n\n  // Required value.\n  var undef = typeof value == 'undefined';\n  if(this._error(sch.require && undef, 'require', key)){\n    return null;\n  }\n\n  // If undefined, make a break.\n  if (undef) {\n    return null;\n  }\n\n  // Wrap schema types.\n  scht = scht.map(function(t){\n    return '[object ' + t[0].toUpperCase() + t.slice(1) + ']'\n  });\n\n  // Typeof value.\n  var type = Object.prototype.toString.call(value), nt = '[object Number]';\n\n  // Auto parse Number\n  if (type != '[object Boolean]' && scht.indexOf(nt) >= 0 && !isNaN(value)) {\n    value = parseFloat(value);\n    type = nt;\n  }\n\n  // Verify types.\n  if (this._error(!~scht.indexOf(type), 'type', key, scht.join(' / '), type)) {\n    return null;\n  }\n\n  // Verify RegExp if exists.\n  if (this._error(type == '[object String]' && sch.regex && !(new RegExp(sch.regex)).test(value),\n      'regex', key, sch.desc || ('should match ' + sch.regex))) {\n    return null;\n  }\n\n  // Verify maximum / minimum of Number value.\n  if (type == '[object Number]') {\n    if (this._error(typeof sch.max != 'undefined' && value > sch.max, 'max', key, sch.max, value)) {\n      return null;\n    }\n    if (this._error(typeof sch.min != 'undefined' && value < sch.min, 'min', key, sch.min, value)) {\n      return null;\n    }\n  }\n\n  // If first type is Array, but current is String, try to split them.\n  if(scht.length > 1 && type != scht[0] && type == '[object String]'){\n    if(scht[0] == '[object Array]') {\n      value = value.split(/([\\w\\-]+\\=\"[^\"]*\")|([\\w\\-]+\\='[^']*')|\"([^\"]*)\"|'([^']*)'|\\s/)\n      // unfortunately, js does not support lookahead RegExp (/(?<!\\\\)\\s+/) now (until next ver).\n      //value = value.split(/((?<![\\w\\-])([\\w\\-]+\\=\"[^\"]*\")|(?<![\\w\\-])([\\w\\-]+\\='[^']*')|\"([^\"]*)\"|'([^']*)'|\\s )/)\n        .filter(function(v){\n          return v && v.trim();\n        });\n    }\n  }\n\n  // Custom types: sbyte && stime.\n  if(sch.ext_type && type == '[object String]' && value.length >= 2) {\n    var seed = {\n      'sbyte': {\n        'G': 1024 * 1024 * 1024,\n        'M': 1024 * 1024,\n        'K': 1024\n      },\n      'stime': {\n        'h': 60 * 60 * 1000,\n        'm': 60 * 1000,\n        's': 1000\n      }\n    }[sch.ext_type];\n\n    if(seed){\n      value = parseFloat(value.slice(0, -1)) * (seed[value.slice(-1)]);\n    }\n  }\n  return value;\n};\n\n/**\n * Wrap errors.\n * @param {Boolean} possible A value indicates whether it is an error or not.\n * @param {String} type\n * @returns {*}\n * @private\n */\nConfig._error = function(possible, type){\n  if (possible) {\n    var args = Array.prototype.slice.call(arguments);\n    args.splice(0, 2, this._errMsgs[type]);\n    this._errors && this._errors.push(util.format.apply(null, args));\n  }\n  return possible;\n}\n"
  },
  {
    "path": "lib/tools/IsAbsolute.js",
    "content": "'use strict';\n\nfunction posix(path) {\n\treturn path.charAt(0) === '/';\n}\n\nfunction win32(path) {\n\t// https://github.com/nodejs/node/blob/b3fcc245fb25539909ef1d5eaa01dbf92e168633/lib/path.js#L56\n\tvar splitDeviceRe = /^([a-zA-Z]:|[\\\\/]{2}[^\\\\/]+[\\\\/]+[^\\\\/]+)?([\\\\/])?([\\s\\S]*?)$/;\n\tvar result = splitDeviceRe.exec(path);\n\tvar device = result[1] || '';\n\tvar isUnc = Boolean(device && device.charAt(1) !== ':');\n\n\t// UNC paths are always absolute\n\treturn Boolean(result[2] || isUnc);\n}\n\nmodule.exports = process.platform === 'win32' ? win32 : posix;\nmodule.exports.posix = posix;\nmodule.exports.win32 = win32;\n"
  },
  {
    "path": "lib/tools/copydirSync.js",
    "content": "var fs = require('fs');\nvar path = require('path');\n/*\n  options: {\n    utimes: false,  // Boolean | Object, keep utimes if true\n    mode: false,    // Boolean | Number, keep file mode if true\n    cover: true,    // Boolean, cover if file exists\n    filter: true,   // Boolean | Function, file filter\n  }\n*/\nfunction copydirSync(from, to, options) {\n  if (typeof options === 'function') {\n    options = {\n      filter: options\n    };\n  }\n  if(typeof options === 'undefined') options = {};\n  if(typeof options.cover === 'undefined') {\n    options.cover = true;\n  }\n  options.filter = typeof options.filter === 'function' ? options.filter : function(state, filepath, filename) {\n    return options.filter;\n  };\n  var stats = fs.lstatSync(from);\n  var statsname = stats.isDirectory() ? 'directory' :\n    stats.isFile() ? 'file' :\n      stats.isSymbolicLink() ? 'symbolicLink' :\n      '';\n  var valid = options.filter(statsname, from, path.dirname(from), path.basename(from));\n\n  if (statsname === 'directory' || statsname === 'symbolicLink') {\n    // Directory or SymbolicLink\n    if(valid) {\n      try {\n        fs.statSync(to);\n      } catch(err) {\n        if(err.code === 'ENOENT') {\n          fs.mkdirSync(to, { recursive: true });\n          options.debug && console.log('>> ' + to);\n        } else {\n          throw err;\n        }\n      }\n      rewriteSync(to, options, stats);\n      if (statsname != 'symbolicLink')\n        listDirectorySync(from, to, options);\n    }\n  } else if(stats.isFile()) {\n    // File\n    if(valid) {\n      if(options.cover) {\n        writeFileSync(from, to, options, stats);\n      } else {\n        try {\n          fs.statSync(to);\n        } catch(err) {\n          if(err.code === 'ENOENT') {\n            writeFileSync(from, to, options, stats);\n          } else {\n            throw err;\n          }\n        }\n      }\n    }\n  } else {\n    throw new Error('stats invalid: '+ from);\n  }\n};\n\nfunction listDirectorySync(from, to, options) {\n  var files = fs.readdirSync(from);\n  copyFromArraySync(files, from, to, options);\n}\n\nfunction copyFromArraySync(files, from, to, options) {\n  if(files.length === 0) return true;\n  var f = files.shift();\n  copydirSync(path.join(from, f), path.join(to, f), options);\n  copyFromArraySync(files, from, to, options);\n}\n\nfunction writeFileSync(from, to, options, stats) {\n  fs.writeFileSync(to, fs.readFileSync(from, 'binary'), 'binary');\n  options.debug && console.log('>> ' + to);\n  rewriteSync(to, options, stats);\n}\n\nfunction rewriteSync(f, options, stats, callback) {\n  if(options.cover) {\n    var mode = options.mode === true ? stats.mode : options.mode;\n    var utimes = options.utimes === true ? {\n      atime: stats.atime,\n      mtime: stats.mtime\n    } : options.utimes;\n    mode && fs.chmodSync(f, mode);\n    utimes && fs.utimesSync(f, utimes.atime, utimes.mtime);\n  }\n  return true;\n}\n\nmodule.exports = copydirSync;\n"
  },
  {
    "path": "lib/tools/deleteFolderRecursive.js",
    "content": "\nconst fs = require('fs');\nconst Path = require('path');\n\nconst deleteFolderRecursive = function(path) {\n  if (fs.existsSync(path)) {\n    fs.readdirSync(path).forEach((file, index) => {\n      const curPath = Path.join(path, file);\n      if (fs.lstatSync(curPath).isDirectory()) { // recurse\n        deleteFolderRecursive(curPath);\n      } else { // delete file\n        fs.unlinkSync(curPath);\n      }\n    });\n    fs.rmdirSync(path);\n  }\n};\n\nmodule.exports = deleteFolderRecursive\n"
  },
  {
    "path": "lib/tools/find-package-json.js",
    "content": "'use strict';\n\nvar path = require('path')\n  , fs = require('fs');\n\n/**\n * Attempt to somewhat safely parse the JSON.\n *\n * @param {String} data JSON blob that needs to be parsed.\n * @returns {Object|false} Parsed JSON or false.\n * @api private\n */\nfunction parse(data) {\n  data = data.toString('utf-8');\n\n  //\n  // Remove a possible UTF-8 BOM (byte order marker) as this can lead to parse\n  // values when passed in to the JSON.parse.\n  //\n  if (data.charCodeAt(0) === 0xFEFF) data = data.slice(1);\n\n  try { return JSON.parse(data); }\n  catch (e) { return false; }\n}\n\n/**\n * Find package.json files.\n *\n * @param {String|Object} root The root directory we should start searching in.\n * @returns {Object} Iterator interface.\n * @api public\n */\nmodule.exports = function find(root) {\n  root = root || process.cwd();\n  if (typeof root !== \"string\") {\n    if (typeof root === \"object\" && typeof root.filename === 'string') {\n      root = root.filename;\n    } else {\n      throw new Error(\"Must pass a filename string or a module object to finder\");\n    }\n  }\n  return {\n    /**\n     * Return the parsed package.json that we find in a parent folder.\n     *\n     * @returns {Object} Value, filename and indication if the iteration is done.\n     * @api public\n     */\n    next: function next() {\n      if (root.match(/^(\\w:\\\\|\\/)$/)) return {\n        value: undefined,\n        filename: undefined,\n        done: true\n      };\n\n      var file = path.join(root, 'package.json')\n        , data;\n\n      root = path.resolve(root, '..');\n\n      if (fs.existsSync(file) && (data = parse(fs.readFileSync(file)))) {\n        data.__path = file;\n\n        return {\n          value: data,\n          filename: file,\n          done: false\n        };\n      }\n\n      return next();\n    }\n  };\n};\n"
  },
  {
    "path": "lib/tools/fmt.js",
    "content": "// --------------------------------------------------------------------------------------------------------------------\n//\n// fmt.js - Command line output formatting.\n//\n// Copyright (c) 2012 Andrew Chilton - http://chilts.org/\n// Written by Andrew Chilton <andychilton@gmail.com>\n//\n// License: http://opensource.org/licenses/MIT\n//\n// --------------------------------------------------------------------------------------------------------------------\n\nvar util = require('util');\n\n// --------------------------------------------------------------------------------------------------------------------\n\nvar sep  = '===============================================================================';\nvar line = '-------------------------------------------------------------------------------';\nvar field = '                    ';\n\n// --------------------------------------------------------------------------------------------------------------------\n\n// separator\nmodule.exports.separator = function() {\n    console.log(sep);\n};\n\n// alias the above\nmodule.exports.sep = module.exports.separator;\n\n// line\nmodule.exports.line = function() {\n    console.log(line);\n};\n\n// title\nmodule.exports.title = function(title) {\n    var out = '--- ' + title + ' ';\n    out += line.substr(out.length);\n    console.log(out);\n};\n\n// field\nmodule.exports.field = function(key, value) {\n    console.log('' + key + field.substr(key.length) + ' : ' + value);\n};\n\n// subfield\nmodule.exports.subfield = function(key, value) {\n    console.log('- ' + key + field.substr(key.length + 2) + ' : ' + value);\n};\n\n// list item\nmodule.exports.li = function(msg) {\n    console.log('* ' + msg);\n};\n\n// dump\nmodule.exports.dump = function(data, name) {\n    if ( name ) {\n        console.log(name + ' :', util.inspect(data, false, null, true));\n    }\n    else {\n        console.log(util.inspect(data, false, null, true));\n    }\n};\n\n// msg\nmodule.exports.msg = function(msg) {\n    console.log(msg);\n};\n\n// --------------------------------------------------------------------------------------------------------------------\n"
  },
  {
    "path": "lib/tools/isbinaryfile.js",
    "content": "/**\n * Copyright 2013-2022 the PM2 project authors. All rights reserved.\n * Use of this source code is governed by a license that\n * can be found in the LICENSE file.\n */\nvar fs = require('fs');\nvar max_bytes = 512;\n\nmodule.exports = function(bytes, size) {\n  // Read the file with no encoding for raw buffer access.\n  if (size === undefined) {\n    var file = bytes;\n    try {\n      if(!fs.statSync(file).isFile()) return false;\n    } catch (err) {\n      // otherwise continue on\n    }\n    var descriptor = fs.openSync(file, 'r');\n    try {\n      bytes = Buffer.alloc(max_bytes);\n      size = fs.readSync(descriptor, bytes, 0, bytes.length, 0);\n    } finally {\n      fs.closeSync(descriptor);\n    }\n  }\n  // async version has a function instead of a `size`\n  else if (typeof size === \"function\") {\n    var file = bytes, callback = size;\n    fs.stat(file, function(err, stat) {\n      if (err || !stat.isFile()) return callback(null, false);\n\n      fs.open(file, 'r', function(err, descriptor){\n        if (err) return callback(err);\n        var bytes = Buffer.alloc(max_bytes);\n        // Read the file with no encoding for raw buffer access.\n        fs.read(descriptor, bytes, 0, bytes.length, 0, function(err, size, bytes){\n          fs.close(descriptor, function(err2){\n            if (err || err2)\n              return callback(err || err2);\n            return callback(null, isBinaryCheck(bytes, size));\n          });\n        });\n      });\n    });\n  }\n\n  return isBinaryCheck(bytes, size);\n}\n\nfunction isBinaryCheck(bytes, size) {\n  if (size === 0)\n    return false;\n\n  var suspicious_bytes = 0;\n  var total_bytes = Math.min(size, max_bytes);\n\n  if (size >= 3 && bytes[0] == 0xEF && bytes[1] == 0xBB && bytes[2] == 0xBF) {\n    // UTF-8 BOM. This isn't binary.\n    return false;\n  }\n\n  for (var i = 0; i < total_bytes; i++) {\n    if (bytes[i] === 0) { // NULL byte--it's binary!\n      return true;\n    }\n    else if ((bytes[i] < 7 || bytes[i] > 14) && (bytes[i] < 32 || bytes[i] > 127)) {\n      // UTF-8 detection\n      if (bytes[i] > 193 && bytes[i] < 224 && i + 1 < total_bytes) {\n        i++;\n        if (bytes[i] > 127 && bytes[i] < 192) {\n          continue;\n        }\n      }\n      else if (bytes[i] > 223 && bytes[i] < 240 && i + 2 < total_bytes) {\n        i++;\n        if (bytes[i] > 127 && bytes[i] < 192 && bytes[i + 1] > 127 && bytes[i + 1] < 192) {\n          i++;\n          continue;\n        }\n      }\n      suspicious_bytes++;\n      // Read at least 32 bytes before making a decision\n      if (i > 32 && (suspicious_bytes * 100) / total_bytes > 10) {\n        return true;\n      }\n    }\n  }\n\n  if ((suspicious_bytes * 100) / total_bytes > 10) {\n    return true;\n  }\n\n  return false;\n}\n"
  },
  {
    "path": "lib/tools/json5.js",
    "content": "// json5.js\n// Modern JSON. See README.md for details.\n//\n// This file is based directly off of Douglas Crockford's json_parse.js:\n// https://github.com/douglascrockford/JSON-js/blob/master/json_parse.js\n\nvar JSON5 = (typeof exports === 'object' ? exports : {});\n\nJSON5.parse = (function () {\n    \"use strict\";\n\n// This is a function that can parse a JSON5 text, producing a JavaScript\n// data structure. It is a simple, recursive descent parser. It does not use\n// eval or regular expressions, so it can be used as a model for implementing\n// a JSON5 parser in other languages.\n\n// We are defining the function inside of another function to avoid creating\n// global variables.\n\n    var at,     // The index of the current character\n        ch,     // The current character\n        escapee = {\n            \"'\":  \"'\",\n            '\"':  '\"',\n            '\\\\': '\\\\',\n            '/':  '/',\n            '\\n': '',       // Replace escaped newlines in strings w/ empty string\n            b:    '\\b',\n            f:    '\\f',\n            n:    '\\n',\n            r:    '\\r',\n            t:    '\\t'\n        },\n        ws = [\n            ' ',\n            '\\t',\n            '\\r',\n            '\\n',\n            '\\v',\n            '\\f',\n            '\\xA0',\n            '\\uFEFF'\n        ],\n        text,\n\n        error = function (m) {\n\n// Call error when something is wrong.\n\n            var error = new SyntaxError();\n            error.message = m;\n            error.at = at;\n            error.text = text;\n            throw error;\n        },\n\n        next = function (c) {\n\n// If a c parameter is provided, verify that it matches the current character.\n\n            if (c && c !== ch) {\n                error(\"Expected '\" + c + \"' instead of '\" + ch + \"'\");\n            }\n\n// Get the next character. When there are no more characters,\n// return the empty string.\n\n            ch = text.charAt(at);\n            at += 1;\n            return ch;\n        },\n\n        peek = function () {\n\n// Get the next character without consuming it or\n// assigning it to the ch varaible.\n\n            return text.charAt(at);\n        },\n\n        identifier = function () {\n\n// Parse an identifier. Normally, reserved words are disallowed here, but we\n// only use this for unquoted object keys, where reserved words are allowed,\n// so we don't check for those here. References:\n// - http://es5.github.com/#x7.6\n// - https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Core_Language_Features#Variables\n// - http://docstore.mik.ua/orelly/webprog/jscript/ch02_07.htm\n\n            var key = ch;\n\n            // Identifiers must start with a letter, _ or $.\n            if ((ch !== '_' && ch !== '$') &&\n                    (ch < 'a' || ch > 'z') &&\n                    (ch < 'A' || ch > 'Z')) {\n                error(\"Bad identifier\");\n            }\n\n            // Subsequent characters can contain digits.\n            while (next() && (\n                    ch === '_' || ch === '$' ||\n                    (ch >= 'a' && ch <= 'z') ||\n                    (ch >= 'A' && ch <= 'Z') ||\n                    (ch >= '0' && ch <= '9'))) {\n                key += ch;\n            }\n\n            return key;\n        },\n\n        number = function () {\n\n// Parse a number value.\n\n            var number,\n                sign = '',\n                string = '',\n                base = 10;\n\n            if (ch === '-' || ch === '+') {\n                sign = ch;\n                next(ch);\n            }\n\n            // support for Infinity (could tweak to allow other words):\n            if (ch === 'I') {\n                number = word();\n                if (typeof number !== 'number' || isNaN(number)) {\n                    error('Unexpected word for number');\n                }\n                return (sign === '-') ? -number : number;\n            }\n\n            // support for NaN\n            if (ch === 'N' ) {\n              number = word();\n              if (!isNaN(number)) {\n                error('expected word to be NaN');\n              }\n              // ignore sign as -NaN also is NaN\n              return number;\n            }\n\n            if (ch === '0') {\n                string += ch;\n                next();\n                if (ch === 'x' || ch === 'X') {\n                    string += ch;\n                    next();\n                    base = 16;\n                } else if (ch >= '0' && ch <= '9') {\n                    error('Octal literal');\n                }\n            }\n\n            switch (base) {\n            case 10:\n                while (ch >= '0' && ch <= '9' ) {\n                    string += ch;\n                    next();\n                }\n                if (ch === '.') {\n                    string += '.';\n                    while (next() && ch >= '0' && ch <= '9') {\n                        string += ch;\n                    }\n                }\n                if (ch === 'e' || ch === 'E') {\n                    string += ch;\n                    next();\n                    if (ch === '-' || ch === '+') {\n                        string += ch;\n                        next();\n                    }\n                    while (ch >= '0' && ch <= '9') {\n                        string += ch;\n                        next();\n                    }\n                }\n                break;\n            case 16:\n                while (ch >= '0' && ch <= '9' || ch >= 'A' && ch <= 'F' || ch >= 'a' && ch <= 'f') {\n                    string += ch;\n                    next();\n                }\n                break;\n            }\n\n            if(sign === '-') {\n                number = -string;\n            } else {\n                number = +string;\n            }\n\n            if (!isFinite(number)) {\n                error(\"Bad number\");\n            } else {\n                return number;\n            }\n        },\n\n        string = function () {\n\n// Parse a string value.\n\n            var hex,\n                i,\n                string = '',\n                delim,      // double quote or single quote\n                uffff;\n\n// When parsing for string values, we must look for ' or \" and \\ characters.\n\n            if (ch === '\"' || ch === \"'\") {\n                delim = ch;\n                while (next()) {\n                    if (ch === delim) {\n                        next();\n                        return string;\n                    } else if (ch === '\\\\') {\n                        next();\n                        if (ch === 'u') {\n                            uffff = 0;\n                            for (i = 0; i < 4; i += 1) {\n                                hex = parseInt(next(), 16);\n                                if (!isFinite(hex)) {\n                                    break;\n                                }\n                                uffff = uffff * 16 + hex;\n                            }\n                            string += String.fromCharCode(uffff);\n                        } else if (ch === '\\r') {\n                            if (peek() === '\\n') {\n                                next();\n                            }\n                        } else if (typeof escapee[ch] === 'string') {\n                            string += escapee[ch];\n                        } else {\n                            break;\n                        }\n                    } else if (ch === '\\n') {\n                        // unescaped newlines are invalid; see:\n                        // https://github.com/aseemk/json5/issues/24\n                        // invalid unescaped chars?\n                        break;\n                    } else {\n                        string += ch;\n                    }\n                }\n            }\n            error(\"Bad string\");\n        },\n\n        inlineComment = function () {\n\n// Skip an inline comment, assuming this is one. The current character should\n// be the second / character in the // pair that begins this inline comment.\n// To finish the inline comment, we look for a newline or the end of the text.\n\n            if (ch !== '/') {\n                error(\"Not an inline comment\");\n            }\n\n            do {\n                next();\n                if (ch === '\\n' || ch === '\\r') {\n                    next();\n                    return;\n                }\n            } while (ch);\n        },\n\n        blockComment = function () {\n\n// Skip a block comment, assuming this is one. The current character should be\n// the * character in the /* pair that begins this block comment.\n// To finish the block comment, we look for an ending */ pair of characters,\n// but we also watch for the end of text before the comment is terminated.\n\n            if (ch !== '*') {\n                error(\"Not a block comment\");\n            }\n\n            do {\n                next();\n                while (ch === '*') {\n                    next('*');\n                    if (ch === '/') {\n                        next('/');\n                        return;\n                    }\n                }\n            } while (ch);\n\n            error(\"Unterminated block comment\");\n        },\n\n        comment = function () {\n\n// Skip a comment, whether inline or block-level, assuming this is one.\n// Comments always begin with a / character.\n\n            if (ch !== '/') {\n                error(\"Not a comment\");\n            }\n\n            next('/');\n\n            if (ch === '/') {\n                inlineComment();\n            } else if (ch === '*') {\n                blockComment();\n            } else {\n                error(\"Unrecognized comment\");\n            }\n        },\n\n        white = function () {\n\n// Skip whitespace and comments.\n// Note that we're detecting comments by only a single / character.\n// This works since regular expressions are not valid JSON(5), but this will\n// break if there are other valid values that begin with a / character!\n\n            while (ch) {\n                if (ch === '/') {\n                    comment();\n                } else if (ws.indexOf(ch) >= 0) {\n                    next();\n                } else {\n                    return;\n                }\n            }\n        },\n\n        word = function () {\n\n// true, false, or null.\n\n            switch (ch) {\n            case 't':\n                next('t');\n                next('r');\n                next('u');\n                next('e');\n                return true;\n            case 'f':\n                next('f');\n                next('a');\n                next('l');\n                next('s');\n                next('e');\n                return false;\n            case 'n':\n                next('n');\n                next('u');\n                next('l');\n                next('l');\n                return null;\n            case 'I':\n                next('I');\n                next('n');\n                next('f');\n                next('i');\n                next('n');\n                next('i');\n                next('t');\n                next('y');\n                return Infinity;\n            case 'N':\n              next( 'N' );\n              next( 'a' );\n              next( 'N' );\n              return NaN;\n            }\n            error(\"Unexpected '\" + ch + \"'\");\n        },\n\n        value,  // Place holder for the value function.\n\n        array = function () {\n\n// Parse an array value.\n\n            var array = [];\n\n            if (ch === '[') {\n                next('[');\n                white();\n                while (ch) {\n                    if (ch === ']') {\n                        next(']');\n                        return array;   // Potentially empty array\n                    }\n                    // ES5 allows omitting elements in arrays, e.g. [,] and\n                    // [,null]. We don't allow this in JSON5.\n                    if (ch === ',') {\n                        error(\"Missing array element\");\n                    } else {\n                        array.push(value());\n                    }\n                    white();\n                    // If there's no comma after this value, this needs to\n                    // be the end of the array.\n                    if (ch !== ',') {\n                        next(']');\n                        return array;\n                    }\n                    next(',');\n                    white();\n                }\n            }\n            error(\"Bad array\");\n        },\n\n        object = function () {\n\n// Parse an object value.\n\n            var key,\n                object = {};\n\n            if (ch === '{') {\n                next('{');\n                white();\n                while (ch) {\n                    if (ch === '}') {\n                        next('}');\n                        return object;   // Potentially empty object\n                    }\n\n                    // Keys can be unquoted. If they are, they need to be\n                    // valid JS identifiers.\n                    if (ch === '\"' || ch === \"'\") {\n                        key = string();\n                    } else {\n                        key = identifier();\n                    }\n\n                    white();\n                    next(':');\n                    object[key] = value();\n                    white();\n                    // If there's no comma after this pair, this needs to be\n                    // the end of the object.\n                    if (ch !== ',') {\n                        next('}');\n                        return object;\n                    }\n                    next(',');\n                    white();\n                }\n            }\n            error(\"Bad object\");\n        };\n\n    value = function () {\n\n// Parse a JSON value. It could be an object, an array, a string, a number,\n// or a word.\n\n        white();\n        switch (ch) {\n        case '{':\n            return object();\n        case '[':\n            return array();\n        case '\"':\n        case \"'\":\n            return string();\n        case '-':\n        case '+':\n        case '.':\n            return number();\n        default:\n            return ch >= '0' && ch <= '9' ? number() : word();\n        }\n    };\n\n// Return the json_parse function. It will have access to all of the above\n// functions and variables.\n\n    return function (source, reviver) {\n        var result;\n\n        text = String(source);\n        at = 0;\n        ch = ' ';\n        result = value();\n        white();\n        if (ch) {\n            error(\"Syntax error\");\n        }\n\n// If there is a reviver function, we recursively walk the new structure,\n// passing each name/value pair to the reviver function for possible\n// transformation, starting with a temporary root object that holds the result\n// in an empty key. If there is not a reviver function, we simply return the\n// result.\n\n        return typeof reviver === 'function' ? (function walk(holder, key) {\n            var k, v, value = holder[key];\n            if (value && typeof value === 'object') {\n                for (k in value) {\n                    if (Object.prototype.hasOwnProperty.call(value, k)) {\n                        v = walk(value, k);\n                        if (v !== undefined) {\n                            value[k] = v;\n                        } else {\n                            delete value[k];\n                        }\n                    }\n                }\n            }\n            return reviver.call(holder, key, value);\n        }({'': result}, '')) : result;\n    };\n}());\n\n// JSON5 stringify will not quote keys where appropriate\nJSON5.stringify = function (obj, replacer, space) {\n    if (replacer && (typeof(replacer) !== \"function\" && !isArray(replacer))) {\n        throw new Error('Replacer must be a function or an array');\n    }\n    var getReplacedValueOrUndefined = function(holder, key, isTopLevel) {\n        var value = holder[key];\n\n        // Replace the value with its toJSON value first, if possible\n        if (value && value.toJSON && typeof value.toJSON === \"function\") {\n            value = value.toJSON();\n        }\n\n        // If the user-supplied replacer if a function, call it. If it's an array, check objects' string keys for\n        // presence in the array (removing the key/value pair from the resulting JSON if the key is missing).\n        if (typeof(replacer) === \"function\") {\n            return replacer.call(holder, key, value);\n        } else if(replacer) {\n            if (isTopLevel || isArray(holder) || replacer.indexOf(key) >= 0) {\n                return value;\n            } else {\n                return undefined;\n            }\n        } else {\n            return value;\n        }\n    };\n\n    function isWordChar(char) {\n        return (char >= 'a' && char <= 'z') ||\n            (char >= 'A' && char <= 'Z') ||\n            (char >= '0' && char <= '9') ||\n            char === '_' || char === '$';\n    }\n\n    function isWordStart(char) {\n        return (char >= 'a' && char <= 'z') ||\n            (char >= 'A' && char <= 'Z') ||\n            char === '_' || char === '$';\n    }\n\n    function isWord(key) {\n        if (typeof key !== 'string') {\n            return false;\n        }\n        if (!isWordStart(key[0])) {\n            return false;\n        }\n        var i = 1, length = key.length;\n        while (i < length) {\n            if (!isWordChar(key[i])) {\n                return false;\n            }\n            i++;\n        }\n        return true;\n    }\n\n    // export for use in tests\n    JSON5.isWord = isWord;\n\n    // polyfills\n    function isArray(obj) {\n        if (Array.isArray) {\n            return Array.isArray(obj);\n        } else {\n            return Object.prototype.toString.call(obj) === '[object Array]';\n        }\n    }\n\n    function isDate(obj) {\n        return Object.prototype.toString.call(obj) === '[object Date]';\n    }\n\n    isNaN = isNaN || function(val) {\n        return typeof val === 'number' && val !== val;\n    };\n\n    var objStack = [];\n    function checkForCircular(obj) {\n        for (var i = 0; i < objStack.length; i++) {\n            if (objStack[i] === obj) {\n                throw new TypeError(\"Converting circular structure to JSON\");\n            }\n        }\n    }\n\n    function makeIndent(str, num, noNewLine) {\n        if (!str) {\n            return \"\";\n        }\n        // indentation no more than 10 chars\n        if (str.length > 10) {\n            str = str.substring(0, 10);\n        }\n\n        var indent = noNewLine ? \"\" : \"\\n\";\n        for (var i = 0; i < num; i++) {\n            indent += str;\n        }\n\n        return indent;\n    }\n\n    var indentStr;\n    if (space) {\n        if (typeof space === \"string\") {\n            indentStr = space;\n        } else if (typeof space === \"number\" && space >= 0) {\n            indentStr = makeIndent(\" \", space, true);\n        } else {\n            // ignore space parameter\n        }\n    }\n\n    // Copied from Crokford's implementation of JSON\n    // See https://github.com/douglascrockford/JSON-js/blob/e39db4b7e6249f04a195e7dd0840e610cc9e941e/json2.js#L195\n    // Begin\n    var cx = /[\\u0000\\u00ad\\u0600-\\u0604\\u070f\\u17b4\\u17b5\\u200c-\\u200f\\u2028-\\u202f\\u2060-\\u206f\\ufeff\\ufff0-\\uffff]/g,\n        escapable = /[\\\\\\\"\\x00-\\x1f\\x7f-\\x9f\\u00ad\\u0600-\\u0604\\u070f\\u17b4\\u17b5\\u200c-\\u200f\\u2028-\\u202f\\u2060-\\u206f\\ufeff\\ufff0-\\uffff]/g,\n        meta = { // table of character substitutions\n        '\\b': '\\\\b',\n        '\\t': '\\\\t',\n        '\\n': '\\\\n',\n        '\\f': '\\\\f',\n        '\\r': '\\\\r',\n        '\"' : '\\\\\"',\n        '\\\\': '\\\\\\\\'\n    };\n    function escapeString(string) {\n\n// If the string contains no control characters, no quote characters, and no\n// backslash characters, then we can safely slap some quotes around it.\n// Otherwise we must also replace the offending characters with safe escape\n// sequences.\n        escapable.lastIndex = 0;\n        return escapable.test(string) ? '\"' + string.replace(escapable, function (a) {\n            var c = meta[a];\n            return typeof c === 'string' ?\n                c :\n                '\\\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);\n        }) + '\"' : '\"' + string + '\"';\n    }\n    // End\n\n    function internalStringify(holder, key, isTopLevel) {\n        var buffer, res;\n\n        // Replace the value, if necessary\n        var obj_part = getReplacedValueOrUndefined(holder, key, isTopLevel);\n\n        if (obj_part && !isDate(obj_part)) {\n            // unbox objects\n            // don't unbox dates, since will turn it into number\n            obj_part = obj_part.valueOf();\n        }\n        switch(typeof obj_part) {\n            case \"boolean\":\n                return obj_part.toString();\n\n            case \"number\":\n                if (isNaN(obj_part) || !isFinite(obj_part)) {\n                    return \"null\";\n                }\n                return obj_part.toString();\n\n            case \"string\":\n                return escapeString(obj_part.toString());\n\n            case \"object\":\n                if (obj_part === null) {\n                    return \"null\";\n                } else if (isArray(obj_part)) {\n                    checkForCircular(obj_part);\n                    buffer = \"[\";\n                    objStack.push(obj_part);\n\n                    for (var i = 0; i < obj_part.length; i++) {\n                        res = internalStringify(obj_part, i, false);\n                        buffer += makeIndent(indentStr, objStack.length);\n                        if (res === null || typeof res === \"undefined\") {\n                            buffer += \"null\";\n                        } else {\n                            buffer += res;\n                        }\n                        if (i < obj_part.length-1) {\n                            buffer += \",\";\n                        } else if (indentStr) {\n                            buffer += \"\\n\";\n                        }\n                    }\n                    objStack.pop();\n                    buffer += makeIndent(indentStr, objStack.length, true) + \"]\";\n                } else {\n                    checkForCircular(obj_part);\n                    buffer = \"{\";\n                    var nonEmpty = false;\n                    objStack.push(obj_part);\n                    for (var prop in obj_part) {\n                        if (obj_part.hasOwnProperty(prop)) {\n                            var value = internalStringify(obj_part, prop, false);\n                            isTopLevel = false;\n                            if (typeof value !== \"undefined\" && value !== null) {\n                                buffer += makeIndent(indentStr, objStack.length);\n                                nonEmpty = true;\n                                var key = isWord(prop) ? prop : escapeString(prop);\n                                buffer += key + \":\" + (indentStr ? ' ' : '') + value + \",\";\n                            }\n                        }\n                    }\n                    objStack.pop();\n                    if (nonEmpty) {\n                        buffer = buffer.substring(0, buffer.length-1) + makeIndent(indentStr, objStack.length) + \"}\";\n                    } else {\n                        buffer = '{}';\n                    }\n                }\n                return buffer;\n            default:\n                // functions and undefined should be ignored\n                return undefined;\n        }\n    }\n\n    // special case...when undefined is used inside of\n    // a compound object/array, return null.\n    // but when top-level, return undefined\n    var topLevelHolder = {\"\":obj};\n    if (obj === undefined) {\n        return getReplacedValueOrUndefined(topLevelHolder, '', true);\n    }\n    return internalStringify(topLevelHolder, '', true);\n};\n"
  },
  {
    "path": "lib/tools/open.js",
    "content": "var exec = require('child_process').exec\n  , path = require('path')\n  ;\n\n\n/**\n * open a file or uri using the default application for the file type.\n *\n * @return {ChildProcess} - the child process object.\n * @param {string} target - the file/uri to open.\n * @param {string} appName - (optional) the application to be used to open the\n *      file (for example, \"chrome\", \"firefox\")\n * @param {function(Error)} callback - called with null on success, or\n *      an error object that contains a property 'code' with the exit\n *      code of the process.\n */\n\nmodule.exports = open;\n\nfunction open(target, appName, callback) {\n  var opener;\n\n  if (typeof(appName) === 'function') {\n    callback = appName;\n    appName = null;\n  }\n\n  switch (process.platform) {\n  case 'darwin':\n    if (appName) {\n      opener = 'open -a \"' + escape(appName) + '\"';\n    } else {\n      opener = 'open';\n    }\n    break;\n  case 'win32':\n    // if the first parameter to start is quoted, it uses that as the title\n    // so we pass a blank title so we can quote the file we are opening\n    if (appName) {\n      opener = 'start \"\" \"' + escape(appName) + '\"';\n    } else {\n      opener = 'start \"\"';\n    }\n    break;\n  default:\n    if (appName) {\n      opener = escape(appName);\n    } else {\n      // use Portlands xdg-open everywhere else\n      opener = path.join(__dirname, './xdg-open');\n    }\n    break;\n  }\n\n  if (process.env.SUDO_USER) {\n    opener = 'sudo -u ' + process.env.SUDO_USER + ' ' + opener;\n  }\n  return exec(opener + ' \"' + escape(target) + '\"', callback);\n}\n\nfunction escape(s) {\n  return s.replace(/\"/g, '\\\\\\\"');\n}\n"
  },
  {
    "path": "lib/tools/passwd.js",
    "content": "\nvar fs = require('fs')\n\nvar getUsers = function() {\n  return fs.readFileSync('/etc/passwd')\n    .toString()\n    .split('\\n')\n    .filter(function (user) {\n      return user.length && user[0] != '#';\n    })\n    .reduce(function(map, user) {\n      var fields = user.split(':');\n\n      map[fields[0]] = map[fields[2]] = {\n        username : fields[0],\n        password : fields[1],\n        userId : fields[2],\n        groupId : fields[3],\n        name : fields[4].split(',')[0],\n        homedir : fields[5],\n        shell : fields[6]\n      };\n\n      return map\n    }, {})\n}\n\nvar getGroups = function(cb) {\n  var groups\n\n  try {\n    groups = fs.readFileSync('/etc/group')\n  } catch(e) {\n    return e\n  }\n\n  return groups\n    .toString()\n    .split('\\n')\n    .filter(function (group) {\n      return group.length && group[0] != '#';\n    })\n    .reduce(function(map, group) {\n      var fields = group.split(':');\n      map[fields[0]] = map[fields[2]] = {\n        name : fields[0],\n        password : fields[1],\n        id : fields[2],\n        members : fields[3].split(',')\n      };\n      return map;\n    }, {})\n}\n\nmodule.exports = {\n  getUsers,\n  getGroups\n}\n"
  },
  {
    "path": "lib/tools/sexec.js",
    "content": "\nvar path = require('path');\nvar fs = require('fs');\nvar child = require('child_process');\n\nvar DEFAULT_MAXBUFFER_SIZE = 20 * 1024 * 1024;\n\nfunction _exec(command, options, callback) {\n  options = options || {};\n\n  if (typeof options === 'function') {\n    callback = options;\n  }\n\n  if (typeof options === 'object' && typeof callback === 'function') {\n    options.async = true;\n  }\n\n  if (!command) {\n    try {\n      console.error('[sexec] must specify command');\n    } catch (e) {\n      return;\n    }\n  }\n\n  options = Object.assign({\n    silent: false,\n    cwd: path.resolve(process.cwd()).toString(),\n    env: process.env,\n    maxBuffer: DEFAULT_MAXBUFFER_SIZE,\n    encoding: 'utf8',\n  }, options);\n\n  var c = child.exec(command, options, function (err, stdout, stderr) {\n    if (callback) {\n      if (!err) {\n        callback(0, stdout, stderr);\n      } else if (err.code === undefined) {\n        // See issue #536\n        /* istanbul ignore next */\n        callback(1, stdout, stderr);\n      } else {\n        callback(err.code, stdout, stderr);\n      }\n    }\n  });\n\n  if (!options.silent) {\n    c.stdout.pipe(process.stdout);\n    c.stderr.pipe(process.stderr);\n  }\n}\n\nmodule.exports = _exec;\n"
  },
  {
    "path": "lib/tools/treeify.js",
    "content": "//     treeify.js\n//     Luke Plaster <notatestuser@gmail.com>\n//     https://github.com/notatestuser/treeify.js\n\n// do the universal module definition dance\n(function (root, factory) {\n\n  if (typeof exports === 'object') {\n    module.exports = factory();\n  } else if (typeof define === 'function' && define.amd) {\n    define(factory);\n  } else {\n    root.treeify = factory();\n  }\n\n}(this, function() {\n\n  function makePrefix(key, last) {\n    var str = (last ? '└' : '├');\n    if (key) {\n      str += '─ ';\n    } else {\n      str += '──┐';\n    }\n    return str;\n  }\n\n  function filterKeys(obj, hideFunctions) {\n    var keys = [];\n    for (var branch in obj) {\n      // always exclude anything in the object's prototype\n      if (!obj.hasOwnProperty(branch)) {\n       continue;\n      }\n      // ... and hide any keys mapped to functions if we've been told to\n      if (hideFunctions && ((typeof obj[branch])===\"function\")) {\n        continue;\n      }\n      keys.push(branch);\n    }\n    return keys;\n  }\n\n  function growBranch(key, root, last, lastStates, showValues, hideFunctions, callback) {\n    var line = '', index = 0, lastKey, circular, lastStatesCopy = lastStates.slice(0);\n\n    if (lastStatesCopy.push([ root, last ]) && lastStates.length > 0) {\n      // based on the \"was last element\" states of whatever we're nested within,\n      // we need to append either blankness or a branch to our line\n      lastStates.forEach(function(lastState, idx) {\n        if (idx > 0) {\n          line += (lastState[1] ? ' ' : '│') + '  ';\n        }\n        if ( ! circular && lastState[0] === root) {\n          circular = true;\n        }\n      });\n\n      // the prefix varies based on whether the key contains something to show and\n      // whether we're dealing with the last element in this collection\n      line += makePrefix(key, last) + key;\n\n      // append values and the circular reference indicator\n      showValues && (typeof root !== 'object' || root instanceof Date) && (line += ': ' + root);\n      circular && (line += ' (circular ref.)');\n\n      callback(line);\n    }\n\n    // can we descend into the next item?\n    if ( ! circular && typeof root === 'object') {\n      var keys = filterKeys(root, hideFunctions);\n      keys.forEach(function(branch){\n        // the last key is always printed with a different prefix, so we'll need to know if we have it\n        lastKey = ++index === keys.length;\n\n        // hold your breath for recursive action\n        growBranch(branch, root[branch], lastKey, lastStatesCopy, showValues, hideFunctions, callback);\n      });\n    }\n  };\n\n  // --------------------\n\n  var Treeify = {};\n\n  // Treeify.asLines\n  // --------------------\n  // Outputs the tree line-by-line, calling the lineCallback when each one is available.\n\n  Treeify.asLines = function(obj, showValues, hideFunctions, lineCallback) {\n    /* hideFunctions and lineCallback are curried, which means we don't break apps using the older form */\n    var hideFunctionsArg = typeof hideFunctions !== 'function' ? hideFunctions : false;\n    growBranch('.', obj, false, [], showValues, hideFunctionsArg, lineCallback || hideFunctions);\n  };\n\n  // Treeify.asTree\n  // --------------------\n  // Outputs the entire tree, returning it as a string with line breaks.\n\n  Treeify.asTree = function(obj, showValues, hideFunctions) {\n    var tree = '';\n    growBranch('.', obj, false, [], showValues, hideFunctions, function(line) {\n      tree += line + '\\n';\n    });\n    return tree;\n  };\n\n  // --------------------\n\n  return Treeify;\n\n}));\n"
  },
  {
    "path": "lib/tools/which.js",
    "content": "\nvar fs = require('fs');\nvar path = require('path');\nvar cst = require('../../constants.js')\n\n// XP's system default value for `PATHEXT` system variable, just in case it's not\n// set on Windows.\nvar XP_DEFAULT_PATHEXT = '.com;.exe;.bat;.cmd;.vbs;.vbe;.js;.jse;.wsf;.wsh';\n\n// For earlier versions of NodeJS that doesn't have a list of constants (< v6)\nvar FILE_EXECUTABLE_MODE = 1;\n\nfunction statFollowLinks() {\n  return fs.statSync.apply(fs, arguments);\n}\n\nfunction isWindowsPlatform() {\n  return cst.IS_WINDOWS;\n}\n\n// Cross-platform method for splitting environment `PATH` variables\nfunction splitPath(p) {\n  return p ? p.split(path.delimiter) : [];\n}\n\n// Tests are running all cases for this func but it stays uncovered by codecov due to unknown reason\n/* istanbul ignore next */\nfunction isExecutable(pathName) {\n  try {\n    // TODO(node-support): replace with fs.constants.X_OK once remove support for node < v6\n    fs.accessSync(pathName, FILE_EXECUTABLE_MODE);\n  } catch (err) {\n    return false;\n  }\n  return true;\n}\n\nfunction checkPath(pathName) {\n  return fs.existsSync(pathName) && !statFollowLinks(pathName).isDirectory()\n    && (isWindowsPlatform() || isExecutable(pathName));\n}\n\n//@\n//@ ### which(command)\n//@\n//@ Examples:\n//@\n//@ ```javascript\n//@ var nodeExec = which('node');\n//@ ```\n//@\n//@ Searches for `command` in the system's `PATH`. On Windows, this uses the\n//@ `PATHEXT` variable to append the extension if it's not already executable.\n//@ Returns a [ShellString](#shellstringstr) containing the absolute path to\n//@ `command`.\nfunction _which(cmd) {\n  if (!cmd) console.error('must specify command');\n\n  var options = {}\n\n  var isWindows = isWindowsPlatform();\n  var pathArray = splitPath(process.env.PATH);\n\n  var queryMatches = [];\n\n  // No relative/absolute paths provided?\n  if (cmd.indexOf('/') === -1) {\n    // Assume that there are no extensions to append to queries (this is the\n    // case for unix)\n    var pathExtArray = [''];\n    if (isWindows) {\n      // In case the PATHEXT variable is somehow not set (e.g.\n      // child_process.spawn with an empty environment), use the XP default.\n      var pathExtEnv = process.env.PATHEXT || XP_DEFAULT_PATHEXT;\n      pathExtArray = splitPath(pathExtEnv.toUpperCase());\n    }\n\n    // Search for command in PATH\n    for (var k = 0; k < pathArray.length; k++) {\n      // already found it\n      if (queryMatches.length > 0 && !options.all) break;\n\n      var attempt = path.resolve(pathArray[k], cmd);\n\n      if (isWindows) {\n        attempt = attempt.toUpperCase();\n      }\n\n      var match = attempt.match(/\\.[^<>:\"/|?*.]+$/);\n      if (match && pathExtArray.indexOf(match[0]) >= 0) { // this is Windows-only\n        // The user typed a query with the file extension, like\n        // `which('node.exe')`\n        if (checkPath(attempt)) {\n          queryMatches.push(attempt);\n          break;\n        }\n      } else { // All-platforms\n        // Cycle through the PATHEXT array, and check each extension\n        // Note: the array is always [''] on Unix\n        for (var i = 0; i < pathExtArray.length; i++) {\n          var ext = pathExtArray[i];\n          var newAttempt = attempt + ext;\n          if (checkPath(newAttempt)) {\n            queryMatches.push(newAttempt);\n            break;\n          }\n        }\n      }\n    }\n  } else if (checkPath(cmd)) { // a valid absolute or relative path\n    queryMatches.push(path.resolve(cmd));\n  }\n\n  if (queryMatches.length > 0) {\n    return options.all ? queryMatches : queryMatches[0];\n  }\n  return options.all ? [] : null;\n}\n\nmodule.exports = _which;\n"
  },
  {
    "path": "lib/tools/xdg-open",
    "content": "#!/bin/sh\n#---------------------------------------------\n#   xdg-open\n#\n#   Utility script to open a URL in the registered default application.\n#\n#   Refer to the usage() function below for usage.\n#\n#   Copyright 2009-2010, Fathi Boudra <fabo@freedesktop.org>\n#   Copyright 2009-2010, Rex Dieter <rdieter@fedoraproject.org>\n#   Copyright 2006, Kevin Krammer <kevin.krammer@gmx.at>\n#   Copyright 2006, Jeremy White <jwhite@codeweavers.com>\n#\n#   LICENSE:\n#\n#   Permission is hereby granted, free of charge, to any person obtaining a\n#   copy of this software and associated documentation files (the \"Software\"),\n#   to deal in the Software without restriction, including without limitation\n#   the rights to use, copy, modify, merge, publish, distribute, sublicense,\n#   and/or sell copies of the Software, and to permit persons to whom the\n#   Software is furnished to do so, subject to the following conditions:\n#\n#   The above copyright notice and this permission notice shall be included\n#   in all copies or substantial portions of the Software.\n#\n#   THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\n#   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n#   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n#   THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR\n#   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,\n#   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\n#   OTHER DEALINGS IN THE SOFTWARE.\n#\n#---------------------------------------------\n\nmanualpage()\n{\ncat << _MANUALPAGE\nName\n\n   xdg-open - opens a file or URL in the user's preferred\n   application\n\nSynopsis\n\n   xdg-open { file | URL }\n\n   xdg-open { --help | --manual | --version }\n\nDescription\n\n   xdg-open opens a file or URL in the user's preferred\n   application. If a URL is provided the URL will be opened in the\n   user's preferred web browser. If a file is provided the file\n   will be opened in the preferred application for files of that\n   type. xdg-open supports file, ftp, http and https URLs.\n\n   xdg-open is for use inside a desktop session only. It is not\n   recommended to use xdg-open as root.\n\nOptions\n\n   --help\n          Show command synopsis.\n\n   --manual\n          Show this manual page.\n\n   --version\n          Show the xdg-utils version information.\n\nExit Codes\n\n   An exit code of 0 indicates success while a non-zero exit code\n   indicates failure. The following failure codes can be returned:\n\n   1\n          Error in command line syntax.\n\n   2\n          One of the files passed on the command line did not\n          exist.\n\n   3\n          A required tool could not be found.\n\n   4\n          The action failed.\n\nExamples\n\nxdg-open 'http://www.freedesktop.org/'\n\n   Opens the freedesktop.org website in the user's default\n   browser.\n\nxdg-open /tmp/foobar.png\n\n   Opens the PNG image file /tmp/foobar.png in the user's default\n   image viewing application.\n_MANUALPAGE\n}\n\nusage()\n{\ncat << _USAGE\n   xdg-open - opens a file or URL in the user's preferred\n   application\n\nSynopsis\n\n   xdg-open { file | URL }\n\n   xdg-open { --help | --manual | --version }\n\n_USAGE\n}\n\n#@xdg-utils-common@\n\n#----------------------------------------------------------------------------\n#   Common utility functions included in all XDG wrapper scripts\n#----------------------------------------------------------------------------\n\nDEBUG()\n{\n  [ -z \"${XDG_UTILS_DEBUG_LEVEL}\" ] && return 0;\n  [ ${XDG_UTILS_DEBUG_LEVEL} -lt $1 ] && return 0;\n  shift\n  echo \"$@\" >&2\n}\n\n# This handles backslashes but not quote marks.\nfirst_word()\n{\n    read first rest\n    echo \"$first\"\n}\n\n#-------------------------------------------------------------\n# map a binary to a .desktop file\nbinary_to_desktop_file()\n{\n    search=\"${XDG_DATA_HOME:-$HOME/.local/share}:${XDG_DATA_DIRS:-/usr/local/share:/usr/share}\"\n    binary=\"`which \"$1\"`\"\n    binary=\"`readlink -f \"$binary\"`\"\n    base=\"`basename \"$binary\"`\"\n    IFS=:\n    for dir in $search; do\n        unset IFS\n        [ \"$dir\" ] || continue\n        [ -d \"$dir/applications\" ] || [ -d \"$dir/applnk\" ] || continue\n        for file in \"$dir\"/applications/*.desktop \"$dir\"/applications/*/*.desktop \"$dir\"/applnk/*.desktop \"$dir\"/applnk/*/*.desktop; do\n            [ -r \"$file\" ] || continue\n            # Check to make sure it's worth the processing.\n            grep -q \"^Exec.*$base\" \"$file\" || continue\n            # Make sure it's a visible desktop file (e.g. not \"preferred-web-browser.desktop\").\n            grep -Eq \"^(NoDisplay|Hidden)=true\" \"$file\" && continue\n            command=\"`grep -E \"^Exec(\\[[^]=]*])?=\" \"$file\" | cut -d= -f 2- | first_word`\"\n            command=\"`which \"$command\"`\"\n            if [ x\"`readlink -f \"$command\"`\" = x\"$binary\" ]; then\n                # Fix any double slashes that got added path composition\n                echo \"$file\" | sed -e 's,//*,/,g'\n                return\n            fi\n        done\n    done\n}\n\n#-------------------------------------------------------------\n# map a .desktop file to a binary\n## FIXME: handle vendor dir case\ndesktop_file_to_binary()\n{\n    search=\"${XDG_DATA_HOME:-$HOME/.local/share}:${XDG_DATA_DIRS:-/usr/local/share:/usr/share}\"\n    desktop=\"`basename \"$1\"`\"\n    IFS=:\n    for dir in $search; do\n        unset IFS\n        [ \"$dir\" ] && [ -d \"$dir/applications\" ] || continue\n        file=\"$dir/applications/$desktop\"\n        [ -r \"$file\" ] || continue\n        # Remove any arguments (%F, %f, %U, %u, etc.).\n        command=\"`grep -E \"^Exec(\\[[^]=]*])?=\" \"$file\" | cut -d= -f 2- | first_word`\"\n        command=\"`which \"$command\"`\"\n        readlink -f \"$command\"\n        return\n    done\n}\n\n#-------------------------------------------------------------\n# Exit script on successfully completing the desired operation\n\nexit_success()\n{\n    if [ $# -gt 0 ]; then\n        echo \"$@\"\n        echo\n    fi\n\n    exit 0\n}\n\n\n#-----------------------------------------\n# Exit script on malformed arguments, not enough arguments\n# or missing required option.\n# prints usage information\n\nexit_failure_syntax()\n{\n    if [ $# -gt 0 ]; then\n        echo \"xdg-open: $@\" >&2\n        echo \"Try 'xdg-open --help' for more information.\" >&2\n    else\n        usage\n        echo \"Use 'man xdg-open' or 'xdg-open --manual' for additional info.\"\n    fi\n\n    exit 1\n}\n\n#-------------------------------------------------------------\n# Exit script on missing file specified on command line\n\nexit_failure_file_missing()\n{\n    if [ $# -gt 0 ]; then\n        echo \"xdg-open: $@\" >&2\n    fi\n\n    exit 2\n}\n\n#-------------------------------------------------------------\n# Exit script on failure to locate necessary tool applications\n\nexit_failure_operation_impossible()\n{\n    if [ $# -gt 0 ]; then\n        echo \"xdg-open: $@\" >&2\n    fi\n\n    exit 3\n}\n\n#-------------------------------------------------------------\n# Exit script on failure returned by a tool application\n\nexit_failure_operation_failed()\n{\n    if [ $# -gt 0 ]; then\n        echo \"xdg-open: $@\" >&2\n    fi\n\n    exit 4\n}\n\n#------------------------------------------------------------\n# Exit script on insufficient permission to read a specified file\n\nexit_failure_file_permission_read()\n{\n    if [ $# -gt 0 ]; then\n        echo \"xdg-open: $@\" >&2\n    fi\n\n    exit 5\n}\n\n#------------------------------------------------------------\n# Exit script on insufficient permission to write a specified file\n\nexit_failure_file_permission_write()\n{\n    if [ $# -gt 0 ]; then\n        echo \"xdg-open: $@\" >&2\n    fi\n\n    exit 6\n}\n\ncheck_input_file()\n{\n    if [ ! -e \"$1\" ]; then\n        exit_failure_file_missing \"file '$1' does not exist\"\n    fi\n    if [ ! -r \"$1\" ]; then\n        exit_failure_file_permission_read \"no permission to read file '$1'\"\n    fi\n}\n\ncheck_vendor_prefix()\n{\n    file_label=\"$2\"\n    [ -n \"$file_label\" ] || file_label=\"filename\"\n    file=`basename \"$1\"`\n    case \"$file\" in\n       [[:alpha:]]*-*)\n         return\n         ;;\n    esac\n\n    echo \"xdg-open: $file_label '$file' does not have a proper vendor prefix\" >&2\n    echo 'A vendor prefix consists of alpha characters ([a-zA-Z]) and is terminated' >&2\n    echo 'with a dash (\"-\"). An example '\"$file_label\"' is '\"'example-$file'\" >&2\n    echo \"Use --novendor to override or 'xdg-open --manual' for additional info.\" >&2\n    exit 1\n}\n\ncheck_output_file()\n{\n    # if the file exists, check if it is writeable\n    # if it does not exist, check if we are allowed to write on the directory\n    if [ -e \"$1\" ]; then\n        if [ ! -w \"$1\" ]; then\n            exit_failure_file_permission_write \"no permission to write to file '$1'\"\n        fi\n    else\n        DIR=`dirname \"$1\"`\n        if [ ! -w \"$DIR\" ] || [ ! -x \"$DIR\" ]; then\n            exit_failure_file_permission_write \"no permission to create file '$1'\"\n        fi\n    fi\n}\n\n#----------------------------------------\n# Checks for shared commands, e.g. --help\n\ncheck_common_commands()\n{\n    while [ $# -gt 0 ] ; do\n        parm=\"$1\"\n        shift\n\n        case \"$parm\" in\n            --help)\n            usage\n            echo \"Use 'man xdg-open' or 'xdg-open --manual' for additional info.\"\n            exit_success\n            ;;\n\n            --manual)\n            manualpage\n            exit_success\n            ;;\n\n            --version)\n            echo \"xdg-open 1.1.0 rc3\"\n            exit_success\n            ;;\n        esac\n    done\n}\n\ncheck_common_commands \"$@\"\n\n[ -z \"${XDG_UTILS_DEBUG_LEVEL}\" ] && unset XDG_UTILS_DEBUG_LEVEL;\nif [ ${XDG_UTILS_DEBUG_LEVEL-0} -lt 1 ]; then\n    # Be silent\n    xdg_redirect_output=\" > /dev/null 2> /dev/null\"\nelse\n    # All output to stderr\n    xdg_redirect_output=\" >&2\"\nfi\n\n#--------------------------------------\n# Checks for known desktop environments\n# set variable DE to the desktop environments name, lowercase\n\ndetectDE()\n{\n    # see https://bugs.freedesktop.org/show_bug.cgi?id=34164\n    unset GREP_OPTIONS\n\n    if [ -n \"${XDG_CURRENT_DESKTOP}\" ]; then\n      case \"${XDG_CURRENT_DESKTOP}\" in\n         ENLIGHTENMENT)\n           DE=enlightenment;\n           ;;\n         GNOME)\n           DE=gnome;\n           ;;\n         KDE)\n           DE=kde;\n           ;;\n         LXDE)\n           DE=lxde;\n           ;;\n         MATE)\n           DE=mate;\n           ;;\n         XFCE)\n           DE=xfce\n           ;;\n      esac\n    fi\n\n    if [ x\"$DE\" = x\"\" ]; then\n      # classic fallbacks\n      if [ x\"$KDE_FULL_SESSION\" != x\"\" ]; then DE=kde;\n      elif [ x\"$GNOME_DESKTOP_SESSION_ID\" != x\"\" ]; then DE=gnome;\n      elif [ x\"$MATE_DESKTOP_SESSION_ID\" != x\"\" ]; then DE=mate;\n      elif `dbus-send --print-reply --dest=org.freedesktop.DBus /org/freedesktop/DBus org.freedesktop.DBus.GetNameOwner string:org.gnome.SessionManager > /dev/null 2>&1` ; then DE=gnome;\n      elif xprop -root _DT_SAVE_MODE 2> /dev/null | grep ' = \\\"xfce4\\\"$' >/dev/null 2>&1; then DE=xfce;\n      elif xprop -root 2> /dev/null | grep -i '^xfce_desktop_window' >/dev/null 2>&1; then DE=xfce\n      elif echo $DESKTOP | grep -q '^Enlightenment'; then DE=enlightenment;\n      fi\n    fi\n\n    if [ x\"$DE\" = x\"\" ]; then\n      # fallback to checking $DESKTOP_SESSION\n      case \"$DESKTOP_SESSION\" in\n         gnome)\n           DE=gnome;\n           ;;\n         LXDE|Lubuntu)\n           DE=lxde;\n           ;;\n         MATE)\n           DE=mate;\n           ;;\n         xfce|xfce4|'Xfce Session')\n           DE=xfce;\n           ;;\n      esac\n    fi\n\n    if [ x\"$DE\" = x\"\" ]; then\n      # fallback to uname output for other platforms\n      case \"$(uname 2>/dev/null)\" in\n        Darwin)\n          DE=darwin;\n          ;;\n      esac\n    fi\n\n    if [ x\"$DE\" = x\"gnome\" ]; then\n      # gnome-default-applications-properties is only available in GNOME 2.x\n      # but not in GNOME 3.x\n      which gnome-default-applications-properties > /dev/null 2>&1  || DE=\"gnome3\"\n    fi\n}\n\n#----------------------------------------------------------------------------\n# kfmclient exec/openURL can give bogus exit value in KDE <= 3.5.4\n# It also always returns 1 in KDE 3.4 and earlier\n# Simply return 0 in such case\n\nkfmclient_fix_exit_code()\n{\n    version=`LC_ALL=C.UTF-8 kde-config --version 2>/dev/null | grep '^KDE'`\n    major=`echo $version | sed 's/KDE.*: \\([0-9]\\).*/\\1/'`\n    minor=`echo $version | sed 's/KDE.*: [0-9]*\\.\\([0-9]\\).*/\\1/'`\n    release=`echo $version | sed 's/KDE.*: [0-9]*\\.[0-9]*\\.\\([0-9]\\).*/\\1/'`\n    test \"$major\" -gt 3 && return $1\n    test \"$minor\" -gt 5 && return $1\n    test \"$release\" -gt 4 && return $1\n    return 0\n}\n\n# This handles backslashes but not quote marks.\nlast_word()\n{\n    read first rest\n    echo \"$rest\"\n}\n\n# Get the value of a key in a desktop file's Desktop Entry group.\n# Example: Use get_key foo.desktop Exec\n# to get the values of the Exec= key for the Desktop Entry group.\nget_key()\n{\n    local file=\"${1}\"\n    local key=\"${2}\"\n    local desktop_entry=\"\"\n\n    IFS_=\"${IFS}\"\n    IFS=\"\"\n    while read line\n    do\n        case \"$line\" in\n            \"[Desktop Entry]\")\n                desktop_entry=\"y\"\n            ;;\n            # Reset match flag for other groups\n            \"[\"*)\n                desktop_entry=\"\"\n            ;;\n            \"${key}=\"*)\n                # Only match Desktop Entry group\n                if [ -n \"${desktop_entry}\" ]\n                then\n                    echo \"${line}\" | cut -d= -f 2-\n                fi\n        esac\n    done < \"${file}\"\n    IFS=\"${IFS_}\"\n}\n\nopen_darwin()\n{\n    open \"$1\"\n\n    if [ $? -eq 0 ]; then\n        exit_success\n    else\n        exit_failure_operation_failed\n    fi\n}\n\nopen_kde()\n{\n    if [ -n \"${KDE_SESSION_VERSION}\" ]; then\n      case \"${KDE_SESSION_VERSION}\" in\n        4)\n          kde-open \"$1\"\n        ;;\n        5)\n          kde-open${KDE_SESSION_VERSION} \"$1\"\n        ;;\n      esac\n    else\n        kfmclient exec \"$1\"\n        kfmclient_fix_exit_code $?\n    fi\n\n    if [ $? -eq 0 ]; then\n        exit_success\n    else\n        exit_failure_operation_failed\n    fi\n}\n\nopen_gnome()\n{\n    if gvfs-open --help 2>/dev/null 1>&2; then\n        gvfs-open \"$1\"\n    else\n        gnome-open \"$1\"\n    fi\n\n    if [ $? -eq 0 ]; then\n        exit_success\n    else\n        exit_failure_operation_failed\n    fi\n}\n\nopen_mate()\n{\n    if gvfs-open --help 2>/dev/null 1>&2; then\n        gvfs-open \"$1\"\n    else\n        mate-open \"$1\"\n    fi\n\n    if [ $? -eq 0 ]; then\n        exit_success\n    else\n        exit_failure_operation_failed\n    fi\n}\n\nopen_xfce()\n{\n    exo-open \"$1\"\n\n    if [ $? -eq 0 ]; then\n        exit_success\n    else\n        exit_failure_operation_failed\n    fi\n}\n\nopen_enlightenment()\n{\n    enlightenment_open \"$1\"\n\n    if [ $? -eq 0 ]; then\n        exit_success\n    else\n        exit_failure_operation_failed\n    fi\n}\n\n#-----------------------------------------\n# Recursively search .desktop file\n\nsearch_desktop_file()\n{\n    local default=\"$1\"\n    local dir=\"$2\"\n    local target=\"$3\"\n\n    local file=\"\"\n    # look for both vendor-app.desktop, vendor/app.desktop\n    if [ -r \"$dir/$default\" ]; then\n      file=\"$dir/$default\"\n    elif [ -r \"$dir/`echo $default | sed -e 's|-|/|'`\" ]; then\n      file=\"$dir/`echo $default | sed -e 's|-|/|'`\"\n    fi\n\n    if [ -r \"$file\" ] ; then\n        command=\"$(get_key \"${file}\" \"Exec\" | first_word)\"\n        command_exec=`which $command 2>/dev/null`\n        icon=\"$(get_key \"${file}\" \"Icon\")\"\n        # FIXME: Actually LC_MESSAGES should be used as described in\n        # http://standards.freedesktop.org/desktop-entry-spec/latest/ar01s04.html\n        localised_name=\"$(get_key \"${file}\" \"Name\")\"\n        set -- $(get_key \"${file}\" \"Exec\" | last_word)\n        # We need to replace any occurrence of \"%f\", \"%F\" and\n        # the like by the target file. We examine each\n        # argument and append the modified argument to the\n        # end then shift.\n        local args=$#\n        local replaced=0\n        while [ $args -gt 0 ]; do\n            case $1 in\n                %[c])\n                    replaced=1\n                    arg=\"${localised_name}\"\n                    shift\n                    set -- \"$@\" \"$arg\"\n                    ;;\n                %[fFuU])\n                    replaced=1\n                    arg=\"$target\"\n                    shift\n                    set -- \"$@\" \"$arg\"\n                    ;;\n                %[i])\n                    replaced=1\n                    shift\n                    set -- \"$@\" \"--icon\" \"$icon\"\n                    ;;\n                *)\n                    arg=\"$1\"\n                    shift\n                    set -- \"$@\" \"$arg\"\n                    ;;\n            esac\n            args=$(( $args - 1 ))\n        done\n        [ $replaced -eq 1 ] || set -- \"$@\" \"$target\"\n        \"$command_exec\" \"$@\"\n\n        if [ $? -eq 0 ]; then\n            exit_success\n        fi\n    fi\n\n    for d in $dir/*/; do\n        [ -d \"$d\" ] && search_desktop_file \"$default\" \"$d\" \"$target\"\n    done\n}\n\n\nopen_generic_xdg_mime()\n{\n    filetype=\"$2\"\n    default=`xdg-mime query default \"$filetype\"`\n    if [ -n \"$default\" ] ; then\n        xdg_user_dir=\"$XDG_DATA_HOME\"\n        [ -n \"$xdg_user_dir\" ] || xdg_user_dir=\"$HOME/.local/share\"\n\n        xdg_system_dirs=\"$XDG_DATA_DIRS\"\n        [ -n \"$xdg_system_dirs\" ] || xdg_system_dirs=/usr/local/share/:/usr/share/\n\nDEBUG 3 \"$xdg_user_dir:$xdg_system_dirs\"\n        for x in `echo \"$xdg_user_dir:$xdg_system_dirs\" | sed 's/:/ /g'`; do\n            search_desktop_file \"$default\" \"$x/applications/\" \"$1\"\n        done\n    fi\n}\n\nopen_generic_xdg_file_mime()\n{\n    filetype=`xdg-mime query filetype \"$1\" | sed \"s/;.*//\"`\n    open_generic_xdg_mime \"$1\" \"$filetype\"\n}\n\nopen_generic_xdg_x_scheme_handler()\n{\n    scheme=\"`echo $1 | sed -n 's/\\(^[[:alnum:]+\\.-]*\\):.*$/\\1/p'`\"\n    if [ -n $scheme ]; then\n        filetype=\"x-scheme-handler/$scheme\"\n        open_generic_xdg_mime \"$1\" \"$filetype\"\n    fi\n}\n\nopen_generic()\n{\n    # Paths or file:// URLs\n    if (echo \"$1\" | grep -q '^file://' ||\n        ! echo \"$1\" | egrep -q '^[[:alpha:]+\\.\\-]+:'); then\n\n        local file=\"$1\"\n\n        # Decode URLs\n        if echo \"$file\" | grep -q '^file:///'; then\n            file=${file#file://}\n            file=\"$(printf \"$(echo \"$file\" | sed -e 's@%\\([a-f0-9A-F]\\{2\\}\\)@\\\\x\\1@g')\")\"\n        fi\n        file_check=${file%%#*}\n        file_check=${file_check%%\\?*}\n        check_input_file \"$file_check\"\n\n        filetype=`xdg-mime query filetype \"$file_check\" | sed \"s/;.*//\"`\n        open_generic_xdg_mime \"$file\" \"$filetype\"\n\n        if which run-mailcap 2>/dev/null 1>&2; then\n            run-mailcap --action=view \"$file\"\n            if [ $? -eq 0 ]; then\n                exit_success\n            fi\n        fi\n\n        if mimeopen -v 2>/dev/null 1>&2; then\n            mimeopen -L -n \"$file\"\n            if [ $? -eq 0 ]; then\n                exit_success\n            fi\n        fi\n    fi\n\n    open_generic_xdg_x_scheme_handler \"$1\"\n\n    IFS=\":\"\n    for browser in $BROWSER; do\n        if [ x\"$browser\" != x\"\" ]; then\n\n            browser_with_arg=`printf \"$browser\" \"$1\" 2>/dev/null`\n            if [ $? -ne 0 ]; then\n                browser_with_arg=$browser;\n            fi\n\n            if [ x\"$browser_with_arg\" = x\"$browser\" ]; then\n                eval '$browser \"$1\"'$xdg_redirect_output;\n            else eval '$browser_with_arg'$xdg_redirect_output;\n            fi\n\n            if [ $? -eq 0 ]; then\n                exit_success;\n            fi\n        fi\n    done\n\n    exit_failure_operation_impossible \"no method available for opening '$1'\"\n}\n\nopen_lxde()\n{\n    # pcmanfm only knows how to handle file:// urls and filepaths, it seems.\n    if (echo \"$1\" | grep -q '^file://' ||\n        ! echo \"$1\" | egrep -q '^[[:alpha:]+\\.\\-]+:')\n    then\n        local file=\"$1\"\n\n        # handle relative paths\n        if ! echo \"$file\" | egrep -q '^(file://)?/'; then\n            file=\"$(pwd)/$file\"\n        fi\n\n        pcmanfm \"$file\"\n\n    else\n        open_generic \"$1\"\n    fi\n\n    if [ $? -eq 0 ]; then\n        exit_success\n    else\n        exit_failure_operation_failed\n    fi\n}\n\n[ x\"$1\" != x\"\" ] || exit_failure_syntax\n\nurl=\nwhile [ $# -gt 0 ] ; do\n    parm=\"$1\"\n    shift\n\n    case \"$parm\" in\n      -*)\n        exit_failure_syntax \"unexpected option '$parm'\"\n        ;;\n\n      *)\n        if [ -n \"$url\" ] ; then\n            exit_failure_syntax \"unexpected argument '$parm'\"\n        fi\n        url=\"$parm\"\n        ;;\n    esac\ndone\n\nif [ -z \"${url}\" ] ; then\n    exit_failure_syntax \"file or URL argument missing\"\nfi\n\ndetectDE\n\nif [ x\"$DE\" = x\"\" ]; then\n    DE=generic\nfi\n\nDEBUG 2 \"Selected DE $DE\"\n\n# sanitize BROWSER (avoid caling ourselves in particular)\ncase \"${BROWSER}\" in\n    *:\"xdg-open\"|\"xdg-open\":*)\n        BROWSER=$(echo $BROWSER | sed -e 's|:xdg-open||g' -e 's|xdg-open:||g')\n        ;;\n    \"xdg-open\")\n        BROWSER=\n        ;;\nesac\n\n# if BROWSER variable is not set, check some well known browsers instead\nif [ x\"$BROWSER\" = x\"\" ]; then\n    BROWSER=links2:elinks:links:lynx:w3m\n    if [ -n \"$DISPLAY\" ]; then\n        BROWSER=x-www-browser:firefox:seamonkey:mozilla:epiphany:konqueror:chromium-browser:google-chrome:$BROWSER\n    fi\nfi\n\ncase \"$DE\" in\n    kde)\n    open_kde \"$url\"\n    ;;\n\n    gnome*)\n    open_gnome \"$url\"\n    ;;\n\n    mate)\n    open_mate \"$url\"\n    ;;\n\n    xfce)\n    open_xfce \"$url\"\n    ;;\n\n    lxde)\n    open_lxde \"$url\"\n    ;;\n\n    enlightenment)\n    open_enlightenment \"$url\"\n    ;;\n\n    generic)\n    open_generic \"$url\"\n    ;;\n\n    *)\n    exit_failure_operation_impossible \"no method available for opening '$url'\"\n    ;;\nesac\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"pm2\",\n  \"preferGlobal\": true,\n  \"version\": \"6.0.14\",\n  \"engines\": {\n    \"node\": \">=16.0.0\"\n  },\n  \"directories\": {\n    \"bin\": \"./bin\",\n    \"lib\": \"./lib\",\n    \"example\": \"./examples\"\n  },\n  \"author\": {\n    \"name\": \"Strzelewicz Alexandre\",\n    \"email\": \"alexandre@pm2.io\",\n    \"url\": \"https://pm2.io\"\n  },\n  \"maintainers\": [\n    {\n      \"name\": \"Alexandre Strzelewicz\",\n      \"email\": \"alexandre@pm2.io\"\n    },\n    {\n      \"name\": \"Antoine Bluchet\",\n      \"email\": \"antoine@pm2.io\"\n    }\n  ],\n  \"contributors\": [\n    {\n      \"name\": \"Alex Kocharin\",\n      \"email\": \"alex@kocharin.ru\"\n    },\n    {\n      \"name\": \"Antoine Bluchet\",\n      \"email\": \"soyuka@gmail.com\"\n    },\n    {\n      \"name\": \"Subhash Burramsetty\"\n    },\n    {\n      \"name\": \"Valentin Marchaud\",\n      \"email\": \"thisismac47@gmail.com\"\n    },\n    {\n      \"name\": \"Valentin Touffet\",\n      \"email\": \"contact@eywek.fr\"\n    },\n    {\n      \"name\": \"Florian Hermouet-Joscht\",\n      \"email\": \"florian@keymetrics.io\"\n    },\n    {\n      \"name\": \"Vincent Vallet\",\n      \"email\": \"wallet77@gmail.com\"\n    },\n    {\n      \"name\": \"Joni Shkurti\",\n      \"email\": \"jonishkurti90@gmail.com\"\n    },\n    {\n      \"name\": \"Jun Tjatse\",\n      \"email\": \"thisnamemeansnothing@gmail.com\"\n    },\n    {\n      \"name\": \"Xu Jingxin\",\n      \"email\": \"sailxjx@gmail.com\"\n    },\n    {\n      \"name\": \"Ben Postlethwaite\",\n      \"email\": \"post.ben.here@gmail.com\"\n    },\n    {\n      \"name\": \"Devo.ps\",\n      \"email\": \"contact@devo.ps\"\n    },\n    {\n      \"name\": \"Bret Copeland\",\n      \"email\": \"bret@atlantisflight.org\"\n    },\n    {\n      \"name\": \"John Hurliman\",\n      \"email\": \"jhurliman@jhurliman.org\"\n    },\n    {\n      \"name\": \"TruongSinh Tran-Nguyen\",\n      \"email\": \"i@truongsinh.pro\"\n    },\n    {\n      \"name\": \"Michael Hueuberger\",\n      \"email\": \"michael.heuberger@binarykitchen.com\"\n    },\n    {\n      \"name\": \"Chris Wiggins\",\n      \"email\": \"chris@chriswiggins.co.nz\"\n    }\n  ],\n  \"homepage\": \"http://pm2.keymetrics.io/\",\n  \"description\": \"Production process manager for Node.JS applications with a built-in load balancer.\",\n  \"main\": \"index.js\",\n  \"types\": \"types/index.d.ts\",\n  \"scripts\": {\n    \"test:unit\": \"bash test/unit.sh\",\n    \"test:e2e\": \"bash test/e2e.sh\",\n    \"test\": \"bash test/unit.sh && bash test/e2e.sh\"\n  },\n  \"keywords\": [\n    \"cli\",\n    \"fault tolerant\",\n    \"sysadmin\",\n    \"tools\",\n    \"pm2\",\n    \"logs\",\n    \"log\",\n    \"json\",\n    \"express\",\n    \"hapi\",\n    \"kraken\",\n    \"reload\",\n    \"load balancer\",\n    \"lb\",\n    \"load-balancer\",\n    \"kubernetes\",\n    \"k8s\",\n    \"pm2-docker\",\n    \"runtime\",\n    \"source maps\",\n    \"graceful\",\n    \"microservice\",\n    \"programmatic\",\n    \"harmony\",\n    \"node-pm2\",\n    \"production\",\n    \"keymetrics\",\n    \"node.js monitoring\",\n    \"strong-pm\",\n    \"deploy\",\n    \"deployment\",\n    \"daemon\",\n    \"supervisor\",\n    \"supervisord\",\n    \"nodemon\",\n    \"pm2.io\",\n    \"ghost\",\n    \"ghost production\",\n    \"monitoring\",\n    \"keymetrics\",\n    \"process manager\",\n    \"forever\",\n    \"profiling\",\n    \"probes\",\n    \"apm\",\n    \"container\",\n    \"forever-monitor\",\n    \"keep process alive\",\n    \"process configuration\",\n    \"clustering\",\n    \"cluster cli\",\n    \"cluster\",\n    \"docker\",\n    \"cron\",\n    \"devops\",\n    \"dev ops\"\n  ],\n  \"bin\": {\n    \"pm2\": \"bin/pm2\",\n    \"pm2-dev\": \"bin/pm2-dev\",\n    \"pm2-docker\": \"bin/pm2-docker\",\n    \"pm2-runtime\": \"bin/pm2-runtime\"\n  },\n  \"dependencies\": {\n    \"@pm2/agent\": \"~2.1.1\",\n    \"@pm2/js-api\": \"~0.8.0\",\n    \"@pm2/io\": \"~6.1.0\",\n    \"@pm2/pm2-version-check\": \"^1.0.4\",\n    \"ansis\": \"4.0.0-node10\",\n    \"async\": \"3.2.6\",\n    \"@pm2/blessed\": \"0.1.81\",\n    \"chokidar\": \"3.6.0\",\n    \"cli-tableau\": \"2.0.1\",\n    \"commander\": \"2.15.1\",\n    \"croner\": \"4.1.97\",\n    \"dayjs\": \"1.11.15\",\n    \"debug\": \"4.4.3\",\n    \"enquirer\": \"2.3.6\",\n    \"eventemitter2\": \"5.0.1\",\n    \"fclone\": \"1.0.11\",\n    \"mkdirp\": \"1.0.4\",\n    \"needle\": \"2.4.0\",\n    \"pidusage\": \"3.0.2\",\n    \"pm2-axon\": \"~4.0.1\",\n    \"pm2-axon-rpc\": \"~0.7.1\",\n    \"pm2-deploy\": \"~1.0.2\",\n    \"pm2-multimeter\": \"^0.1.2\",\n    \"promptly\": \"2.2.0\",\n    \"semver\": \"7.7.2\",\n    \"source-map-support\": \"0.5.21\",\n    \"sprintf-js\": \"1.1.2\",\n    \"vizion\": \"~2.2.1\",\n    \"js-yaml\": \"4.1.1\"\n  },\n  \"overrides\": {\n    \"debug\": \"4.4.3\"\n  },\n  \"optionalDependencies\": {\n    \"pm2-sysmonit\": \"^1.2.8\"\n  },\n  \"devDependencies\": {\n    \"mocha\": \"^11.7.0\",\n    \"should\": \"^13.2.3\"\n  },\n  \"bugs\": {\n    \"url\": \"https://github.com/Unitech/pm2/issues\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git://github.com/Unitech/pm2.git\"\n  },\n  \"license\": \"AGPL-3.0\"\n}\n"
  },
  {
    "path": "packager/alpine/pm2/APKBUILD",
    "content": "# Contributor: Paul LESELLIER <paul@keymetrics.io>\n# Maintainer: Paul LESELLIER <paul@keymetrics.io>\npkgname=pm2\npkgver=master\npkgrel=0\npkgdesc=\"PM2 CE: Production Process Manager for Node.js apps with a built-in Load Balancer.\"\nurl=\"http://pm2.io\"\narch=\"noarch\"\nlicense=\"GNU-AGPL-3.0\"\ndepends=\"nodejs\"\nmakedepends=\"make nodejs-npm\"\ninstall=\"\" # \"$pkgname.pre-install $pkgname.post-install\"\nsubpackages=\"\" # \"$pkgname-dev $pkgname-doc\"\nsource=\"\n\thttps://github.com/Unitech/pm2/archive/$pkgver.zip\n\t\"\nbuilddir=\"$srcdir/\"\n\nbuild() {\n\tcd \"$builddir\"\n\tcd pm2-$pkgver\n\tnpm install --production\n}\n\npackage() {\n\tcd \"$builddir\"\n\n\techo $pkgdir\n\n\tcd pm2-$pkgver\n\n\tfor filename in constants.js paths.js index.js package.json\n\tdo\n\t\techo \"  [+] Installing: $filename to /usr/share/pm2/$filename\"\n\t\tinstall -m 644 -D $filename $pkgdir/usr/share/pm2/$filename\n\tdone\n\n\tfor dirname in bin lib node_modules\n\tdo\n\t\techo \"[~] Processing directory $dirname\"\n\n\t\tCHMOD_VAL=644\n\t\tif [ \"$dirname\" == \"bin\" ]; then\n\t\t   CHMOD_VAL=755\n\t\tfi\n\n\t\tfor filename in $(find $dirname -type f)\n\t\tdo\n\t\t\techo \"  [+] Installing: $filename to /usr/share/pm2/$filename\"\n\t\t\tinstall -m $CHMOD_VAL -D $filename $pkgdir/usr/share/pm2/$filename\n\t\tdone\n\tdone\n\n\techo \"[!] Linking pm2 binary as /usr/bin/pm2\"\n\tmkdir $pkgdir/usr/bin/\n\tcd $pkgdir/usr/bin/\n\tln -s ../share/pm2/bin/pm2 pm2\n}\n\nsha512sums=\"f38040c3df19d610292fa9c28cab818e2d00360332e8bce627f6886e03601d52070084e57b4a4bbee52ca9bf960693a44eea28d28448767bc51d92fbee75757c  2.7.2.zip\"\n"
  },
  {
    "path": "packager/alpine/pm2_io.rsa.pub",
    "content": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAugt3OP2U2VjgY1VvlxyC\nqP33UT96MMulFzjthMJD57VU4yBzjoboIDjc1AIVjEiCXDktxNezFiqG/MHMh/nl\nDVB/zvNI8CpNte250+iwSwADZZGh+Fy9V1vFARVOXZhHB7V/QXsuHoT0QXTyY4Ss\n9RiS5AMHJzK1CRAZrfivAiGN7j6+G6dz2vnDIK6Q3OVpS5ssV3+44Hx7itBKXCls\ncvhjI4GGcZGf3K26oHfNwh1WjVT4L0Yjox5B8kN4tzif9fhKSCinnynfUZbyeSBA\n4gU1cE4twQneXdc1NtPPrSOHz5THNmmQXqNNQ7sEv88LBRutcnph7IuuHEes1J/Q\nAwIDAQAB\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "packager/build-deb-rpm.sh",
    "content": "#!/bin/bash\n\nset -ex\n\n# Ensure all the tools we need are available\nensureAvailable() {\n  eval $1 --version >/dev/null || (echo \"You need to install $1\" && exit 2)\n}\nensureAvailable dpkg-deb\nensureAvailable fpm\nensureAvailable fakeroot\nensureAvailable lintian\nensureAvailable rpmbuild\n\nPACKAGE_TMPDIR=tmp/debian_pkg\necho \"Cleaning PACKAGE_TMPDIR...\"\nrm -rf $PACKAGE_TMPDIR\n\nPM2_VERSION=`node dist/bin/pm2 --version`\nVERSION=$PM2_VERSION\nTARBALL_NAME=dist/pm2-v$PM2_VERSION.tar.gz\nOUTPUT_DIR=artifacts\n\nif [ ! -e $TARBALL_NAME ]; then\n  echo \"Hey! Listen! You need to run build-dist.sh first.\"\n  exit 1\nfi;\n\nmkdir -p $OUTPUT_DIR\n# Remove old packages\nrm -f dist/*.deb $OUTPUT_DIR/*.deb $OUTPUT_DIR/*.rpm\n\n# Extract to a temporary directory\nrm -rf $PACKAGE_TMPDIR\nmkdir -p $PACKAGE_TMPDIR/\ntar zxf $TARBALL_NAME -C $PACKAGE_TMPDIR/\n\n# Create Linux package structure\nmkdir -p $PACKAGE_TMPDIR/usr/share/pm2/\nmkdir -p $PACKAGE_TMPDIR/usr/share/doc/pm2/\nmv $PACKAGE_TMPDIR/dist/bin $PACKAGE_TMPDIR/usr/share/pm2/\nmv $PACKAGE_TMPDIR/dist/lib $PACKAGE_TMPDIR/usr/share/pm2/\nmv $PACKAGE_TMPDIR/dist/constants.js $PACKAGE_TMPDIR/usr/share/pm2/\nmv $PACKAGE_TMPDIR/dist/paths.js $PACKAGE_TMPDIR/usr/share/pm2/\nmv $PACKAGE_TMPDIR/dist/index.js $PACKAGE_TMPDIR/usr/share/pm2/\nmv $PACKAGE_TMPDIR/dist/node_modules $PACKAGE_TMPDIR/usr/share/pm2/\nmv $PACKAGE_TMPDIR/dist/package.json $PACKAGE_TMPDIR/usr/share/pm2/\ncp packager/debian/copyright $PACKAGE_TMPDIR/usr/share/doc/pm2/copyright\n\nINSTALLED_SIZE=`du -sk $PACKAGE_TMPDIR | cut -f 1`\nsed -i \"s/__VERSION__/$VERSION/\" packager/debian/control\nsed -i \"s/__INSTALLED_SIZE__/$INSTALLED_SIZE/\" packager/debian/control\n\nmkdir -p $PACKAGE_TMPDIR/etc/default\necho \"[+] Adding default configuration file for pm2 to package.\"\ncat <<EOF > $PACKAGE_TMPDIR/etc/default/pm2\n##\n## Default configuration var for pm2\n##\n\n# Path for PM2's home (configuration files, modules, sockets... etc)\nexport PM2_HOME=/etc/pm2\n\n# User that own files in PM2_HOME\nexport PM2_SOCKET_USER=\\`id -u pm2\\`\n\n# Group that own files in PM2_HOME\nexport PM2_SOCKET_GROUP=\\`id -g pm2\\`\n\nEOF\n\nmkdir -p $PACKAGE_TMPDIR/etc/systemd/system/\necho \"[+] Adding systemd configuration for pm2 to package.\"\ncat <<EOF > $PACKAGE_TMPDIR/etc/systemd/system/pm2.service\n[Unit]\nDescription=PM2 process manager\nDocumentation=https://pm2.keymetrics.io/\nAfter=network.target\n\n[Service]\nType=forking\nLimitNOFILE=infinity\nLimitNPROC=infinity\nLimitCORE=infinity\nPIDFile=/etc/pm2/pm2.pid\nRestart=on-failure\n\nExecStart=/usr/bin/pm2 resurrect\nExecReload=/usr/bin/pm2 reload all\nExecStop=/usr/bin/pm2 kill\n\n[Install]\nWantedBy=multi-user.target\nEOF\n\n# These are unneeded and throw lintian lint errors\nrm -f $PACKAGE_TMPDIR/usr/share/pm2/node_modules/node-uuid/benchmark/bench.gnu\nfind $PACKAGE_TMPDIR/usr/share/pm2 \\( -name '*.md' -o  -name '*.md~' -o -name '*.gitmodules' \\) -delete\n\n# Assume everything else is junk we don't need\nrm -rf $PACKAGE_TMPDIR/dist\n\n# Currently the \"binaries\" are JavaScript files that expect to be in the same\n# directory as the libraries, so we can't just copy them directly to /usr/bin.\n# We set the path and pass the args in another script instead.\n\nmkdir -p $PACKAGE_TMPDIR/usr/bin/\n\ncat <<EOF > $PACKAGE_TMPDIR/usr/bin/pm2\n#!/bin/bash\n. /etc/default/pm2\n/usr/share/pm2/bin/pm2 \\$@\nEOF\nchmod a+x $PACKAGE_TMPDIR/usr/bin/pm2\n\n#### Build RPM\nfpm --input-type dir --chdir $PACKAGE_TMPDIR \\\n    --name pm2 \\\n    --url https://pm2.io/ \\\n    --category 'Development/Languages' \\\n    --license AGPLv3 \\\n    --description '$(cat packager/debian/description)' \\\n    --vendor 'Keymetrics <tech@keymetrics.io>' \\\n    --maintainer 'Alexandre Strzelewicz <tech@keymetrics.io>' \\\n    --version $PM2_VERSION \\\n    --after-install packager/rhel/postinst \\\n    --before-remove packager/rhel/prerm \\\n    --after-remove packager/rhel/postrm \\\n    --architecture noarch \\\n    --depends nodejs \\\n    --output-type rpm .\n\n##### Adapt files for Debian-like distro\nmkdir -p $PACKAGE_TMPDIR/DEBIAN\nmkdir -p $PACKAGE_TMPDIR/usr/share/lintian/overrides/\ncp packager/debian/lintian-overrides $PACKAGE_TMPDIR/usr/share/lintian/overrides/pm2\n\n# Debian/Ubuntu call the Node.js binary \"nodejs\", not \"node\".\nsed -i 's/env node/env nodejs/' $PACKAGE_TMPDIR/usr/share/pm2/bin/pm2\n\n# Replace variables in Debian package control file\ncp packager/debian/* $PACKAGE_TMPDIR/DEBIAN/.\n\nls $PACKAGE_TMPDIR/DEBIAN/\n\n##### Build DEB (Debian, Ubuntu) package\nfakeroot dpkg-deb -b $PACKAGE_TMPDIR \"pm2_\"$VERSION\"_all.deb\"\n"
  },
  {
    "path": "packager/build-dist.sh",
    "content": "#!/bin/sh\n\nset -ex\n\n#npm run build\nnpm pack\nrm -rf dist\nmkdir dist\nmv pm2-*.tgz dist/pack.tgz\n\ncd dist\ntar -xzf pack.tgz --strip 1\nrm -rf pack.tgz\nnpm install --production\ncd ..\n\n# First run that print a banner\nnode dist/bin/pm2 --version\n\n# cleanup\nfind -name \"*~\" -delete\n\n# fix chmod\nchmod 755 `find -name LICENSE`\nchmod a+x `find -name \"*.sh\"`\n\ntar -cvzf dist/pm2-v`node dist/bin/pm2 --version`.tar.gz dist/*\nshasum -a 256 dist/pm2-*.tar.gz\n"
  },
  {
    "path": "packager/debian/control",
    "content": "Package: pm2\nVersion: __VERSION__\nDepends: nodejs (>= 6.12.2)\nConflicts: nodejs (<< 0.12.0)\nSection: devel\nPriority: optional\nArchitecture: all\nInstalled-Size: __INSTALLED_SIZE__\nMaintainer: Alexandre Strzelewicz <alexandre@keymetrics.io>\nHomepage: http://pm2.io/\nDescription: PM2 - Process Manager for Node.js.\n PM2 is a Process Manager mainly for Node.js that allows to automatically increase performance and stability while enhancing the process management experience for developer and DevOps.\n"
  },
  {
    "path": "packager/debian/copyright",
    "content": "Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/\nUpstream-Name: PM2\nUpstream-Contact: Alexandre Strzelewicz <alexandre@keymetrics.io>\nSource: http://pm2.o/\n\nFiles: *\nCopyright: 2013-present, Keymetrics\nLicense: AGPLv3\n"
  },
  {
    "path": "packager/debian/description",
    "content": "Advanced Process Manager\nPM2: Process Manager for Node.js\n\nPM2 is a Process Manager mainly for Node.js that allows to automatically increase performance and stability while enhancing the process management experience for developer and DevOps.\n"
  },
  {
    "path": "packager/debian/lintian-overrides",
    "content": "# No changelog file at the moment\npm2: debian-changelog-file-missing\n# node-gyp throws this\npm2: python-script-but-no-python-dep\n# Some .js files in node_modules have 0777 permissions instead of 0666\npm2: executable-not-elf-or-script\n# Some node_modules have node in shebang\npm2: unusual-interpreter\n# Third-party licenses\npm2: extra-license-file\n# lib/async.js in node_modules\npm2: embedded-javascript-library\n# No manpage... yet!\npm2: binary-without-manpage\n"
  },
  {
    "path": "packager/debian/postinst",
    "content": "#!/bin/bash\n\nset -e\n\nadduser --quiet --system \\\n        --group --home /etc/pm2 \\\n        --gecos \"PM2 Process Manager\" pm2\n\nif hash systemctl 2> /dev/null; then\n    {\n        systemctl enable \"pm2.service\" && \\\n            systemctl start \"pm2.service\"\n    } || echo \"pm2 could not be registered or started\"\nelif hash service 2> /dev/null; then\n    service \"pm2\" start || echo \"pm2 could not be registered or started\"\nelse\n    echo 'Ingnoring pm2 auto-startup.'\n    echo 'You can run `pm2 startup` as root to do it manually.'\nfi\n"
  },
  {
    "path": "packager/debian/postrm",
    "content": "#!/bin/bash\n\ndeluser --quiet pm2 || true\n"
  },
  {
    "path": "packager/debian/prerm",
    "content": "#!/bin/bash\n\nif hash systemctl 2> /dev/null; then\n    systemctl disable \"pm2.service\" && \\\n        systemctl stop \"pm2.service\" || \\\n        echo \"pm2 wasn't even running!\"\nelif hash service 2> /dev/null; then\n    service \"pm2\" stop || echo \"pm2 wasn't even running!\"\nelse\n    echo \"Your system does not appear to use upstart, systemd or sysv, so pm2 could not be stopped\"\n    echo 'Unless these systems were removed since install, no processes have been left running'\nfi\n"
  },
  {
    "path": "packager/publish_deb_rpm.sh",
    "content": "#!/bin/bash\n\nREPOSITORY_NAME=\"keymetrics/pm2\"\n\nfor OSDIST in 'ubuntu/trusty' 'ubuntu/xenial' 'ubuntu/yakkety' 'ubuntu/zesty' 'ubuntu/artful' 'debian/wheezy' 'debian/jessie' 'debian/stretch' 'debian/buster' 'raspbian/wheezy' 'raspbian/jessie' 'raspbian/stretch' 'raspbian/buster'\ndo\n    package_cloud push $REPOSITORY_NAME/$OSDIST `find -name \"*.deb\"`\ndone\n\nfor OSDIST in 'el/5' 'el/6' 'el/7' 'poky/jethro' 'poky/krogoth'\ndo\n    package_cloud push $REPOSITORY_NAME/$OSDIST `find -name \"*.rpm\"`\ndone\n\n"
  },
  {
    "path": "packager/rhel/postinst",
    "content": "#!/bin/bash\n\nset -e\n\nmkdir -p /etc/pm2\n\nadduser --system \\\n        --home-dir /etc/pm2 \\\n        --comment \"PM2 Process Manager\" pm2\n\nchown -R pm2:pm2 /etc/pm2\n\nif hash systemctl 2> /dev/null; then\n    {\n        systemctl enable \"pm2.service\" && \\\n            systemctl start \"pm2.service\"\n    } || echo \"pm2 could not be registered or started\"\nelif hash service 2> /dev/null; then\n    service \"pm2\" start || echo \"pm2 could not be registered or started\"\nelse\n    echo 'Ingnoring pm2 auto-startup.'\n    echo 'You can run `pm2 startup` as root to do it manually.'\nfi\n"
  },
  {
    "path": "packager/rhel/postrm",
    "content": "#!/bin/bash\n\nuserdel pm2 || true\ngroupdel pm2 || true\n"
  },
  {
    "path": "packager/rhel/prerm",
    "content": "#!/bin/bash\n\nif hash systemctl 2> /dev/null; then\n    systemctl disable \"pm2.service\" && \\\n        systemctl stop \"pm2.service\" || \\\n        echo \"pm2 wasn't even running!\"\nelif hash service 2> /dev/null; then\n    service \"pm2\" stop || echo \"pm2 wasn't even running!\"\nelse\n    echo \"Your system does not appear to use upstart, systemd or sysv, so pm2 could not be stopped\"\n    echo 'Unless these systems were removed since install, no processes have been left running'\nfi\n"
  },
  {
    "path": "packager/setup.deb.sh",
    "content": "#!/bin/bash\n\nREPOSITORY_OWNER=\"Keymetrics\"\n\nshow_banner ()\n{\n    echo\n    echo \"__/\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\____/\\\\\\\\\\\\\\\\____________/\\\\\\\\\\\\\\\\____/\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\_____\"\n    echo \" _\\\\/\\\\\\\\\\\\/////////\\\\\\\\\\\\_\\\\/\\\\\\\\\\\\\\\\\\\\\\\\________/\\\\\\\\\\\\\\\\\\\\\\\\__/\\\\\\\\\\\\///////\\\\\\\\\\\\___\"\n    echo \"  _\\\\/\\\\\\\\\\\\_______\\\\/\\\\\\\\\\\\_\\\\/\\\\\\\\\\\\//\\\\\\\\\\\\____/\\\\\\\\\\\\//\\\\\\\\\\\\_\\\\///______\\\\//\\\\\\\\\\\\__\"\n    echo \"   _\\\\/\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\/__\\\\/\\\\\\\\\\\\\\\\///\\\\\\\\\\\\/\\\\\\\\\\\\/_\\\\/\\\\\\\\\\\\___________/\\\\\\\\\\\\/___\"\n    echo \"    _\\\\/\\\\\\\\\\\\/////////____\\\\/\\\\\\\\\\\\__\\\\///\\\\\\\\\\\\/___\\\\/\\\\\\\\\\\\________/\\\\\\\\\\\\//_____\"\n    echo \"     _\\\\/\\\\\\\\\\\\_____________\\\\/\\\\\\\\\\\\____\\\\///_____\\\\/\\\\\\\\\\\\_____/\\\\\\\\\\\\//________\"\n    echo \"      _\\\\/\\\\\\\\\\\\_____________\\\\/\\\\\\\\\\\\_____________\\\\/\\\\\\\\\\\\___/\\\\\\\\\\\\/___________\"\n    echo \"       _\\\\/\\\\\\\\\\\\_____________\\\\/\\\\\\\\\\\\_____________\\\\/\\\\\\\\\\\\__/\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\_\"\n    echo \"        _\\\\///______________\\\\///______________\\\\///__\\\\///////////////__\"\n    echo \"                          Community Edition Setup\"\n    echo\n}\n\nunknown_os ()\n{\n    echo \"Unfortunately, your operating system distribution and version might not be supported by this script.\"\n    echo\n    echo \"You can override the OS detection by setting os= and dist= prior to running this script.\"\n    echo \"For example, to force Ubuntu Trusty: os=ubuntu dist=trusty ./script.sh\"\n    echo\n    echo \"For more informations, please read the documentation on http://pm2.io/\"\n    exit 1\n}\n\ngpg_check ()\n{\n    echo \"Checking for gpg...\"\n    if command -v gpg > /dev/null; then\n        echo \"Detected gpg...\"\n    else\n        echo \"Installing gnupg for GPG verification...\"\n        apt-get install -y gnupg\n        if [ \"$?\" -ne \"0\" ]; then\n            echo \"Unable to install GPG! Your base system has a problem; please check your default OS's package repositories because GPG should work.\"\n            echo \"Repository installation aborted.\"\n            exit 1\n        fi\n    fi\n}\n\ncurl_check ()\n{\n    echo \"Checking for curl...\"\n    if command -v curl > /dev/null; then\n        echo \"Detected curl...\"\n    else\n        echo \"Installing curl...\"\n        apt-get install -q -y curl\n        if [ \"$?\" -ne \"0\" ]; then\n            echo \"Unable to install curl! Your base system has a problem; please check your default OS's package repositories because curl should work.\"\n            echo \"Repository installation aborted.\"\n            exit 1\n        fi\n    fi\n}\n\ninstall_debian_keyring ()\n{\n    if [ \"${os}\" = \"debian\" ]; then\n        echo \"Installing debian-archive-keyring which is needed for installing \"\n        echo \"apt-transport-https on many Debian systems.\"\n        apt-get install -y debian-archive-keyring &> /dev/null\n    fi\n}\n\n\ndetect_os ()\n{\n    if [[ ( -z \"${os}\" ) && ( -z \"${dist}\" ) ]]; then\n        # some systems dont have lsb-release yet have the lsb_release binary and\n        # vice-versa\n        if [ -e /etc/lsb-release ]; then\n            . /etc/lsb-release\n\n            if [ \"${ID}\" = \"raspbian\" ]; then\n                os=${ID}\n                dist=`cut --delimiter='.' -f1 /etc/debian_version`\n            else\n                os=${DISTRIB_ID}\n                dist=${DISTRIB_CODENAME}\n\n                if [ -z \"$dist\" ]; then\n                    dist=${DISTRIB_RELEASE}\n                fi\n            fi\n\n        elif [ `which lsb_release 2>/dev/null` ]; then\n            dist=`lsb_release -c | cut -f2`\n            os=`lsb_release -i | cut -f2 | awk '{ print tolower($1) }'`\n\n        elif [ -e /etc/debian_version ]; then\n            # some Debians have jessie/sid in their /etc/debian_version\n            # while others have '6.0.7'\n            os=`cat /etc/issue | head -1 | awk '{ print tolower($1) }'`\n            if grep -q '/' /etc/debian_version; then\n                dist=`cut --delimiter='/' -f1 /etc/debian_version`\n            else\n                dist=`cut --delimiter='.' -f1 /etc/debian_version`\n            fi\n\n        else\n            unknown_os\n        fi\n    fi\n\n    if [ -z \"$dist\" ]; then\n        unknown_os\n    fi\n\n    # remove whitespace from OS and dist name\n    os=\"${os// /}\"\n    dist=\"${dist// /}\"\n\n    echo \"Detected operating system as $os/$dist.\"\n}\n\ninstall_node ()\n{\n    # Official install method of\n    # https://nodejs.org/en/download/package-manager/#debian-and-ubuntu-based-linux-distributions\n    # without using sudo.\n    curl -fsSL https://deb.nodesource.com/setup_lts.x | bash - || exit 1\n}\n\nmain ()\n{\n    show_banner\n    detect_os\n    curl_check\n    gpg_check\n\n    # Need to first run apt-get update so that apt-transport-https can be\n    # installed\n    echo -n \"Running apt-get update... \"\n    apt-get update &> /dev/null\n    echo \"done.\"\n\n    # Install the debian-archive-keyring package on debian systems so that\n    # apt-transport-https can be installed next\n    install_debian_keyring\n\n    echo -n \"Installing apt-transport-https... \"\n    apt-get install -y apt-transport-https &> /dev/null\n    echo \"done.\"\n\n    install_node\n\n    gpg_key_url=\"https://packagecloud.io/$REPOSITORY_OWNER/pm2/gpgkey\"\n    apt_config_url=\"https://packagecloud.io/install/repositories/$REPOSITORY_OWNER/pm2/config_file.list?os=${os}&dist=${dist}&source=script\"\n\n    apt_source_path=\"/etc/apt/sources.list.d/\"$REPOSITORY_OWNER\"_pm2.list\"\n\n    echo -n \"Installing $apt_source_path...\"\n\n    # create an apt config file for this repository\n    curl -sSf \"${apt_config_url}\" > $apt_source_path\n    curl_exit_code=$?\n\n    if [ \"$curl_exit_code\" = \"22\" ]; then\n        echo \"This script is unable to download the repository definition.\"\n        echo\n        [ -e $apt_source_path ] && rm $apt_source_path\n        unknown_os\n    elif [ \"$curl_exit_code\" = \"35\" -o \"$curl_exit_code\" = \"60\" ]; then\n        echo \"curl is unable to connect to packagecloud.io over TLS when running: \"\n        echo \"    curl ${apt_config_url}\"\n        echo \"This is usually due to one of two things:\"\n        echo\n        echo \" 1.) Missing CA root certificates (make sure the ca-certificates package is installed)\"\n        echo \" 2.) An old version of libssl. Try upgrading libssl on your system to a more recent version\"\n        echo\n        echo \"Contact support@packagecloud.io with information about your system for help.\"\n        [ -e $apt_source_path ] && rm $apt_source_path\n        exit 1\n    elif [ \"$curl_exit_code\" -gt \"0\" ]; then\n        echo\n        echo \"Unable to run: \"\n        echo \"    curl ${apt_config_url}\"\n        echo\n        echo \"Double check your curl installation and try again.\"\n        [ -e $apt_source_path ] && rm $apt_source_path\n        exit 1\n    else\n        echo \"done.\"\n    fi\n\n    echo -n \"Importing packagecloud gpg key... \"\n    # import the gpg key\n    curl -L \"${gpg_key_url}\" 2> /dev/null | apt-key add - &>/dev/null\n    echo \"done.\"\n\n    echo -n \"Running apt-get update... \"\n    # update apt on this system\n    apt-get update &> /dev/null\n    echo \"done.\"\n\n    echo -n \"Installing PM2...\"\n    apt-get install -y pm2 &> /dev/null\n    echo \"done.\"\n\n    CURR_USER=$SUDO_USER\n    if [ \"$CURR_USER\" == \"\" ]; then\n        CURR_USER=$USER\n    fi\n\n    if [ \"$CURR_USER\" == \"root\" ] || [ \"$CURR_USER\" == \"\" ]; then\n        echo \"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\"\n        echo \"WARNING: You are either running this script as root or the\"\n        echo \"         \\$USER variable is empty. In order to have a\"\n        echo \"         working PM2 installation, you need to add your\"\n        echo \"         user in the pm2 group using the following\"\n        echo \"         command:      usermod -aG pm2 <username>\"\n        echo \"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\"\n    else\n        echo -n \"Adding $CURR_USER to group pm2...\"\n        usermod -aG pm2 $CURR_USER\n        echo \"done.\"\n    fi\n    echo\n    echo \"Installation done.\"\n    echo \"You now need to logout of your system and login again in order to be able to use the 'pm2' command.\"\n}\n\nmain\n\n"
  },
  {
    "path": "packager/setup.rpm.sh",
    "content": "#!/bin/bash\n\nREPOSITORY_OWNER=\"Keymetrics\"\n\nshow_banner ()\n{\n    echo\n    echo \"__/\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\____/\\\\\\\\\\\\\\\\____________/\\\\\\\\\\\\\\\\____/\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\_____\"\n    echo \" _\\\\/\\\\\\\\\\\\/////////\\\\\\\\\\\\_\\\\/\\\\\\\\\\\\\\\\\\\\\\\\________/\\\\\\\\\\\\\\\\\\\\\\\\__/\\\\\\\\\\\\///////\\\\\\\\\\\\___\"\n    echo \"  _\\\\/\\\\\\\\\\\\_______\\\\/\\\\\\\\\\\\_\\\\/\\\\\\\\\\\\//\\\\\\\\\\\\____/\\\\\\\\\\\\//\\\\\\\\\\\\_\\\\///______\\\\//\\\\\\\\\\\\__\"\n    echo \"   _\\\\/\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\/__\\\\/\\\\\\\\\\\\\\\\///\\\\\\\\\\\\/\\\\\\\\\\\\/_\\\\/\\\\\\\\\\\\___________/\\\\\\\\\\\\/___\"\n    echo \"    _\\\\/\\\\\\\\\\\\/////////____\\\\/\\\\\\\\\\\\__\\\\///\\\\\\\\\\\\/___\\\\/\\\\\\\\\\\\________/\\\\\\\\\\\\//_____\"\n    echo \"     _\\\\/\\\\\\\\\\\\_____________\\\\/\\\\\\\\\\\\____\\\\///_____\\\\/\\\\\\\\\\\\_____/\\\\\\\\\\\\//________\"\n    echo \"      _\\\\/\\\\\\\\\\\\_____________\\\\/\\\\\\\\\\\\_____________\\\\/\\\\\\\\\\\\___/\\\\\\\\\\\\/___________\"\n    echo \"       _\\\\/\\\\\\\\\\\\_____________\\\\/\\\\\\\\\\\\_____________\\\\/\\\\\\\\\\\\__/\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\_\"\n    echo \"        _\\\\///______________\\\\///______________\\\\///__\\\\///////////////__\"\n    echo \"                          Community Edition Setup\"\n    echo\n}\n\nunknown_os ()\n{\n    echo \"Unfortunately, your operating system distribution and version might not be supported by this script.\"\n    echo\n    echo \"You can override the OS detection by setting os= and dist= prior to running this script.\"\n    echo \"For example, to force Ubuntu Trusty: os=ubuntu dist=trusty ./script.sh\"\n    echo\n    echo \"For more informations, please read the documentation on http://pm2.io/\"\n    exit 1\n}\n\ncurl_check ()\n{\n    echo \"Checking for curl...\"\n    if command -v curl > /dev/null; then\n        echo \"Detected curl...\"\n    else\n        echo \"Installing curl...\"\n        yum install -d0 -e0 -y curl\n    fi\n}\n\n\ndetect_os ()\n{\n    if [[ ( -z \"${os}\" ) && ( -z \"${dist}\" ) ]]; then\n        if [ -e /etc/os-release ]; then\n            . /etc/os-release\n            os=${ID}\n            if [ \"${os}\" = \"poky\" ]; then\n                dist=`echo ${VERSION_ID}`\n            elif [ \"${os}\" = \"sles\" ]; then\n                dist=`echo ${VERSION_ID}`\n            elif [ \"${os}\" = \"opensuse\" ]; then\n                dist=`echo ${VERSION_ID}`\n            else\n                dist=`echo ${VERSION_ID} | awk -F '.' '{ print $1 }'`\n            fi\n\n        elif [ `which lsb_release 2>/dev/null` ]; then\n            # get major version (e.g. '5' or '6')\n            dist=`lsb_release -r | cut -f2 | awk -F '.' '{ print $1 }'`\n\n            # get os (e.g. 'centos', 'redhatenterpriseserver', etc)\n            os=`lsb_release -i | cut -f2 | awk '{ print tolower($1) }'`\n\n        elif [ -e /etc/oracle-release ]; then\n            dist=`cut -f5 --delimiter=' ' /etc/oracle-release | awk -F '.' '{ print $1 }'`\n            os='ol'\n\n        elif [ -e /etc/fedora-release ]; then\n            dist=`cut -f3 --delimiter=' ' /etc/fedora-release`\n            os='fedora'\n\n        elif [ -e /etc/redhat-release ]; then\n            os_hint=`cat /etc/redhat-release  | awk '{ print tolower($1) }'`\n            if [ \"${os_hint}\" = \"centos\" ]; then\n                dist=`cat /etc/redhat-release | awk '{ print $3 }' | awk -F '.' '{ print $1 }'`\n                os='centos'\n            elif [ \"${os_hint}\" = \"scientific\" ]; then\n                dist=`cat /etc/redhat-release | awk '{ print $4 }' | awk -F '.' '{ print $1 }'`\n                os='scientific'\n            else\n                dist=`cat /etc/redhat-release  | awk '{ print tolower($7) }' | cut -f1 --delimiter='.'`\n                os='redhatenterpriseserver'\n            fi\n\n        else\n            aws=`grep -q Amazon /etc/issue`\n            if [ \"$?\" = \"0\" ]; then\n                dist='6'\n                os='aws'\n            else\n                unknown_os\n            fi\n        fi\n    fi\n\n    if [[ ( -z \"${os}\" ) || ( -z \"${dist}\" ) ]]; then\n        unknown_os\n    fi\n\n    # remove whitespace from OS and dist name\n    os=\"${os// /}\"\n    dist=\"${dist// /}\"\n\n    echo \"Detected operating system as ${os}/${dist}.\"\n}\n\nfinalize_yum_repo ()\n{\n    echo \"Installing pygpgme to verify GPG signatures...\"\n    yum install -y pygpgme --disablerepo='Keymetrics_pm2'\n    pypgpme_check=`rpm -qa | grep -qw pygpgme`\n    if [ \"$?\" != \"0\" ]; then\n        echo\n        echo \"WARNING: \"\n        echo \"The pygpgme package could not be installed. This means GPG verification is not possible for any RPM installed on your system. \"\n        echo \"To fix this, add a repository with pygpgme. Usualy, the EPEL repository for your system will have this. \"\n        echo \"More information: https://fedoraproject.org/wiki/EPEL#How_can_I_use_these_extra_packages.3F\"\n        echo\n\n        # set the repo_gpgcheck option to 0\n        sed -i'' 's/repo_gpgcheck=1/repo_gpgcheck=0/' /etc/yum.repos.d/Keymetrics_pm2.repo\n    fi\n\n    echo \"Installing yum-utils...\"\n    yum install -y yum-utils --disablerepo='Keymetrics_pm2'\n    yum_utils_check=`rpm -qa | grep -qw yum-utils`\n    if [ \"$?\" != \"0\" ]; then\n        echo\n        echo \"WARNING: \"\n        echo \"The yum-utils package could not be installed. This means you may not be able to install source RPMs or use other yum features.\"\n        echo\n    fi\n\n    echo \"Generating yum cache for Keymetrics_pm2...\"\n    yum -q makecache -y --disablerepo='*' --enablerepo='Keymetrics_pm2'\n}\n\nfinalize_zypper_repo ()\n{\n    zypper --gpg-auto-import-keys refresh Keymetrics_pm2\n}\n\ninstall_node ()\n{\n    curl --silent --location https://rpm.nodesource.com/setup_lts.x | bash - || exit 1\n}\n\ninstall_pm2 ()\n{\n    PKG_MANAGER=$1\n    echo -n \"Installing PM2 with $PKG_MANAGER...\"\n    $PKG_MANAGER install -y pm2 2> /dev/null\n\n    CURR_USER=$SUDO_USER\n    if [ \"$CURR_USER\" == \"\" ]; then\n        CURR_USER=$USER\n    fi\n\n    if [ \"$CURR_USER\" == \"root\" ] || [ \"$CURR_USER\" == \"\" ]; then\n        echo \"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\"\n        echo \"WARNING: You are either running this script as root or the\"\n        echo \"         \\$USER variable is empty. In order to have a\"\n        echo \"         working PM2 installation, you need to add your\"\n        echo \"         user in the pm2 group using the following\"\n        echo \"         command:      usermod -aG pm2 <username>\"\n        echo \"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\"\n    else\n        echo -n \"Adding $CURR_USER to group pm2...\"\n        usermod -aG pm2 $CURR_USER\n        echo \"done.\"\n    fi\n}\n\nmain ()\n{\n    show_banner\n    detect_os\n    curl_check\n\n    yum_repo_config_url=\"https://packagecloud.io/install/repositories/$REPOSITORY_OWNER/pm2/config_file.repo?os=${os}&dist=${dist}&source=script\"\n\n    if [ \"${os}\" = \"sles\" ] || [ \"${os}\" = \"opensuse\" ]; then\n        yum_repo_path=/etc/zypp/repos.d/Keymetrics_pm2.repo\n    else\n        yum_repo_path=/etc/yum.repos.d/Keymetrics_pm2.repo\n        install_node\n    fi\n\n    echo \"Downloading repository file: ${yum_repo_config_url}\"\n\n    curl -sSf \"${yum_repo_config_url}\" > $yum_repo_path\n    curl_exit_code=$?\n\n    if [ \"$curl_exit_code\" = \"22\" ]; then\n        echo\n        echo\n        echo -n \"Unable to download repo config from: \"\n        echo \"${yum_repo_config_url}\"\n        echo\n        echo \"This usually happens if your operating system is not supported by \"\n        echo \"packagecloud.io, or this script's OS detection failed.\"\n        echo\n        echo \"You can override the OS detection by setting os= and dist= prior to running this script.\"\n        echo \"You can find a list of supported OSes and distributions on our website: https://packagecloud.io/docs#os_distro_version\"\n        echo\n        echo \"For example, to force CentOS 6: os=el dist=6 ./script.sh\"\n        echo\n        echo \"If you are running a supported OS, please email support@packagecloud.io and report this.\"\n        [ -e $yum_repo_path ] && rm $yum_repo_path\n        exit 1\n    elif [ \"$curl_exit_code\" = \"35\" -o \"$curl_exit_code\" = \"60\" ]; then\n        echo\n        echo \"curl is unable to connect to packagecloud.io over TLS when running: \"\n        echo \"    curl ${yum_repo_config_url}\"\n        echo\n        echo \"This is usually due to one of two things:\"\n        echo\n        echo \" 1.) Missing CA root certificates (make sure the ca-certificates package is installed)\"\n        echo \" 2.) An old version of libssl. Try upgrading libssl on your system to a more recent version\"\n        echo\n        echo \"Contact support@packagecloud.io with information about your system for help.\"\n        [ -e $yum_repo_path ] && rm $yum_repo_path\n        exit 1\n    elif [ \"$curl_exit_code\" -gt \"0\" ]; then\n        echo\n        echo \"Unable to run: \"\n        echo \"    curl ${yum_repo_config_url}\"\n        echo\n        echo \"Double check your curl installation and try again.\"\n        [ -e $yum_repo_path ] && rm $yum_repo_path\n        exit 1\n    else\n        echo \"done.\"\n    fi\n\n    if [ \"${os}\" = \"sles\" ] || [ \"${os}\" = \"opensuse\" ]; then\n        finalize_zypper_repo\n        install_pm2 zypper\n    else\n        finalize_yum_repo\n        install_pm2 yum\n    fi\n\n    echo\n    echo \"Installation done.\"\n    echo \"You now need to logout of your system and login again in order to be able to use the 'pm2' command.\"\n}\n\nmain\n"
  },
  {
    "path": "paths.js",
    "content": "/**\n * Copyright 2013-2022 the PM2 project authors. All rights reserved.\n * Use of this source code is governed by a license that\n * can be found in the LICENSE file.\n */\n\nvar debug = require('debug')('pm2:paths');\nvar p     = require('path');\nvar fs    = require('fs')\n\nfunction getDefaultPM2Home() {\n  var PM2_ROOT_PATH;\n\n  if (process.env.PM2_HOME)\n    PM2_ROOT_PATH = process.env.PM2_HOME;\n  else if (process.env.HOME && !process.env.HOMEPATH)\n    PM2_ROOT_PATH = p.resolve(process.env.HOME, '.pm2');\n  else if (process.env.HOME || process.env.HOMEPATH)\n    PM2_ROOT_PATH = p.resolve(process.env.HOMEDRIVE, process.env.HOME || process.env.HOMEPATH, '.pm2');\n  else {\n    console.error('[PM2][Initialization] Environment variable HOME (Linux) or HOMEPATH (Windows) are not set!');\n    console.error('[PM2][Initialization] Defaulting to /etc/.pm2');\n    PM2_ROOT_PATH = p.resolve('/etc', '.pm2');\n  }\n\n  debug('pm2 home resolved to %s', PM2_ROOT_PATH, process.env.HOME);\n  return PM2_ROOT_PATH;\n}\n\nmodule.exports = function(PM2_HOME) {\n  var has_node_embedded = false\n\n  if (fs.existsSync(p.resolve(__dirname, './node')) === true) {\n    has_node_embedded = true\n  }\n\n  if (!PM2_HOME) {\n    PM2_HOME = getDefaultPM2Home()\n  }\n\n  var pm2_file_stucture = {\n    PM2_HOME                 : PM2_HOME,\n    PM2_ROOT_PATH            : PM2_HOME,\n\n    PM2_CONF_FILE            : p.resolve(PM2_HOME, 'conf.js'),\n    PM2_MODULE_CONF_FILE     : p.resolve(PM2_HOME, 'module_conf.json'),\n\n    PM2_LOG_FILE_PATH        : p.resolve(PM2_HOME, 'pm2.log'),\n    PM2_PID_FILE_PATH        : p.resolve(PM2_HOME, 'pm2.pid'),\n\n    PM2_RELOAD_LOCKFILE      : p.resolve(PM2_HOME, 'reload.lock'),\n\n    DEFAULT_PID_PATH         : p.resolve(PM2_HOME, 'pids'),\n    DEFAULT_LOG_PATH         : p.resolve(PM2_HOME, 'logs'),\n    DEFAULT_MODULE_PATH      : p.resolve(PM2_HOME, 'modules'),\n    PM2_IO_ACCESS_TOKEN      : p.resolve(PM2_HOME, 'pm2-io-token'),\n    DUMP_FILE_PATH           : p.resolve(PM2_HOME, 'dump.pm2'),\n    DUMP_BACKUP_FILE_PATH    : p.resolve(PM2_HOME, 'dump.pm2.bak'),\n\n    DAEMON_RPC_PORT          : p.resolve(PM2_HOME, 'rpc.sock'),\n    DAEMON_PUB_PORT          : p.resolve(PM2_HOME, 'pub.sock'),\n    INTERACTOR_RPC_PORT      : p.resolve(PM2_HOME, 'interactor.sock'),\n\n    INTERACTOR_LOG_FILE_PATH : p.resolve(PM2_HOME, 'agent.log'),\n    INTERACTOR_PID_PATH      : p.resolve(PM2_HOME, 'agent.pid'),\n    INTERACTION_CONF         : p.resolve(PM2_HOME, 'agent.json5'),\n\n    HAS_NODE_EMBEDDED        : has_node_embedded,\n    BUILTIN_NODE_PATH        : has_node_embedded === true ? p.resolve(__dirname, './node/bin/node') : null,\n    BUILTIN_NPM_PATH         : has_node_embedded === true ? p.resolve(__dirname, './node/bin/npm') : null,\n  };\n\n  // allow overide of file paths via environnement\n  var paths = Object.keys(pm2_file_stucture);\n  paths.forEach(function (key) {\n    var envKey = key.indexOf('PM2_') > -1 ? key : 'PM2_' + key;\n    if (process.env[envKey] && key !== 'PM2_HOME' && key !== 'PM2_ROOT_PATH') {\n      pm2_file_stucture[key] = process.env[envKey];\n    }\n  });\n\n  if (process.platform === 'win32' ||\n      process.platform === 'win64') {\n    //@todo instead of static unique rpc/pub file custom with PM2_HOME or UID\n    pm2_file_stucture.DAEMON_RPC_PORT = '\\\\\\\\.\\\\pipe\\\\rpc.sock';\n    pm2_file_stucture.DAEMON_PUB_PORT = '\\\\\\\\.\\\\pipe\\\\pub.sock';\n    pm2_file_stucture.INTERACTOR_RPC_PORT = '\\\\\\\\.\\\\pipe\\\\interactor.sock';\n  }\n\n  return pm2_file_stucture;\n};\n"
  },
  {
    "path": "pm2",
    "content": "#!/bin/bash\n\nSCRIPT_PATH=\"$(dirname \"$0\")/../lib/binaries/CLI.js\"\n\n# Check if 'bun' is available, otherwise use 'node'\nif command -v bun &> /dev/null\nthen\n    bun \"$SCRIPT_PATH\" \"$@\"\nelse\n    node \"$SCRIPT_PATH\" \"$@\"\nfi\n"
  },
  {
    "path": "preinstall.js",
    "content": "const fs = require('fs');\nconst path = require('path');\n\n// Determine platform\nconst isWindows = process.platform === 'win32';\n\nif (!isWindows)\n  process.exit(0)\n\nconst sourceFile = 'bin/pm2-windows';\nconst destinationFile = 'bin/pm2';\n\n// Resolve file paths\nconst sourcePath = path.resolve(__dirname, sourceFile);\nconst destinationPath = path.resolve(__dirname, destinationFile);\n\n// Copy the appropriate file based on the platform\nfs.copyFile(sourcePath, destinationPath, (err) => {\n  if (err) {\n    console.error(`Error copying file from ${sourcePath} to ${destinationPath}:`, err);\n    process.exit(1);\n  }\n  console.log(`Successfully copied ${sourceFile} to ${destinationFile}`);\n});\n"
  },
  {
    "path": "pres/TMP.md",
    "content": "### Commands Cheatsheet\n\n<details>\n  <summary>Commands Cheatsheet</summary>\n\n```bash\n# General\n$ npm install pm2 -g            # Install PM2\n$ pm2 start app.js              # Start, Daemonize and auto-restart application (Node)\n$ pm2 start app.py              # Start, Daemonize and auto-restart application (Python)\n$ pm2 start npm -- start        # Start, Daemonize and auto-restart Node application\n\n# Cluster Mode (Node.js only)\n$ pm2 start app.js -i 4         # Start 4 instances of application in cluster mode\n                                # it will load balance network queries to each app\n$ pm2 reload all                # Zero Second Downtime Reload\n$ pm2 scale [app-name] 10       # Scale Cluster app to 10 process\n\n# Process Monitoring\n$ pm2 list                      # List all processes started with PM2\n$ pm2 list --sort=<field>       # Sort all processes started with PM2\n$ pm2 monit                     # Display memory and cpu usage of each app\n$ pm2 show [app-name]           # Show all information about application\n\n# Log management\n$ pm2 logs                      # Display logs of all apps\n$ pm2 logs [app-name]           # Display logs for a specific app\n$ pm2 logs --json               # Logs in JSON format\n$ pm2 flush\n$ pm2 reloadLogs\n\n# Process State Management\n$ pm2 start app.js --name=\"api\" # Start application and name it \"api\"\n$ pm2 start app.js -- -a 34     # Start app and pass option \"-a 34\" as argument\n$ pm2 start app.js --watch      # Restart application on file change\n$ pm2 start script.sh           # Start bash script\n$ pm2 start app.json            # Start all applications declared in app.json\n$ pm2 reset [app-name]          # Reset all counters\n$ pm2 stop all                  # Stop all apps\n$ pm2 stop 0                    # Stop process with id 0\n$ pm2 restart all               # Restart all apps\n$ pm2 delete all                # Kill and delete all apps\n$ pm2 delete 0                  # Delete app with id 0\n\n# Startup/Boot management\n$ pm2 startup                   # Detect init system, generate and configure pm2 boot on startup\n$ pm2 save                      # Save current process list\n$ pm2 resurrect                 # Restore previously saved processes\n$ pm2 unstartup                 # Disable and remove startup system\n\n$ pm2 update                    # Save processes, kill PM2 and restore processes\n$ pm2 init                      # Generate a sample js configuration file\n\n# Deployment\n$ pm2 deploy app.json prod setup    # Setup \"prod\" remote server\n$ pm2 deploy app.json prod          # Update \"prod\" remote server\n$ pm2 deploy app.json prod revert 2 # Revert \"prod\" remote server by 2\n\n# Module system\n$ pm2 module:generate [name]    # Generate sample module with name [name]\n$ pm2 install pm2-logrotate     # Install module (here a log rotation system)\n$ pm2 uninstall pm2-logrotate   # Uninstall module\n$ pm2 publish                   # Increment version, git push and npm publish\n```\n\n</details>\n\nAlso check out the [example folder](https://github.com/Unitech/pm2/tree/master/examples) to discover all features.\n"
  },
  {
    "path": "run.sh",
    "content": "#!/bin/bash\n\n# Check if 'bun' is available, otherwise use 'node'\nif command -v bun &> /dev/null\nthen\n    bun \"$@\"\nelse\n    node \"$@\"\nfi\n"
  },
  {
    "path": "test/Dockerfile",
    "content": "FROM node:alpine\n\nRUN mkdir -p /var/pm2\n\nWORKDIR /var/pm2\n\nENV NODE_ENV test\nENV PM2_DISCRETE_MODE true\n\nRUN apk update && apk add bash git curl python python3 php5 && rm -rf /var/cache/apk/*\nRUN ln -s /usr/bin/php5 /usr/bin/php\nRUN npm install -g mocha@3.5\n\nCMD [\"mocha\", \"./test/programmatic/api.mocha.js\"]\n"
  },
  {
    "path": "test/README.md",
    "content": "\n# Installing development version\n\n```bash\n$ npm install git://github.com/Unitech/pm2.git#development -g\n```\n\n# Redhat\n\n```\n$ sudo yum install git wget emacs\n$ sudo yum groupinstall \"Development Tools\"\n$ wget -qO- https://raw.github.com/creationix/nvm/master/install.sh | sh\n$ # put .bash_profile content to .bashrc\n$ source .bashrc\n$ nvm install v0.11.10\n$ nvm alias default 0.11.10\n$ npm install pm2 -g\n$ # OR\n$ npm install git://github.com/Unitech/pm2.git#development -g\n```\n\n# CentOS\n\n```\n$ yum install git wget emacs\n$ wget -qO- https://raw.github.com/creationix/nvm/master/install.sh | sh\n$\n```\n\n## Remove init script\n\nsudo update-rc.d -f pm2-init.sh remove\n```\n$ chkconfig --del pm2-init.sh\n$ chkconfig --add pm2-init.sh\n```\n\ngyp WARN EACCES user \"root\" does not have permission to create dev dir :\nhttps://github.com/TooTallNate/node-gyp/issues/126\n-> add --unsafe-perm\n\n# .pm2\n\nDoesnt work\n\n```\n$ sudo sh -c 'echo \"export PM2_HOME=/var/\" >> /etc/profile'\n$ sudo mkdir /var/.pm2; chown -R tknew:tknew /var/.pm2\n```\n"
  },
  {
    "path": "test/benchmarks/monit-daemon.sh",
    "content": "#!/bin/bash\n\nwhile [ true ]\ndo\n    PM2_PID=`pgrep \"pm2: Daemon\" -o`\n\n    # Run garbage collector\n    kill -SIGILL $PM2_PID\n    sleep 5\n\n    FILE=\"/proc/$PM2_PID/smaps\"\n    Rss=`echo 0 $(cat $FILE  | grep Rss | awk '{print $2}' | sed 's#^#+#') | bc;`\n\n    echo `date +%H:%M:%S` $Rss >> $RESULT_FILE\n    sleep 100\ndone\n"
  },
  {
    "path": "test/benchmarks/monit.sh",
    "content": "#!/bin/bash\n\nRESULT_FILE=result.monit\n\nexport RESULT_FILE=$RESULT_FILE\n\nlaunch() {\n    echo \"========= `date`\" >> $RESULT_FILE\n    nohup ./monit-daemon.sh &> monit.log &\n}\n\nppkill() {\n    pkill -f monit-daemon.sh ; pkill -f sleep\n}\n\ncase \"$1\" in\n    start)\n        launch\n        ;;\n    kill)\n        ppkill\n        ;;\n    stop)\n        ppkill\n        ;;\n    restart)\n        ppkill\n        launch\n        ;;\n    *)\n        echo \"Usage: {start|kill|stop|restart}\"\n        exit 1\n        ;;\nesac\nexit $RETVAL\n"
  },
  {
    "path": "test/benchmarks/result.monit",
    "content": "========= Fri Aug 22 14:11:29 EDT 2014\n14:11:35 61012\n========= Fri Aug 22 14:11:45 EDT 2014\n14:11:50 59984\n========= Fri Aug 22 14:12:47 EDT 2014\n14:12:52 59464\n"
  },
  {
    "path": "test/docker_parallel_test.sh",
    "content": "set -e\n\ndocker build -t pm2-test -f test/Dockerfile .\n\nJOBS=2\nOPTS=\"--jobs $JOBS --joblog joblog-X docker run -v `pwd`:/var/pm2 pm2-test\"\n\nls test/e2e/cli/* | parallel $OPTS bash\n\n#ls test/e2e/binaries/* test/e2e/modules/* test/e2e/internal/* test/e2e/process-file/* test/e2e/cli/* test/e2e/logs/* | parallel $OPTS bash\n"
  },
  {
    "path": "test/e2e/binaries/pm2-dev.sh",
    "content": "#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\n\n\npm2_path=`pwd`/bin/pm2-dev\n\nif [ ! -f $pm2_path ];\nthen\n    pm2_path=`pwd`/../bin/pm2-dev\n    if [ ! -f $pm2_path ];\n    then\n        pm2_path=`pwd`/../../bin/pm2-dev\n    fi\nfi\n\npm2dev=\"$pm2_path\"\n\nexport PM2_HOME=$HOME'/.pm2-dev'\n\ncd $file_path/pm2-dev\n\n# Test with js\n$pm2dev app.js  &\nsleep 2\n$pm2 ls\nshould 'should have started 1 apps' 'online' 1\nshould 'should watch be true' 'watch: true' 1\npkill -f Daemon\n$pm2 kill\n\necho \"THEN\"\n# Test with json and args\n$pm2dev start app.json --test-mode\n$pm2 ls\nshould 'should have started 1 apps' 'online' 1\n$pm2 prettylist | grep \"watch: \\[ 'server', 'client' \\]\"\nspec \"Should application have two watch arguments\"\n$pm2 prettylist | grep \"ignore_watch: \\[ 'node_modules', 'client/img' \\]\"\nspec \"Should application have two ignore_watch arguments\"\n$pm2 kill\n"
  },
  {
    "path": "test/e2e/binaries/pm2-runtime.sh",
    "content": "#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\n\n\npm2_path=`pwd`/bin/pm2-runtime\n\nif [ ! -f $pm2_path ];\nthen\n    pm2_path=`pwd`/../bin/pm2-runtime\n    if [ ! -f $pm2_path ];\n    then\n        pm2_path=`pwd`/../../bin/pm2-runtime\n    fi\nfi\n\npm2_runtime=\"$pm2_path\"\n\nexport PM2_RUNTIME_DEBUG='true'\n\ncd $file_path/pm2-dev\n\n#\n# Simple start with 4 apps\n#\n$pm2 kill\npkill -f PM2\n\n$pm2_runtime app.js -i 4\nshould 'should have started 4 apps' 'online' 4\n\n$pm2 kill\n\n#\n# Test with json and args\n#\n$pm2_runtime app.json\nshould 'should have started 1 apps' 'online' 1\n$pm2 prettylist | grep \"watch: \\[ 'server', 'client' \\]\"\nspec \"Should application have two watch arguments\"\n$pm2 prettylist | grep \"ignore_watch: \\[ 'node_modules', 'client/img' \\]\"\nspec \"Should application have two ignore_watch arguments\"\n$pm2 kill\n\n# Restore default behavior for exit checks\nunset PM2_RUNTIME_DEBUG\n\n#\n# --no-autorestart checks\n#\n# $pm2_runtime app.js --no-autorestart\n# PID_PM2=$!\n# $pm2 pid app\n# echo \"OK\"\n# PID=`cat /tmp/pid`\n# echo $PID\n# kill $PID\n# sleep 3\n# pgrep \"PM2\"\n# ispec \"PM2 runtime should be killed because no app is running\"\n\n#\n# Auto Exit Worker\n#\n$pm2_runtime exited_app.js 2> /dev/null\nsleep 1\npgrep \"PM2\"\nispec \"PM2 runtime should be killed because no app is running\"\n"
  },
  {
    "path": "test/e2e/cli/app-configuration.sh",
    "content": "#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\n\nnode -e \"require('semver').lt(process.versions.node, '6.0.0') ? process.exit(0) : process.exit(1)\"\n[ $? -eq 1 ] || exit 0\n\ncd $file_path\n\n$pm2 unset echo\nspec \"Should unset echo variables\"\n\n$pm2 start echo.js --name \"echo\"\nshould 'should app be online' 'online' 1\n\nshould 'should not have config variable' \"config_var: 'false'\" 0\n\n$pm2 set echo.config_var false\n\nexists 'should NOW have config variable' \"config_var: 'false'\"\n\n$pm2 set echo.probes true\n\nexists 'should NOW have config variable' \"probes: 'true'\"\nshould 'should have start 3 apps' 'restart_time: 2' 1\n\n$pm2 multiset \"echo.conf false\"\n\nexists 'should NOW have config variable' \"conf: 'false'\"\nshould 'should have start 3 apps' 'restart_time: 3' 1\n\n# $pm2 get echo.config_var | grep \"false\"\n# spec \"Should get method work\"\n\n# $pm2 get echo | grep \"false\\|true\"\n# spec \"Should get method work\"\n\n# $pm2 conf echo.config_var | grep \"false\"\n# spec \"Should conf method work\"\n\n# $pm2 conf echo | grep \"false\\|true\"\n# spec \"Should get method work\"\n\n$pm2 delete all\n\n#\n#\n#\n#\n\n$pm2 unset \"probe-test\"\n$pm2 start probes.js --name \"probe-test\"\n\necho \"Wait for init...\"\n\nsleep 3\n\nexists 'probe test-probe exist' \"test-probe\"\nexists 'probe Event Loop Latency exist' \"Event Loop Latency p95\"\n\n# Set new value for alert probe\n# $pm2 set probe-test.probes.Event\\ Loop\\ Latency.value 25\n# sleep 1\n\n# exists 'probe Event Loop Latency alerted' \"alert: { cmp: '>', value: 25, mode: 'threshold'\"\n\n# Override value for test-probe\n# $pm2 set probe-test.probes.test-probe.value 30\n# sleep 1\n"
  },
  {
    "path": "test/e2e/cli/args.sh",
    "content": "#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\n\ncd $file_path/args\n\nrm args.log\n> args.log\n\n## Test #1\n\n$pm2 start -f params_check.js --merge-logs -o args.log --name 'some-project' -- \\\n     --logger.level=info \\\n     --koa.hostname=0.0.0.0 \\\n     --ms.amqp.hostname=amqps://localhost \\\n     --ms.amqp.port=5673 \\\n     --ms.amqp.heartbeat=30 \\\n     --ms.amqp.username=someuser \\\n     --ms.amqp.password=12345 \\\n     --ms.amqp.vhost=default \\\n     --mailer.config.host=localhost \\\n     --mailer.config.auth=null\n\nfunction hasArg {\n    if [ \"$2\" ]\n    then\n        OCCURENCES=$2\n    else\n        OCCURENCES=1\n    fi\n    CMD=`grep -c -- $1 args.log`\n    [ $CMD -eq $OCCURENCES ] || fail \"Arg $1 not present\"\n    success \"Arg $1 present\"\n}\n\nsleep 2\nhasArg \"--logger.level=info\"\nhasArg \"--koa.hostname=0.0.0.0\"\nhasArg \"--ms.amqp.hostname=amqps://localhost\"\nhasArg \"--ms.amqp.port=5673\"\nhasArg \"--ms.amqp.heartbeat=30\"\nhasArg \"--ms.amqp.username=someuser\"\nhasArg \"--ms.amqp.password=12345\"\nhasArg \"--ms.amqp.vhost=default\"\nhasArg \"--mailer.config.host=localhost\"\nhasArg \"--mailer.config.auth=null\"\n\n## Test #2 with double params\n\n$pm2 delete all\n>args.log\n\n$pm2 start -f params_check.js echo.js --merge-logs -o args.log -- argv1 argv1 argv2 argv3\n\nsleep 2\nhasArg \"argv1\" 2\nhasArg \"argv2\"\nhasArg \"argv3\"\n"
  },
  {
    "path": "test/e2e/cli/attach.sh",
    "content": "#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\n\ncd $file_path/stdin\n\n$pm2 start stdin.js -o out-rel.log --merge-logs\n>out-rel.log\n\n# Send LINE\\n to stdin application\n$pm2 send 0 \"LINE\"\n\ncat out-rel.log\ngrep \"LINE\" out-rel.log\nspec \"Should have reveived line\"\n\n$pm2 delete all\n"
  },
  {
    "path": "test/e2e/cli/binary.sh",
    "content": "#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\n\ncd $file_path\n\nfunction getInterpreter() {\n\techo `$pm2 prettylist | grep \"exec_interpreter:\" | awk -F\"'\" '{print $2}'`\n}\n\n#\n# Testing pm2 execution of binary files\n#\n$pm2 start `type -p watch` -- ls\n\nOUT=$(getInterpreter)\n\n[ $OUT=\"none\" ] || fail \"$1\"\nsuccess \"$1\"\n\n$pm2 kill\n$pm2 start binary-js-file\n\nOUT=$(getInterpreter)\necho $OUT\n\n[ $OUT=\"node\" ] || fail \"$1\"\nsuccess \"$1\"\n\n$pm2 kill\n$pm2 start binary-js-file.js\n\nOUT=$(getInterpreter)\n[ $OUT=\"node\" ] || fail \"$1\"\nsuccess \"$1\"\n\n$pm2 kill\n$pm2 start binary-py-file.py\n\nOUT=$(getInterpreter)\n[ $OUT=\"python\" ] || fail \"$1\"\nsuccess \"$1\"\n\n$pm2 kill\n\n#\n# Should execute command in $PATH\n#\n$pm2 start ls\nspec \"Should script started\"\n\nOUT=$(getInterpreter)\n[ $OUT=\"none\" ] || fail \"$1\"\nsuccess \"Right interpreter\"\n\nshould 'Have the right relative path' '/bin/ls' 1\n"
  },
  {
    "path": "test/e2e/cli/bun.sh",
    "content": "#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\n\ncd $file_path/interpreter\n\n########### typescript fork test\n$pm2 delete all\n\n>typescript.log\n\n$pm2 start echo.ts -o typescript.log --merge-logs\n\nsleep 1.5\n\ngrep \"Hello Typescript!\" typescript.log\nspec \"Should work on Typescript files in fork mode\"\n\n# ########### typescript cluster test\n$pm2 delete all\n\n>typescript.log\n\n$pm2 start echo.tsx -o typescript.log --merge-logs\n\nsleep 1.5\n\ngrep \"Hello Typescript!\" typescript.log\nspec \"Should work on Typescript files in fork mode\"\n\n$pm2 delete all\n"
  },
  {
    "path": "test/e2e/cli/cli-actions-1.sh",
    "content": "#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\n\ncd $file_path\n\n# Determine wget / curl\nwhich wget > /dev/null\nif [ $? -eq 0 ]\nthen\n    http_get=\"wget\"\nelse\n    echo -e \"\\033[31mYou need wget to run this test \\033[0m\";\n    exit 1;\nfi\n\n#\n# Different way to stop process\n#\n$pm2 start echo.js\n$pm2 start echo.js -f\n$pm2 start echo.js -f\n\nsleep 0.5\n\nshould 'should have started 3 apps' 'online' 3\n\n$pm2 stop 12412\n$pm2 stop 0\n\nshould 'should have stopped 1 apps' 'stopped' 1\n\n$pm2 stop asdsdaecho.js\n\n$pm2 stop echo\n\nshould 'should have stopped 3 apps' 'stopped' 3\n\n\n#\n# Describe process\n#\n$pm2 describe 0\nspec \"should describe stopped process\"\n\n$pm2 restart 1\n\n$pm2 describe 1\nspec \"should describe online process\"\n\n$pm2 describe asdsa\nispec \"should exit with right exit code when no process found\"\n\n#\n# Update pm2\n#\n$pm2 updatePM2\nspec \"should update pm2\"\n\n\n\n#\n# Verify PID\n#\n$pm2 kill\n\n$pm2 start echo.js -p echo.pid\n\nsleep 0.5\nls echo-0.pid\nspec \"should pid file exists\"\n\n$pm2 stop all\n\nsleep 1\n\nls echo-0.pid\nispec \"should pid file be deleted once stopped\"\n\n$pm2 kill\n\n$pm2 start echo.js -p echo.pid -i 1\n\nsleep 1\n\nls echo-0.pid\nspec \"should pid file exists\"\n\n$pm2 stop all\n\nsleep 1\n\nls echo-0.pid\nispec \"should pid file be deleted once stopped\"\n\n$pm2 kill\n\n\n\n\n\n\n#\n# Main tests\n#\n$pm2 kill\nspec \"kill daemon\"\n\n$pm2 start eyayimfake\nispec \"should fail if script doesnt exist\"\n\n$pm2\nispec \"No argument\"\n\n$pm2 list\n\n$pm2 start cluster-pm2.json\nspec \"Should start well formated json with name for file prefix\"\n\n$pm2 list\nspec \"Should list processes successfully\"\n\n\n$pm2 start multi-echo.json\nspec \"Should start multiple applications\"\n\n$pm2 init echo\nspec \"Should init echo sample json\"\n\n$pm2 start echo-pm2.json -f\nspec \"Should start echo service\"\n\n$pm2 list\n\n# Not consistent on travis :(\n# OUT=`$pm2 logs --nostream --lines 10 PM2 | wc -l`\n# [ $OUT -gt 10 ] || fail \"Error : pm2 logs ouput showed $OUT lines but min is 10\"\n# success \"should only print logs\"\n\n# OUT=`$pm2 logs --nostream --lines 100 PM2 | wc -l`\n# [ $OUT -gt 100 ] || fail \"Error : pm2 logs ouput showed $OUT  lines but min is 100\"\n# success \"should only print logs: \"\n\n# sleep 1\n\n# kill $!\n# spec \"Should kill logs\"\n\n# $pm2 logs echo &\n# spec \"Should display logs\"\n# TMPPID=$!\n\n# sleep 1\n\n# kill $!\n# spec \"Should kill logs\"\n\n\n# $pm2 web\n# spec \"Should start web interface\"\n\n# sleep 1\n\n# JSON_FILE='/tmp/web-json'\n\n# $http_get -q http://localhost:9615/ -O $JSON_FILE\n# cat $JSON_FILE | grep \"HttpInterface.js\" > /dev/null\n# spec \"Should get the right JSON with HttpInterface file launched\"\n\n# $pm2 flush\n# spec \"Should clean logs\"\n\n# # cat ~/.pm2/logs/echo-out.log | wc -l\n# # spec \"File Log should be cleaned\"\n\n# sleep 1\n# $http_get -q http://localhost:9615/ -O $JSON_FILE\n# cat $JSON_FILE | grep \"restart_time\\\":0\" > /dev/null\n# spec \"Should get the right JSON with HttpInterface file launched\"\n\n# #\n# # Restart only one process\n# #\n# $pm2 restart 1\n# should 'should has restarted process' 'restart_time: 1' 1\n\n# #\n# # Restart all processes\n# #\n# $pm2 restart all\n# spec \"Should restart all processes\"\n\n# sleep 1\n# $http_get -q http://localhost:9615/ -O $JSON_FILE\n# OUT=`cat $JSON_FILE | grep -o \"restart_time\\\":1\" | wc -l`\n\n# [ $OUT -eq 7 ] || fail \"Error while wgeting data via web interface\"\n# success \"Got data from interface\"\n\n\n$pm2 start echo-env.js\n\n$pm2 list\n\n$pm2 dump\nspec \"Should dump current processes\"\n\n$pm2 save\nspec \"Should save (dump alias) current processes\"\n\n\nls ~/.pm2/dump.pm2\nspec \"Dump file should be present\"\n\n$pm2 stop all\nspec \"Should stop all processes\"\n\nsleep 0.5\nshould 'should have stopped 8 apps' 'stopped' 8\n\n\n$pm2 kill\n\n#\n# Issue #71\n#\n\nPROC_NAME='ECHONEST'\n# Launch a script with name option\n$pm2 start echo.js --name $PROC_NAME -f\nshould 'should have started app with name' 'ECHONEST' 7\n\n# Restart a process by name\n$pm2 restart $PROC_NAME\nshould 'should have restarted app by name' 'restart_time: 1' 1\n\n\n\n$pm2 kill\n\n$pm2 resurrect\nspec \"Should resurrect all apps\"\n\nsleep 0.5\nshould 'should have resurrected all processes' 'restart_time' 8\n\n\n\n$pm2 delete all\nspec \"Should delete all processes\"\n\nsleep 0.5\nshould 'should have deleted process' 'restart_time' 0\n\n$pm2 kill\nspec \"Should kill daemon\"\n"
  },
  {
    "path": "test/e2e/cli/cli-actions-2.sh",
    "content": "#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\n\ncd $file_path\n\n############# Start / Stop / Restart\necho \"---- Start an app, stop it, if state stopped and started, restart stopped app\"\n$pm2 start echo.js\nspec \"Should start an app by script.js\"\n$pm2 stop echo.js\nspec \"Should stop an app by script.js\"\n$pm2 restart echo.js\nspec \"Should restart an app by script.js (TRANSITIONAL STATE)\"\n\n############### Start edge case\n\n$pm2 delete all\n\necho \"Start application with filename starting with a numeric\"\n$pm2 start 001-test.js\nshould 'should app be online' 'online' 1\n$pm2 stop 001-test\nshould 'should app be stopped' 'stopped' 1\n$pm2 restart 001-test\nshould 'should app be online once restart called' 'online' 1\n\n\n############## PID\n\n$pm2 delete all\n$pm2 start 001-test.js --name \"test\"\nshould 'should app be online' 'online' 1\n$pm2 pid > /tmp/pid-tmp\n$pm2 pid test\n\n###############\n\n$pm2 delete all\necho \"Start application with filename starting with a numeric\"\n$pm2 start throw-string.js -l err-string.log --merge-logs --no-automation\n>err-string.log\nsleep 1\ngrep 'throw-string.js' err-string.log\nspec \"Should have written raw stack when throwing a string\"\n\n####\n\n$pm2 delete all\n\n$pm2 start echo.js --name gege\nshould 'should app be online' 'online' 1\n$pm2 stop gege\nshould 'should app be stopped' 'stopped' 1\n$pm2 restart gege\nshould 'should app be online once restart called' 'online' 1\n\n###############\n$pm2 delete all\n\necho \"---- BY_NAME Start an app, stop it, if state stopped and started, restart stopped app\"\n\n$pm2 start echo.js --name gege\nshould 'should app be online' 'online' 1\n$pm2 stop gege\nshould 'should app be stopped' 'stopped' 1\n$pm2 restart gege\nshould 'should app be online once restart called' 'online' 1\n\n###############\n$pm2 delete all\n\necho \"Start an app, start it one more time, if started, throw message\"\n$pm2 start echo.js\n$pm2 start echo.js\nispec \"Should not re start app\"\n\n########### DELETED STUFF BY ID\n$pm2 delete all\n\n$pm2 start echo.js\n$pm2 delete 0\nshould 'should has been deleted process by id' \"name: 'echo'\" 0\n\n########### DELETED STUFF BY NAME\n$pm2 delete all\n\n$pm2 start echo.js --name test\n$pm2 delete test\nshould 'should has been deleted process by name' \"name: 'test'\" 0\n\n########### DELETED STUFF BY SCRIPT\n$pm2 delete all\n\n$pm2 start echo.js\n$pm2 delete echo.js\n$pm2 list\nshould 'should has been deleted process by script' \"name: 'echo'\" 0\n\n######## Actions on app name as number (#1937)\n$pm2 delete all\n$pm2 start echo.js --name \"455\"\nshould 'should restart processes' 'restart_time: 0' 1\n$pm2 restart 455\nshould 'should restart processes' 'restart_time: 1' 1\n$pm2 restart 0\nshould 'should restart processes' 'restart_time: 2' 1\n$pm2 stop 455\nshould 'should app be stopped' 'stopped' 1\n$pm2 delete 455\nshould 'should has been deleted process by id' \"name: '455'\" 0\n\n$pm2 kill\n########### OPTIONS OUTPUT FILES\n$pm2 delete all\n\n$pm2 start echo.js -o outech.log -e errech.log --name gmail -i 2\nsleep 2\ncat outech-0.log > /dev/null\nspec \"file outech-0.log exist\"\ncat errech-0.log > /dev/null\nspec \"file errech-0.log exist\"\n\n########### Stdout / Stderr\n\nrm stdout-stderr.log\n$pm2 start stdout-stderr.js -l stdout-stderr.log --merge-logs\nsleep 2\ncat stdout-stderr.log | grep \"outwrite\"\nspec \"stdout written\"\ncat stdout-stderr.log | grep \"outcb\"\nspec \"stdout cb written\"\ncat stdout-stderr.log | grep \"errwrite\"\nspec \"stderr written\"\ncat stdout-stderr.log | grep \"errcb\"\nspec \"stderr cb written\"\n\n$pm2 delete all\n\n## #2350 verify all script have been killed\n$pm2 start python-script.py\n$pm2 start echo.js\nshould 'should app be online' 'online' 2\n\nkill `cat ~/.pm2/pm2.pid`\nspec \"should have killed pm2\"\n\nsleep 3\n# pgrep \"python\"\n# ispec \"should python script be killed\"\n"
  },
  {
    "path": "test/e2e/cli/dump.sh",
    "content": "#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\ncd $file_path\n\n$pm2 start echo.js -i 4\nspec \"should start 4 processes\"\nshould 'should have 4 apps started' 'online' 4\n\nrm -f ~/.pm2/dump.pm2 ~/.pm2/dump.pm2.bak\n$pm2 save\nspec \"should save process list\"\nls ~/.pm2/dump.pm2\nspec \"dump file should exist\"\nls ~/.pm2/dump.pm2.bak\nispec \"dump backup file should not exist\"\n\n$pm2 save\nspec \"should save and backup process list\"\nls ~/.pm2/dump.pm2\nspec \"dump file should exist\"\nls ~/.pm2/dump.pm2.bak\nspec \"dump backup file should exist\"\n"
  },
  {
    "path": "test/e2e/cli/ecosystem.e2e.sh",
    "content": "#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\n\ncd $file_path/ecosystem\n\n$pm2 start ecosystem.config.js\n"
  },
  {
    "path": "test/e2e/cli/env-refresh.sh",
    "content": "#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\ncd $file_path\n\necho -e \"\\033[1mENV REFRESH\\033[0m\"\n\n#\n# REFRESH with Restart via CLI\n#\nTEST_VARIABLE='hello1' $pm2 start env.js -o out-env.log --merge-logs --name \"env\"\n>out-env.log\n\nsleep 0.5\ngrep \"hello1\" out-env.log &> /dev/null\nspec \"should contain env variable\"\n\nTEST_VARIABLE='89hello89' $pm2 restart env --update-env\n\nsleep 1.0\ngrep \"89hello89\" out-env.log &> /dev/null\nspec \"should contain refreshed environment variable\"\n\n>out-env.log\nTEST_VARIABLE=\"CLUNEWSTER\" $pm2 restart env\nsleep 0.5\ngrep \"89hello89\" out-env.log &> /dev/null\nspec \"should not change environment (--skip-env)\"\n\n$pm2 delete all\n\n#\n# Cluster mode\n#\n>out-env.log\n$pm2 start env.js -o out-env.log --merge-logs\nsleep 1\ngrep \"undefined\" out-env.log &> /dev/null\nspec \"should contain nothing\"\n\n>out-env.log\nTEST_VARIABLE=\"CLUSTER\" $pm2 reload env --update-env\nsleep 1\ngrep \"CLUSTER\" out-env.log &> /dev/null\nspec \"should contain CLUSTER\"\n\n>out-env.log\nTEST_VARIABLE=\"CLUNEWSTER\" $pm2 reload env\nsleep 1\ngrep \"CLUSTER\" out-env.log &> /dev/null\nspec \"should contain not change environment (--skip-env)\"\n\n#\n# REFRESH with Restart via JSON\n#\n\n$pm2 start env.json\n>out-env.log\n\nsleep 0.5\ngrep \"YES\" out-env.log &> /dev/null\nspec \"should contain env variable\"\n\n\n$pm2 restart env-refreshed.json\n>out-env.log\n\nsleep 0.5\ngrep '{\"HEYYYY\":true}' out-env.log &> /dev/null\nspec \"should contain refreshed env variable via json\"\n\n\n$pm2 start env-ecosystem.json --env production\n>out-env.log\n\nsleep 0.5\ngrep \"No worries!\" out-env.log &> /dev/null\nspec \"should use deploy.production.env.TEST_VARIABLE\"\n\n\n$pm2 kill\n\n# Bun edit require('module').globalPaths does not return paths\nif [ \"$IS_BUN\" = false ]; then\n    $pm2 l\n    NODE_PATH='/test' $pm2 start local_require.js\n    should 'should have loaded the right globalPaths' 'restart_time: 0' 1\n\n    $pm2 kill\n    $pm2 l\n    NODE_PATH='/test2' $pm2 start local_require.js -i 1\n    should 'should have loaded the right globalPaths' 'restart_time: 0' 1\nfi\n"
  },
  {
    "path": "test/e2e/cli/extra-lang.sh",
    "content": "#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\n\ncd $file_path/extra-lang\n\nwhich php\nspec \"should php cli be installed\"\nwhich python3\nspec \"should python cli be installed\"\n\n#\n# JSON\n#\n$pm2 start apps.json\nshould 'should have started 2 apps' 'online' 2\n\n>python-app.log\n>php-app-out.log\n>php-error.log\n\nsleep 1\n\ngrep \"Python\" python-app.log\nspec \"Python script should have written data in log file\"\n\ngrep \"PHP\" php-app-out.log\nspec \"PHP script should have written data in log file\"\n\ngrep \"ERROR\" php-error.log\nspec \"PHP script should have written data in error log file\"\n\n# Switch to production environment\n$pm2 restart apps.json --env production\nshould 'should have started 2 apps' 'online' 2\n\n>python-app.log\n>php-app-out.log\n>php-error.log\n\nsleep 1\n\ngrep \"PythonProduction\" python-app.log\nspec \"Python script should have written data in log file (Production mode)\"\n\n#\n# CLI\n#\n$pm2 delete all\n\n>cli-python.log\n\n$pm2 start echo.py --interpreter=\"/usr/bin/python3\" --interpreter-args=\"-u\" --log=\"cli-python.log\" --merge-logs\nshould 'should have started 1 app' 'onl\\ine' 1\nsleep 1\ngrep \"RAWPython\" cli-python.log\nspec \"Python script should have written data in log file\"\n"
  },
  {
    "path": "test/e2e/cli/fork.sh",
    "content": "#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\n\ncd $file_path\n\n########### Fork mode\n$pm2 start echo.js -x\nshould 'should start app in fork mode' 'fork_mode' 1\n\n$pm2 restart echo.js\nshould 'should has restarted app' 'restart_time: 1' 1\n\n########### Fork mode\n$pm2 kill\n\n$pm2 start bashscript.sh\nshould 'should start app in fork mode' 'fork_mode' 1\n\n########### Auto Detective Interpreter In Fork mode\n\n### Dump resurrect should be ok\n$pm2 dump\n\n$pm2 kill\n\n#should 'should has forked app' 'fork' 0\n\n$pm2 resurrect\nshould 'should has forked app' 'fork_mode' 1\n\n## Delete\n\n$pm2 list\n\n$pm2 delete 0\nshould 'should has delete process' 'fork_mode' 0\n"
  },
  {
    "path": "test/e2e/cli/mjs.sh",
    "content": "#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\n\ncd $file_path/mjs\n\n# Activate test only for Node > 9.0.0\nnode -e \"require('semver').gte(process.versions.node, '9.0.0') ? process.exit(0) : process.exit(1)\"\n[ $? -eq 0 ] || exit 0\n\n$pm2 start --node-args=\"--experimental-modules\" index.mjs -o outech.log -e errech.log\n>outech.log\n>errech.log\nsleep 1\nshould 'should app be online in fork mode with MJS support' 'online' 1\n\n$pm2 delete all\n\n$pm2 start --node-args=\"--experimental-modules\" -i 2 index.mjs\nsleep 1\nshould 'should app be online in cluster mode with MJS support' 'online' 2\n"
  },
  {
    "path": "test/e2e/cli/monit.sh",
    "content": "#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\n\ncd $file_path\n\n$pm2 link xxx aaa\n\n$pm2 start http.js -i 4\nspec \"should start 4 processes\"\n\n$pm2 monitor 0\nshould 'should monitoring flag enabled (id)' '_km_monitored: true' 1\n\n$pm2 unmonitor 0\nshould 'should monitoring flag disabled (id)' '_km_monitored: false' 1\n\n$pm2 monitor http\nshould 'should monitoring flag enabled (name)' '_km_monitored: true' 4\n\n$pm2 unmonitor http\nshould 'should monitoring flag disabled (name)' '_km_monitored: false' 4\n\n$pm2 monitor all\nshould 'should monitoring flag enabled ' '_km_monitored: true' 4\n\n$pm2 unmonitor all\nshould 'should monitoring flag disabled (name)' '_km_monitored: false' 4\n"
  },
  {
    "path": "test/e2e/cli/multiparam.sh",
    "content": "#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\n\ncd $file_path\n\n## Start\n$pm2 start child.js echo.js server.js\nshould 'should app be online' 'online' 3\n\n## Restart\n$pm2 restart child echo server\nshould 'should app be online' 'online' 3\nshould 'should all script been restarted one time' 'restart_time: 1' 3\n\n## Stop\n$pm2 stop child echo server\nshould 'should app be stopped' 'stopped' 3\n\n## Delete\n$pm2 delete child echo server\nshouldnot 'should app be deleted' 'stopped' 3\n"
  },
  {
    "path": "test/e2e/cli/operate-regex.sh",
    "content": "#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\n\ncd $file_path\n\n$pm2 start echo.js --name \"echo-3\"\n$pm2 start echo.js --name \"echo-1\"\n$pm2 start echo.js --name \"echo-2\"\n\nsleep 0.5\n\nshould 'should have started 3 apps' 'online' 3\n\n$pm2 stop /echo-[1,2]/\n\nshould 'should have stopped 2 apps' 'stopped' 2\nshould 'only one app should still be online' 'online' 1\n\n$pm2 stop /echo-3/\nshould 'should have stopped 1 apps' 'online' 0\n\n$pm2 restart /echo-[1,2]/\n\nshould 'should have restarted 2 apps' 'online' 2\n"
  },
  {
    "path": "test/e2e/cli/piped-config.sh",
    "content": "\n#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\n\ncd $file_path\n\n############# TEST\n\ncat all.json | $pm2 start -\nshould 'should start processes' 'online' 6\n\ncat all.json | $pm2 delete -\nshould 'should delete all processes' 'name' 0\n"
  },
  {
    "path": "test/e2e/cli/plus.sh",
    "content": "#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\n\ncd $file_path\n\n$pm2 start echo.js\n$pm2 prettylist | grep \"km_link: false\"\nspec \"should km_link not be enabled\"\n\n$pm2 plus alcz82ewyhy2va6 litfrsovr52celr --install-all\n\nshould 'have started 3 apps' 'online' 3\nshould 'all application be monitored' 'km_link: true' 3\n\n$pm2 plus delete\n\nshould 'have started 1 apps' 'online' 1\n$pm2 prettylist | grep \"km_link: false\"\nspec \"should km_link be disabled\"\n"
  },
  {
    "path": "test/e2e/cli/python-support.sh",
    "content": "#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\n\ncd $file_path/extra-lang\n\n#\n# Config file\n#\n\n$pm2 start app-python.config.js --only 'echo-python-1'\nshould 'should mode be fork' 'fork_mode' 1\nshould 'should have started 1 apps' 'online' 1\n\n$pm2 delete all\n\n# Check with multi instances\n$pm2 start app-python.config.js --only 'echo-python-max'\nshould 'should mode be fork' 'fork_mode' 4\nshould 'should have started 4 apps' 'online' 4\n\n# Should keep same params on restart\n$pm2 restart all\nshould 'should have restarted processes' 'restart_time: 1' 4\nshould 'should mode be fork' 'fork_mode' 4\n\n$pm2 delete all\n\n#\n# CLI\n#\n\n$pm2 start echo.py\nshould 'should mode be fork' 'fork_mode' 1\nshould 'should have started 1 apps' 'online' 1\n\n$pm2 delete all\n\n$pm2 start echo.py -i 4\nshould 'should mode be fork' 'fork_mode' 4\nshould 'should have started 4 apps' 'online' 4\n\n$pm2 restart all\nshould 'should have restarted processes' 'restart_time: 1' 4\nshould 'should mode be fork' 'fork_mode' 4\n"
  },
  {
    "path": "test/e2e/cli/reload.sh",
    "content": "#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\n\ncd $file_path\n$pm2 start delayed_exit.js -i 2\nshould 'should start processes' 'online' 2\nshould 'should app be in cluster mode' \"exec_mode: 'cluster_mode'\" 2\nOUT_LOG=`$pm2 prettylist | grep -m 1 -E \"pm_out_log_path:\" | sed \"s/.*'\\([^']*\\)',/\\1/\"`\n> $OUT_LOG\n\n$pm2 reload delayed_exit\n\nsleep 1\n\nOUT=`grep \"SIGINT\" \"$OUT_LOG\" | wc -l`\n[ $OUT -eq 1 ] || fail \"Signal not received by the process name\"\nsuccess \"Processes sucessfully receives the SIGINT signal\"\n\n$pm2 kill\n\n$pm2 start delayed_exit.js\nshould 'should start processes' 'online' 1\n$pm2 stop delayed_exit.js\nsleep 3\nshould 'should stop processes' 'stopped' 1\n$pm2 restart delayed_exit.js\nshould 'should restart processes' 'restart_time: 0' 1\n$pm2 restart delayed_exit.js\nsleep 3\nshould 'should restart processes' 'restart_time: 1' 1\n$pm2 kill\n\n# $pm2 start delayed_exit.js -i 2\n# should 'should start processes' 'online' 2\n# $pm2 stop delayed_exit.js\n# sleep 3\n# should 'should stop processes' 'stopped' 2\n# $pm2 restart delayed_exit.js\n# should 'should restart processes' 'restart_time: 0' 2\n# $pm2 restart delayed_exit.js\n# should 'should restart processes' 'restart_time: 1' 2\n# $pm2 reload delayed_exit.js\n# should 'should restart processes' 'restart_time: 2' 2\n# $pm2 gracefulReload delayed_exit.js\n# should 'should restart processes' 'restart_time: 3' 2\n# $pm2 kill\n\n$pm2 start child.js -i 4\nsleep 0.5\nshould 'should start processes' 'online' 4\n$pm2 restart all\nshould 'should restarted be one for all' 'restart_time' 4\n$pm2 restart child.js\nshould 'should restart a second time (BY SCRIPT NAME)' 'restart_time: 2' 4\n\n$pm2 restart child\nshould 'should restart a third time (BY NAME)' 'restart_time: 3' 4\nsleep 0.5\n$pm2 reload all\nsleep 0.5\nshould 'should RELOAD a fourth time' 'restart_time: 4' 4\n\n############### CLUSTER STUFF\n$pm2 kill\n\n\n$pm2 start child.js -i 4\nshould 'should start processes' 'online' 4\n\n$pm2 start network.js -i 4\nshould 'should has 8 online apps' 'online' 8\n\nshould 'should has 4 api online' 'network.js' 4\nshould 'should has 4 child.js online' 'child.js' 4\n\n$pm2 reload all\nshould 'should reload all' 'restart_time' 8\n\n$pm2 reload child.js\nshould 'should reload only child.js' 'restart_time: 2' 4\n\n$pm2 reload network.js\nshould 'should reload network.js' 'restart_time: 2' 8\n\n############### BLOCKING STUFF\n\n# this is not a networked application\n$pm2 start echo.js\nshould 'should has 8 online apps' 'online' 9\n\n$pm2 reload echo\nshould 'should not hang and fallback to restart behaviour' 'restart_time' 9\n\n\n############### NO-AUTORESTART\n$pm2 kill\n\n$pm2 start killtoofast.js --no-autorestart\nshould 'should not restart' 'restart_time: 0' 1\n\n$pm2 delete all\n$pm2 start no-restart.json\nshould 'should not restart' 'restart_time: 0' 1\n\n############### STOP EXIT CODES\n$pm2 kill\n\n$pm2 start exitcode42.js --stop-exit-codes 42\nsleep 2\nshould 'should not restart' 'restart_time: 0' 1\n\n$pm2 delete all\n$pm2 start exitcode42.js --stop-exit-codes 34\nsleep 1\nshouldnot 'should restart' 'restart_time: 0' 1\n$pm2 kill\n\n$pm2 start exitcode42.js --stop-exit-codes 3\nsleep 1\nshouldnot 'should restart processes' 'restart_time: 0' 1\n$pm2 kill\n\n$pm2 delete all\n$pm2 start stop-exit-codes.json\nsleep 0.5\nshould 'should not restart' 'restart_time: 0' 1\n\n\n############### Via ENV: SEND() instead of KILL()\n$pm2 kill\nexport PM2_KILL_USE_MESSAGE='true'\n\n$pm2 start signal-send.js\nshould 'should start processes' 'online' 1\n\nOUT_LOG=`$pm2 prettylist | grep -m 1 -E \"pm_out_log_path:\" | sed \"s/.*'\\([^']*\\)',/\\1/\"`\n> $OUT_LOG\n\n$pm2 reload signal-send.js\nsleep 1\n\nOUT=`grep \"shutdown\" \"$OUT_LOG\" | wc -l`\n[ $OUT -eq 1 ] || fail \"Signal not received by the process name\"\nsuccess \"Processes sucessfully receives the signal\"\n\nunset PM2_KILL_USE_MESSAGE\n\n############### VIA --shutdown-with-message\n$pm2 kill\n\n$pm2 start signal-send.js --shutdown-with-message\nshould 'should start processes' 'online' 1\n\nOUT_LOG=`$pm2 prettylist | grep -m 1 -E \"pm_out_log_path:\" | sed \"s/.*'\\([^']*\\)',/\\1/\"`\n> $OUT_LOG\n\n$pm2 reload signal-send.js\nsleep 1\n\nOUT=`grep \"shutdown\" \"$OUT_LOG\" | wc -l`\n[ $OUT -eq 1 ] || fail \"Signal not received by the process name\"\nsuccess \"Processes sucessfully receives the signal\"\n"
  },
  {
    "path": "test/e2e/cli/reset.sh",
    "content": "\n#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\n\ncd $file_path\n\necho \"################## RESET ###################\"\n\n#\n# BY ID\n#\n$pm2 start echo.js\nshould 'should restarted be one for all' 'restart_time: 0' 1\n\n$pm2 restart 0\nshould 'should process restarted' 'restart_time: 1' 1\n\n$pm2 reset 0\nshould 'should process reseted' 'restart_time: 0' 1\n\n#\n# BY NAME\n#\n$pm2 start echo.js -i 4 -f\nshould 'should restarted be one for all' 'restart_time: 0' 5\n\n$pm2 restart echo\nshould 'should process restarted' 'restart_time: 1' 5\n\n$pm2 reset echo\nshould 'should process reseted' 'restart_time: 0' 5\n\n\n#\n# ALL\n#\n$pm2 restart all\n$pm2 restart all\n$pm2 restart all\nshould 'should process restarted' 'restart_time: 3' 5\n\n$pm2 reset all\nshould 'should process reseted' 'restart_time: 0' 5\n\n#\n# Restart delay test\n#\n\n$pm2 delete all\n$pm2 start killtoofast.js --restart-delay 5000\nshould 'should process not have been restarted yet' 'restart_time: 0' 1\n\n$pm2 kill\n"
  },
  {
    "path": "test/e2e/cli/resurrect.sh",
    "content": "#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\ncd $file_path\n\n$pm2 start echo.js -i 4\nspec \"should start 4 processes\"\nshould 'should have 4 apps started' 'online' 4\n\n$pm2 save\n$pm2 resurrect\nspec \"should resurrect from dump\"\nshould 'should have still 4 apps started' 'online' 4\n\n$pm2 save\n$pm2 delete all\necho \"[{\" > ~/.pm2/dump.pm2\n$pm2 resurrect\nspec \"should resurrect from backup if dump is broken\"\nls ~/.pm2/dump.pm2\nispec \"should delete broken dump\"\nshould 'should have still 4 apps started' 'online' 4\n\n$pm2 delete all\n$pm2 resurrect\nspec \"should resurrect from backup if dump is missing\"\nshould 'should have still 4 apps started' 'online' 4\n"
  },
  {
    "path": "test/e2e/cli/right-exit-code.sh",
    "content": "#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\n\ncd $file_path\n\n$pm2 kill\n\n$pm2 restart BULLSHIT\nispec \"Unknown process = error exit\"\n\n$pm2 restart 666\nispec \"Unknown process = error exit\"\n\n$pm2 restart all\nispec \"No process = error exit\"\n\n$pm2 stop all\nispec \"No process = error exit\"\n\n$pm2 delete 10\nispec \"No process = error exit\"\n\n$pm2 delete toto\nispec \"No process = error exit\"\n"
  },
  {
    "path": "test/e2e/cli/serve.sh",
    "content": "#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\n\ncd $file_path/serve\nPORT=8081\nPORT_2=8082\necho \"################## PM2 SERVE ###################\"\n\n$pm2 serve --port $PORT\nshould 'should have started serving dir' 'online' 1\n\ncurl http://localhost:$PORT/ > /tmp/tmp_out.txt\nOUT=`cat /tmp/tmp_out.txt | grep -o \"good shit\" | wc -l`\n[ $OUT -eq 1 ] || fail \"should have served index file under /\"\nsuccess \"should have served index file under /\"\n\ncurl http://localhost:$PORT/index.html > /tmp/tmp_out.txt\nOUT=`cat /tmp/tmp_out.txt | grep -o \"good shit\" | wc -l`\n[ $OUT -eq 1 ] || fail \"should have served index file under /index.html\"\nsuccess \"should have served index file under /index.html\"\n\necho \"Shutting down the server\"\n$pm2 delete all\n\ncurl http://localhost:$PORT/index.html > /tmp/tmp_out.txt\nOUT=`cat /tmp/tmp_out.txt | grep -o \"good shit\" | wc -l`\n[ $OUT -eq 0 ] || fail \"should be offline\"\nsuccess \"should be offline\"\n\necho \"testing SPA\"\n$pm2 serve . $PORT --spa\nshould 'should have started serving dir' 'online' 1\n\ncurl http://localhost:$PORT/ > /tmp/tmp_out.txt\nOUT=`cat /tmp/tmp_out.txt | grep -o \"good shit\" | wc -l`\n[ $OUT -eq 1 ] || fail \"should have served index file under /index.html\"\nsuccess \"should have served index file under /index.html\"\n\ncurl http://localhost:$PORT/index.html > /tmp/tmp_out.txt\nOUT=`cat /tmp/tmp_out.txt | grep -o \"good shit\" | wc -l`\n[ $OUT -eq 1 ] || fail \"should have served index file under /index.html\"\nsuccess \"should have served index file under /index.html\"\n\ncurl http://localhost:$PORT/other.html > /tmp/tmp_out.txt\nOUT=`cat /tmp/tmp_out.txt | wc -l`\n[ $OUT -eq 2 ] || fail \"should have served file under /other.html\"\nsuccess \"should have served file under /other.html\"\n\ncurl http://localhost:$PORT/mangezdespommes/avecpepin/lebref > /tmp/tmp_out.txt\nOUT=`cat /tmp/tmp_out.txt | grep -o \"good shit\" | wc -l`\n[ $OUT -eq 1 ] || fail \"should have served index file under /index.html\"\nsuccess \"should have served index file under /index.html\"\n\ncurl http://localhost:$PORT/mangezdespommes/avecpepin/lebref/other.html > /tmp/tmp_out.txt\nOUT=`cat /tmp/tmp_out.txt | wc -l`\n[ $OUT -eq 2 ] || fail \"should have served file under /other.html\"\nsuccess \"should have served file under /other.html\"\n\necho \"Shutting down the server\"\n$pm2 delete all\n\necho \"testing basic auth\"\n$pm2 serve . $PORT --basic-auth-username user --basic-auth-password pass\nshould 'should have started serving dir' 'online' 1\n\ncurl http://user:pass@localhost:$PORT/index.html > /tmp/tmp_out.txt\nOUT=`cat /tmp/tmp_out.txt | grep -o \"good shit\" | wc -l`\n[ $OUT -eq 1 ] || fail \"should have served index file under /index.html\"\nsuccess \"should have served index file under /index.html\"\n\necho \"Shutting down the server\"\n$pm2 delete all\n\necho \"Testing with static ecosystem\"\n\n$pm2 start ecosystem-serve.json\nshould 'should have started serving dir' 'online' 1\n\ncurl http://user:pass@localhost:8081/index.html > /tmp/tmp_out.txt\nOUT=`cat /tmp/tmp_out.txt | grep -o \"good shit\" | wc -l`\n[ $OUT -eq 1 ] || fail \"should be listening on port 8081\"\nsuccess \"should be listening on port 8081\"\n\ncurl http://user:pass@localhost:8081/mangezdesmangues/aupakistan > /tmp/tmp_out.txt\nOUT=`cat /tmp/tmp_out.txt | grep -o \"good shit\" | wc -l`\n[ $OUT -eq 1 ] || fail \"should be listening on port 8081\"\nsuccess \"should be listening on port 8081\"\n\necho \"Shutting down the server\"\n$pm2 delete all\n\n$pm2 serve . $PORT_2\nshould 'should have started serving dir' 'online' 1\n\ncurl http://localhost:$PORT_2/index.html > /tmp/tmp_out.txt\nOUT=`cat /tmp/tmp_out.txt | grep -o \"good shit\" | wc -l`\n[ $OUT -eq 1 ] || fail \"should be listening on port $PORT_2\"\nsuccess \"should be listening on port $PORT_2\"\n\nnode -e \"require('semver').lt(process.versions.node, '6.0.0') ? process.exit(0) : process.exit(1)\"\n[ $? -eq 1 ] || exit 0\n\n$pm2 delete all\n\n$pm2 serve . $PORT_2 --name frontend\nshould 'should have started serving dir' 'online' 1\nshould 'should have custom name' 'frontend' 7\n\ncurl http://localhost:$PORT_2/index.html > /tmp/tmp_out.txt\nOUT=`cat /tmp/tmp_out.txt | grep -o \"good shit\" | wc -l`\n[ $OUT -eq 1 ] || fail \"should be listening on port $PORT_2\"\nsuccess \"should be listening on port $PORT_2\"\n\ncurl http://localhost:$PORT_2/yolo.html > /tmp/tmp_out.txt\nOUT=`cat /tmp/tmp_out.txt | grep -o \"your file doesnt exist\" | wc -l`\n[ $OUT -eq 1 ] || fail \"should have served custom 404 file\"\nsuccess \"should have served custom 404 file\"\n\n$pm2 delete all\n\n$pm2 start ecosystem.json\nshould 'should have started serving dir' 'online' 1\n\ncurl http://localhost:8081/index.html > /tmp/tmp_out.txt\nOUT=`cat /tmp/tmp_out.txt | grep -o \"good shit\" | wc -l`\n[ $OUT -eq 1 ] || fail \"should be listening on port 8081\"\nsuccess \"should be listening on port 8081\"\n\n$pm2 stop ecosystem.json\n\ncurl http://localhost:8081/index.html > /tmp/tmp_out.txt\nOUT=`cat /tmp/tmp_out.txt | grep -o \"good shit\" | wc -l`\n[ $OUT -eq 0 ] || fail \"should be offline\"\nsuccess \"should be offline\"\n"
  },
  {
    "path": "test/e2e/cli/smart-start.sh",
    "content": "\n#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\n\ncd $file_path\n\n#\n# Test for SMART start\n#\n\n$pm2 start echo.js\nshould 'process should have been started' 'restart_time: 0' 1\nshould 'process should have been started' 'online' 1\n\n$pm2 stop echo\nshould 'process should have been started' 'stopped' 1\n\n$pm2 start echo\nshould 'process should have been started' 'online' 1\n\n$pm2 start echo\nshould 'process should have been started' 'restart_time: 1' 1\nshould 'process should have been started' 'online' 1\n\n$pm2 start 0\nshould 'process should have been started' 'restart_time: 2' 1\nshould 'process should have been started' 'online' 1\n\n# $pm2 stop echo\n# should 'process should have been started' 'stopped' 1\n\n# $pm2 start all\n# should 'process should have been started' 'restart_time: 2' 1\n# should 'process should have been started' 'online' 1\n"
  },
  {
    "path": "test/e2e/cli/sort.sh",
    "content": "#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\n\ncd $file_path/sort\n\n$pm2 start http.js\n$pm2 start other.js\n\n\n$pm2 list --sort=name > /tmp/tmp_out.txt\n\nOUT=`cat /tmp/tmp_out.txt | grep -v \"npm\" | grep -no \"http\" -m 1 | cut -f1 -d:`\nOUT2=`cat /tmp/tmp_out.txt | grep -v \"npm\" | grep -no \"other\" -m 1 | cut -f1 -d:`\n\n[ $OUT -lt $OUT2 ] || fail \"should sort app by name (asc)\"\nsuccess \"should sort app by name (asc)\"\n\n$pm2 list --sort=name:desc > /tmp/tmp_out.txt\n\nOUT=`cat /tmp/tmp_out.txt | grep -v \"npm\" | grep -no \"http\" -m 1 | cut -f1 -d:`\nOUT2=`cat /tmp/tmp_out.txt | grep -v \"npm\" | grep -no \"other\" -m 1 | cut -f1 -d:`\n\n[ $OUT -gt $OUT2 ] || fail \"should sort app by name (desc)\"\nsuccess \"should sort app by name (desc)\"\n\n\n$pm2 list --sort=id > /tmp/tmp_out.txt\n\nOUT=`cat /tmp/tmp_out.txt | grep -v \"npm\" | grep -no \"http\" -m 1 | cut -f1 -d:`\nOUT2=`cat /tmp/tmp_out.txt | grep -v \"npm\" | grep -no \"other\" -m 1 | cut -f1 -d:`\n\n[ $OUT -lt $OUT2 ] || fail \"should sort app by id (asc)\"\nsuccess \"should sort app by id (asc)\"\n\n$pm2 list --sort=id:desc > /tmp/tmp_out.txt\n\nOUT=`cat /tmp/tmp_out.txt | grep -v \"npm\" | grep -no \"http\" -m 1 | cut -f1 -d:`\nOUT2=`cat /tmp/tmp_out.txt | grep -v \"npm\" | grep -no \"other\" -m 1 | cut -f1 -d:`\n\n[ $OUT -gt $OUT2 ] || fail \"should sort app by id (desc)\"\nsuccess \"should sort app by id (desc)\"\n"
  },
  {
    "path": "test/e2e/cli/start-app.sh",
    "content": "#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\n\ncd $file_path/start-app\n\n#\n# Direct command\n#\n$pm2 delete all\n\nif [ \"$IS_BUN\" = true ]; then\n    $pm2 start \"bun -e 'setTimeout(function() { }, 100000); console.log(process.env.TEST)'\" -l test.log --merge-logs\nelse\n    $pm2 start \"node -e 'setTimeout(function() { }, 100000); console.log(process.env.TEST)'\" -l test.log --merge-logs\nfi\n\nshould 'should have started command' 'online' 1\nshould 'should have not been restarted' 'restart_time: 0' 1\n\ncat test.log | grep \"undefined\" &> /dev/null\nsleep 1\nspec \"should have printed undefined env var\"\n\nTEST='ok' $pm2 restart 0 --update-env\ncat test.log | grep \"ok\" &> /dev/null\n\nsleep 1\nshould 'should have started command' 'online' 1\nshould 'should have not been restarted' 'restart_time: 1' 1\nspec \"should have printed undefined env var\"\n\n#\n# Direct command via Conf file\n#\n$pm2 delete all\n\nif [ \"$IS_BUN\" = true ]; then\n    $pm2 start ecosystem-bun.config.js\nelse\n    $pm2 start ecosystem.config.js\nfi\n\nshould 'should have started command' 'online' 1\nshould 'should have not been restarted' 'restart_time: 0' 1\ncat test-conf.log | grep \"test_val\" 2> /dev/null\nspec \"should have printed the test_val\"\n\n#\n# Compile C Program\n#\ncd $file_path/c-compile\n$pm2 start \"cc hello.c; ./a.out\" -l c-log.log --merge-logs\nsleep 2\ncat c-log.log | grep \"Hello World\" &> /dev/null\nspec \"should have printed compiled output\"\n"
  },
  {
    "path": "test/e2e/cli/startOrX.sh",
    "content": "#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\n\necho -e \"\\033[1mStartOrX.sh:\\033[0m\"\n\ncd $file_path\n\n$pm2 startOrRestart all.json\n\nshould 'should start processes' 'online' 6\n\n$pm2 startOrRestart all.json\n\nshould 'should has restarted app' 'restart_time: 1' 6\n\n$pm2 startOrReload all.json\n\nshould 'should has reloaded app' 'restart_time: 2' 6\n\n# slow\n# $pm2 startOrGracefulReload all.json\n# should 'should has graceful reloaded app' 'restart_time: 3' 8\n"
  },
  {
    "path": "test/e2e/cli/watch.sh",
    "content": "#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\n\ncd $file_path/watch\n\n$pm2 start http.js --watch\nshould '1 should watch be false' 'watch: true' 1\n\n$pm2 stop http\nshould '2 should watch be false when stopping app' 'watch: false' 1\n\n$pm2 start http\nshould '3 should watch be false when starting app' 'watch: false' 1\n\n$pm2 restart http.js\nshould '4 should restart with watch and should be false' 'watch: false' 1\n\n$pm2 delete http\n\n$pm2 start http.js\nshould '5 should watch be false' 'watch: false' 1\n\n$pm2 stop http.js\nshould '5 should watch be false' 'watch: false' 1\n\n$pm2 start http.js --watch\nshould '7 should restart with watch and should be true' 'watch: true' 1\n\n$pm2 delete all\n\n$pm2 start http.js\nshould '8 should watch be false' 'watch: false' 1\n\n$pm2 restart http.js\nshould '6 should watch be false' 'watch: false' 1\n\n$pm2 restart http.js --watch\nshould '7 should restart with watch and should be true' 'watch: true' 1\n\n$pm2 restart http.js --watch\nshould '8 should restart with watch and should be false' 'watch: false' 1\n\n$pm2 restart http.js --watch\nshould '9 should restart with watch and should be true' 'watch: true' 1\n\n$pm2 stop http.js\nshould '10 should stop app and watch is stopped' 'watch: false' 1\n\n$pm2 restart http.js --watch\nshould '11 should restart stopped app with watch and should be true' 'watch: true' 1\n\n$pm2 restart http.js --watch\nshould '12 should restart with watch and should be false' 'watch: false' 1\n\n$pm2 kill\n\n$pm2 start app.json\nshould 'should have started 1 apps' 'online' 1\nshould 'should have restarted app by name' 'restart_time: 0' 1\nshould 'should watch be false' 'watch: false' 1\n\n$pm2 restart app.json\nshould 'should have started 1 apps' 'online' 1\nshould 'should have restarted app by name' 'restart_time: 1' 1\nshould 'should watch be false' 'watch: false' 1\n\n$pm2 stop app.json\nshould 'should have started 1 apps' 'stopped' 1\nshould 'should have restarted app by name' 'restart_time: 1' 1\nshould 'should watch be false' 'watch: false' 1\n\n$pm2 kill\n\n$pm2 start app-watch.json\nshould 'should have started 1 apps' 'online' 1\nshould 'should have restarted app by name' 'restart_time: 0' 1\nshould 'should watch exist' 'watch: true' 1\n\n$pm2 stop app-watch.json\nshould 'should have started 1 apps' 'stopped' 1\nshould 'should have restarted app by name' 'restart_time: 0' 1\nshould 'should watch exist' 'watch: false' 1\n\n$pm2 restart app-watch.json\nshould 'should have started 1 apps' 'online' 1\nshould 'should have restarted app by name' 'restart_time: 0' 1\nshould 'should watch exist' 'watch: true' 1\n\n$pm2 restart all\nshould 'should have started 1 apps' 'online' 1\nshould 'should have restarted app by name' 'restart_time: 1' 1\nshould 'should watch exist' 'watch: true' 1\n\n$pm2 stop all\nshould 'should have started 1 apps' 'stopped' 1\nshould 'should have restarted app by name' 'restart_time: 1' 1\nshould 'should watch exist' 'watch: false' 1\n"
  },
  {
    "path": "test/e2e/docker.sh",
    "content": "#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\n\n# Docker should\nfunction dshould {\n    sleep 0.3\n    CID=`docker ps -lq`\n    docker exec -it $CID /bin/sh -c \"PM2_HOME=/root/.pm2-dev/ pm2 prettylist\" > /tmp/dout\n    OUT=`cat /tmp/dout | grep -o \"$2\" | wc -l`\n    [ $OUT -eq $3 ] || fail \"$1\"\n    success \"$1\"\n}\n\nfunction PRODshould {\n    sleep 0.3\n    CID=`docker ps -lq`\n    docker exec -it $CID /bin/sh -c \"pm2 prettylist\" > /tmp/dout\n    OUT=`cat /tmp/dout | grep -o \"$2\" | wc -l`\n    [ $OUT -eq $3 ] || fail \"$1\"\n    success \"$1\"\n}\n\n# Bootstrap one app\ncd $file_path/docker/expressor\n\n# Kill all running container\ndocker kill `docker ps -aq -f status=running`\nrm Dockerfile\nrm -rf node_modules\n\n################### DEV MODE\n#\n# Wrap Application in Development mode\n#\n$pm2 start process.json --dockerdaemon --container\nCID=`docker ps -lq`\n\n# Check Dockerfile generation\nls Dockerfile\nspec \"Dockerfile have been generated\"\n\ngrep \"process.json\" Dockerfile\nspec \"Right entry file\"\n\nsleep 1\n# Check running processes inside container\ndshould 'should have started 2 apps' 'online' 2\ndshould 'should the 2 apps not being restarted' 'restart_time: 0' 2\n\ndocker kill $CID\n\n#\n# Edit Dockerfile (simulate user who needs a specific library)\n#\nsed -i '' '5i\\\nRUN pm2 install pm2-server-monit\n' Dockerfile\n\n$pm2 start process.json --dockerdaemon --container\nsleep 1\ngrep \"RUN pm2 install pm2-server-monit\" Dockerfile\nspec \"Custom line should still be present\"\ndshould 'should have started 2 apps' 'online' 2\ndshould 'should the 2 apps be stable' 'restart_time: 0' 2\n\ndocker kill `docker ps -lq`\n\n################### DISTRIBUTION MODE\n#\n# Wrap Application in Distribution mode\n#\n$pm2 start process.json --container --dist --image-name pm2ns/test --dockerdaemon\nsleep 2\ngrep \"RUN pm2 install pm2-server-monit\" Dockerfile\nspec \"Custom line should still be present\"\n\nPRODshould 'should have started 3 apps (2 user app + one module)' 'online' 3\n\ndocker kill `docker ps -lq`\nrm Dockerfile\n\n# Re wrap with new generated Dockerfile\n$pm2 start process.json  --container --dist --image-name pm2ns/test --dockerdaemon\nsleep 1\nPRODshould 'should have started 2 apps' 'online' 2\nPRODshould 'should the 2 apps not being restarted' 'restart_time: 0' 2\ngrep \"pm2-docker\" Dockerfile\nspec \"pm2-docker runtime should be present\"\n\n# Exit\ndocker kill `docker ps -lq`\nrm Dockerfile\n"
  },
  {
    "path": "test/e2e/esmodule.sh",
    "content": "#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/include.sh\"\n\n# Bootstrap one app\ncd $file_path/esmodules/mjs\n\n#### FORK MODE\n\n$pm2 delete all\n\n$pm2 start index.mjs\nsleep 2\nshould 'should have detected es module via .mjs file extension and started 1 app' 'online' 1\nshould 'should have application in stable state' 'restart_time: 0' 1\n\n$pm2 restart index\nsleep 2\nshould 'should have detected es module via .mjs file extension and started 1 app' 'online' 1\nshould 'should have application in stable state' 'restart_time: 1' 1\n\n$pm2 delete all\n\ncd $file_path/esmodules/packagemodule\n\n$pm2 start index.js\nsleep 2\nshould 'should have detected es module via .mjs file extension and started 1 app' 'online' 1\nshould 'should have application in stable state' 'restart_time: 0' 1\n\n$pm2 restart index\nsleep 2\nshould 'should have detected es module via .mjs file extension and started 1 app' 'online' 1\nshould 'should have application in stable state' 'restart_time: 1' 1\n\n$pm2 save\n\n$pm2 update\n\nsleep 2\nshould 'should have detected es module via .mjs file extension and started 1 app' 'online' 1\nshould 'should have application in stable state' 'restart_time: 0' 1\n\n#### CLUSTER MODE\n\ncd $file_path/esmodules/mjs\n\n$pm2 delete all\n\n$pm2 start index.mjs -i 4\nsleep 2\nshould 'should have detected es module via .mjs file extension and started 4 apps' 'online' 4\nshould 'should have application in stable state' 'restart_time: 0' 4\n\n$pm2 restart index\nsleep 2\nshould 'should have detected es module via .mjs file extension and started 4 app' 'online' 4\nshould 'should have application in stable state' 'restart_time: 1' 4\n\n$pm2 delete all\n\ncd $file_path/esmodules/packagemodule\n\n$pm2 start index.js -i 4\nsleep 2\nshould 'should have detected es module via .mjs file extension and started 4 apps' 'online' 4\nshould 'should have application in stable state' 'restart_time: 0' 4\n\n$pm2 restart index\nsleep 2\nshould 'should have detected es module via .mjs file extension and started 4 app' 'online' 4\nshould 'should have application in stable state' 'restart_time: 1' 4\n\n$pm2 delete all\n"
  },
  {
    "path": "test/e2e/file-descriptor.sh",
    "content": "#!/usr/bin/env bash\n\n#\n# LSOF check\n#\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\n\ncd $file_path\n\necho \"################## RELOAD ###################\"\n\n# lsof -c PM2 > /tmp/no_pm2_out.dat\n\n# $pm2 list\n\n# sleep 1\n# lsof -c PM2 > /tmp/empty_pm2_out.dat\n\n# $pm2 start echo.js -i 3\n# $pm2 start killtoofast.js -i 3\n# $pm2 delete all\n\n# sleep 3\n# lsof -c PM2 > /tmp/empty_pm2_out2.dat\n\n# OUT1=`cat /tmp/empty_pm2_out.dat | wc -l`\n# OUT2=`cat /tmp/empty_pm2_out2.dat | wc -l`\n\n# if [ $OUT1 -eq $OUT2 ]; then\n#   success \"All file descriptors have been closed\"\n# else\n#   fail \"Some file descriptors are still open\"\n# fi\n\n# $pm2 start killtoofast.js -i 6\n# $pm2 kill\n\n# rm /tmp/no_pm2_out.dat\n# rm /tmp/no_pm2_out2.dat\n# rm /tmp/empty_pm2_out.dat\n# rm /tmp/empty_pm2_out2.dat\n\n# sleep 6\n> /tmp/no_pm_pm2_out.dat\n> /tmp/no_pm_pm2_out2.dat\n\nlsof -c PM2 > /tmp/no_pm2_out2.dat\ndiff /tmp/no_pm2_out.dat /tmp/no_pm2_out2.dat\n\nif [ $? == \"0\" ]; then\n  success \"All file descriptors have been closed\"\nelse\n  fail \"Some file descriptors are still open\"\nfi\n\nrm /tmp/no_pm2_out.dat\nrm /tmp/no_pm2_out2.dat\nrm /tmp/empty_pm2_out.dat\nrm /tmp/empty_pm2_out2.dat\n"
  },
  {
    "path": "test/e2e/include.sh",
    "content": "#\n# (C) 2013 Unitech.io Inc.\n#\n\n# export PM2_RPC_PORT=4242\n# export PM2_PUB_PORT=4243\n\nnode=\"`type -P node`\"\n\nif command -v bun >/dev/null 2>&1\nthen\n    IS_BUN=true\nelse\n    IS_BUN=false\nfi\n\npm2_path=`pwd`/bin/pm2\n\nif [ ! -f $pm2_path ];\nthen\n    pm2_path=`pwd`/../bin/pm2\n    if [ ! -f $pm2_path ];\n    then\n        pm2_path=`pwd`/../../bin/pm2\n    fi\nfi\n\npm2=\"$pm2_path\"\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nfile_path=\"${SRC}/../fixtures\"\n\nif [ ! -d $file_path ];\nthen\n    file_path=\"${SRC}/../../fixtures\"\n    if [ ! -d $file_path ];\n    then\n        file_path=\"${SRC}/../../../fixtures\"\n    fi\nfi\n\n$pm2 link delete\n$pm2 kill\n\nfunction fail {\n  echo -e \"######## ✘ $1\"\n  exit 1\n}\n\nfunction success {\n  echo -e \"------------> ✔ $1\"\n}\n\nfunction spec {\n  RET=$?\n  sleep 0.1\n  [ $RET -eq 0 ] || fail \"$1\"\n  success \"$1\"\n}\n\nfunction runTest {\n    echo \"[~] Starting test $1\"\n    START=$(date +%s)\n    bash $1\n    RET=$?\n    if [ $RET -ne 0 ];\n    then\n        STR=\"[RETRY] $1 failed and NOW is getting retried\"\n        echo $STR\n        echo $STR >> e2e_time\n        bash $1\n        RET=$?\n\n        if [ $RET -ne 0 ];\n        then\n           fail $1\n        fi\n    fi\n\n    END=$(date +%s)\n    DIFF=$(echo \"$END - $START\" | bc)\n    STR=\"[V] $1 succeeded and took $DIFF seconds\"\n    echo $STR\n    echo $STR >> e2e_time\n}\n\nfunction ispec {\n  RET=$?\n  sleep 0.2\n  [ $RET -ne 0 ] || fail \"$1\"\n  success \"$1\"\n}\n\nfunction should {\n    sleep 0.3\n    $pm2 prettylist > /tmp/tmp_out.txt\n    OUT=`cat /tmp/tmp_out.txt | grep -v \"npm\" | grep -o \"$2\" | wc -l`\n    [ $OUT -eq $3 ] || { [ -n \"${4+x}\" ] && [ $OUT -eq $4 ]; } || fail \"$1\"\n    #[ $OUT -eq $3 ] || fail \"$1\"\n    success \"$1\"\n}\n\nfunction shouldnot {\n    sleep 0.3\n    $pm2 prettylist > /tmp/tmp_out.txt\n    OUT=`cat /tmp/tmp_out.txt | grep -v \"npm\" | grep -o \"$2\" | wc -l`\n    [ $OUT -ne $3 ] || fail \"$1\"\n    success \"$1\"\n}\n\nfunction exists {\n    sleep 0.3\n    $pm2 prettylist > /tmp/tmp_out.txt\n    OUT=`cat /tmp/tmp_out.txt | grep -v \"npm\" | grep -o \"$2\" | wc -l`\n    [ $OUT -ge 1 ] || fail \"$1\"\n    success \"$1\"\n}\n"
  },
  {
    "path": "test/e2e/internals/daemon-paths-override.sh",
    "content": "#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\n\n$pm2 kill\nrm /tmp/.toto.pid\n\n########### Override PM2 pid path\nPM2_PID_FILE_PATH=/tmp/.toto.pid $pm2 ls\n\nsleep 2\ntest -f /tmp/.toto.pid\n\nspec 'should have picked the pm2 pid path'\n"
  },
  {
    "path": "test/e2e/internals/increment-var.sh",
    "content": "\n#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\n\ncd $file_path/increment-var/\n\n$pm2 delete all\n\necho '-------- CLUSTER MODE TEST -------'\n\n$pm2 start ecosystem.json --only sample-normal\n\nshould \"start 2 processes\" \"online\" 2\nshould \"start one process with NODE_APP_INSTANCE at 0\" \"NODE_APP_INSTANCE: 0\" 1\nshould \"start one process with NODE_APP_INSTANCE at 1\" \"NODE_APP_INSTANCE: 1\" 1\nshould \"not start one process with NODE_APP_INSTANCE at 2\" \"NODE_APP_INSTANCE: 2\" 0\n\n$pm2 scale sample-normal 4\n\nshould \"start 2 more processes\" \"online\" 4\nshould \"start one process with NODE_APP_INSTANCE at 0\" \"NODE_APP_INSTANCE: 0\" 1\nshould \"start one process with NODE_APP_INSTANCE at 1\" \"NODE_APP_INSTANCE: 1\" 1\nshould \"start one process with NODE_APP_INSTANCE at 2\" \"NODE_APP_INSTANCE: 2\" 1\nshould \"start one process with NODE_APP_INSTANCE at 3\" \"NODE_APP_INSTANCE: 3\" 1\nshould \"not start one process with NODE_APP_INSTANCE at 4\" \"NODE_APP_INSTANCE: 4\" 0\n\n$pm2 scale sample-normal 2\n\nshould \"deleted 2 more processes\" \"online\" 2\nshould \"not have the process with NODE_APP_INSTANCE at 0\" \"NODE_APP_INSTANCE: 0\" 0\nshould \"not have the process with NODE_APP_INSTANCE at 1\" \"NODE_APP_INSTANCE: 1\" 0\nshould \"have one process with NODE_APP_INSTANCE at 2\" \"NODE_APP_INSTANCE: 2\" 1\nshould \"have one process with NODE_APP_INSTANCE at 3\" \"NODE_APP_INSTANCE: 3\" 1\n\n$pm2 scale sample-normal 4\n\nshould \"start 2 more processes\" \"online\" 4\nshould \"should reuse old number with NODE_APP_INSTANCE at 0\" \"NODE_APP_INSTANCE: 0\" 1\nshould \"should reuse old number with NODE_APP_INSTANCE at 1\" \"NODE_APP_INSTANCE: 1\" 1\nshould \"have one process with NODE_APP_INSTANCE at 2\" \"NODE_APP_INSTANCE: 2\" 1\nshould \"have one process with NODE_APP_INSTANCE at 3\" \"NODE_APP_INSTANCE: 3\" 1\nshould \"not have the process with NODE_APP_INSTANCE at 4\" \"NODE_APP_INSTANCE: 4\" 0\nshould \"not have the process with NODE_APP_INSTANCE at 5\" \"NODE_APP_INSTANCE: 5\" 0\n\n$pm2 delete all\n\n$pm2 start ecosystem.json --only sample-other-instance\n\nshould \"start 2 processes\" \"online\" 2\nshould \"not have deleted the process with NODE_APP_INSTANCE at 0\" \"NODE_APP_INSTANCE: 0\" 0\nshould \"not have deleted the process with NODE_APP_INSTANCE at 1\" \"NODE_APP_INSTANCE: 1\" 0\nshould \"start one process with APP_ID at 0\" \"APP_ID: 0\" 1\nshould \"start one process with APP_ID at 1\" \"APP_ID: 1\" 1\n\n$pm2 delete all\n\n$pm2 start ecosystem.json --only sample-increment\n\nshould \"start 2 processes\" \"online\" 2\nshould \"start one process with NODE_APP_INSTANCE at 0\" \"NODE_APP_INSTANCE: 0\" 1\nshould \"start one process with NODE_APP_INSTANCE at 1\" \"NODE_APP_INSTANCE: 1\" 1\nshould \"start one process with PORT at 3000\" \"PORT: 3000\" 2\nshould \"start one process with PORT at 3001\" \"PORT: 3001\" 2\n\n$pm2 delete all\n\necho '-------- FORK MODE TEST -------'\n\n$pm2 start ecosystem.json --only sample-normal-fork\n\nshould \"start 2 processes\" \"online\" 2\nshould \"start one process with NODE_APP_INSTANCE at 0\" \"NODE_APP_INSTANCE: 0\" 1\nshould \"start one process with NODE_APP_INSTANCE at 1\" \"NODE_APP_INSTANCE: 1\" 1\nshould \"not start one process with NODE_APP_INSTANCE at 2\" \"NODE_APP_INSTANCE: 2\" 0\n\n$pm2 scale sample-normal-fork 4\n\nshould \"start 2 more processes\" \"online\" 4\nshould \"start one process with NODE_APP_INSTANCE at 0\" \"NODE_APP_INSTANCE: 0\" 1\nshould \"start one process with NODE_APP_INSTANCE at 1\" \"NODE_APP_INSTANCE: 1\" 1\nshould \"start one process with NODE_APP_INSTANCE at 2\" \"NODE_APP_INSTANCE: 2\" 1\nshould \"start one process with NODE_APP_INSTANCE at 3\" \"NODE_APP_INSTANCE: 3\" 1\nshould \"not start one process with NODE_APP_INSTANCE at 4\" \"NODE_APP_INSTANCE: 4\" 0\n\n$pm2 scale sample-normal-fork 2\n\nshould \"deleted 2 more processes\" \"online\" 2\nshould \"not have the process with NODE_APP_INSTANCE at 0\" \"NODE_APP_INSTANCE: 0\" 0\nshould \"not have the process with NODE_APP_INSTANCE at 1\" \"NODE_APP_INSTANCE: 1\" 0\nshould \"have one process with NODE_APP_INSTANCE at 2\" \"NODE_APP_INSTANCE: 2\" 1\nshould \"have one process with NODE_APP_INSTANCE at 3\" \"NODE_APP_INSTANCE: 3\" 1\n\n$pm2 scale sample-normal-fork 4\n\nshould \"start 2 more processes\" \"online\" 4\nshould \"should reuse old number with NODE_APP_INSTANCE at 0\" \"NODE_APP_INSTANCE: 0\" 1\nshould \"should reuse old number with NODE_APP_INSTANCE at 1\" \"NODE_APP_INSTANCE: 1\" 1\nshould \"have one process with NODE_APP_INSTANCE at 2\" \"NODE_APP_INSTANCE: 2\" 1\nshould \"have one process with NODE_APP_INSTANCE at 3\" \"NODE_APP_INSTANCE: 3\" 1\nshould \"not have the process with NODE_APP_INSTANCE at 4\" \"NODE_APP_INSTANCE: 4\" 0\nshould \"not have the process with NODE_APP_INSTANCE at 5\" \"NODE_APP_INSTANCE: 5\" 0\n\n$pm2 delete all\n\n$pm2 start ecosystem.json --only sample-other-instance-fork\n\nshould \"start 2 processes\" \"online\" 2\nshould \"not have deleted the process with NODE_APP_INSTANCE at 0\" \"NODE_APP_INSTANCE: 0\" 0\nshould \"not have deleted the process with NODE_APP_INSTANCE at 1\" \"NODE_APP_INSTANCE: 1\" 0\nshould \"start one process with APP_ID at 0\" \"APP_ID: 0\" 1\nshould \"start one process with APP_ID at 1\" \"APP_ID: 1\" 1\n\n$pm2 delete all\n\n$pm2 start ecosystem.json --only sample-increment-fork\n\nshould \"start 2 processes\" \"online\" 2\nshould \"start one process with NODE_APP_INSTANCE at 0\" \"NODE_APP_INSTANCE: 0\" 1\nshould \"start one process with NODE_APP_INSTANCE at 1\" \"NODE_APP_INSTANCE: 1\" 1\nshould \"start one process with PORT at 3000\" \"PORT: 3000\" 2\nshould \"start one process with PORT at 3001\" \"PORT: 3001\" 2\n"
  },
  {
    "path": "test/e2e/internals/infinite-loop.sh",
    "content": "#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\n\ncd $file_path\n\necho \"Starting infinite loop tests\"\n\n$pm2 start killtoofast.js --name unstable-process\n\necho -n \"Waiting for process to restart too many times and pm2 to stop it\"\n\nfor (( i = 0; i <= 100; i++ )); do\n    sleep 0.1\n    echo -n \".\"\ndone\n\n\n$pm2 list\nshould 'should has stopped unstable process' 'errored' 1\n\n$pm2 delete all\n\necho \"Start infinite loop tests for restart|reload\"\n\ncp killnotsofast.js killthen.js\n\n$pm2 start killthen.js --name killthen\n\n$pm2 list\n\nshould 'should killthen alive for a long time' 'online' 1\n\n# Replace killthen file with the fast quit file\n\nsleep 15\ncp killtoofast.js killthen.js\n\necho \"Restart with unstable process\"\n\n$pm2 list\n\n$pm2 restart all  # pm2 reload should also work here\n\nfor (( i = 0; i <= 80; i++ )); do\n    sleep 0.1\n    echo -n \".\"\ndone\n\n$pm2 list\n\nshould 'should has stoped unstable process' 'errored' 1\n\nrm killthen.js\n\n$pm2 list\n\n$pm2 kill\n"
  },
  {
    "path": "test/e2e/internals/listen-timeout.sh",
    "content": "#!/usr/bin/env bash\n\n#export PM2_GRACEFUL_LISTEN_TIMEOUT=1000\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\ncd $file_path/listen-timeout/\n\necho -e \"\\033[1mENV REFRESH\\033[0m\"\n\n$pm2 start wait-ready.js -i 1 --wait-ready --listen-timeout 5000\n\n$pm2 reload all &\nsleep 2\nshould 'should have started 1 clustered app' 'online' 1\nshould 'should restart processes with new name' 'restart_time: 1' 1\n"
  },
  {
    "path": "test/e2e/internals/options-via-env.sh",
    "content": "\n#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\n\ncd $file_path\n\n# With start\n$pm2 start echo.js\nshould 'should deep_monitoring' 'deep_monitoring' 0\n\n$pm2 delete all\n\nPM2_DEEP_MONITORING=true $pm2 start echo.js\nshould 'should deep_monitoring' 'deep_monitoring' 1\n\n$pm2 delete all\n\n# With restart\n$pm2 start echo.js\nshould 'should deep_monitoring' 'deep_monitoring' 0\nPM2_DEEP_MONITORING=true $pm2 restart echo\nshould 'should deep_monitoring' 'deep_monitoring' 1\n"
  },
  {
    "path": "test/e2e/internals/promise.sh",
    "content": "#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\n\ncd $file_path/promise/\n\n# Check for 0.10 & 0.12 support\nnode -e \"process.version.indexOf('v0') > -1 ? process.exit(1) : process.exit(0)\"\nRET=$?\n[ $RET -eq 0 ] || exit 0\n\necho \"###### Cluster mode\"\n> rejection.log\n$pm2 start rejection.js -i 1 -l rejection.log --merge-logs\nsleep 1\nshould 'should has not restarted process' 'restart_time: 0' 1\ncat rejection.log | grep \"Errorla\"\nspec \"should have logged promise error\"\n\n$pm2 delete all\n\n> empty-rejection.log\n$pm2 start empty-rejection.js -i 1 -l empty-rejection.log --merge-logs\nsleep 1\nshould 'should has not restarted process' 'restart_time: 0' 1\n\ncat empty-rejection.log | grep \"You have triggered an unhandledRejection, you may have forgotten to catch a Promise rejection\"\nspec \"should have logged promise error\"\n\n$pm2 delete all\n\necho \"###### Fork mode\"\n\n> rejection.log\n$pm2 start rejection.js -l rejection.log --merge-logs\nsleep 1\nshould 'should has not restarted process' 'restart_time: 0' 1\n\ncat rejection.log | grep \"You have triggered an unhandledRejection, you may have forgotten to catch a Promise rejection\"\nspec \"should have logged promise error\"\n\n$pm2 delete all\n\n> empty-rejection.log\n$pm2 start empty-rejection.js -l empty-rejection.log --merge-logs\nsleep 1\nshould 'should has not restarted process' 'restart_time: 0' 1\n\ncat empty-rejection.log | grep \"You have triggered an unhandledRejection, you may have forgotten to catch a Promise rejection\"\nspec \"should have logged promise error\"\n"
  },
  {
    "path": "test/e2e/internals/signal.sh",
    "content": "#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\n\ncd $file_path\n\n#\n# Signal feature\n#\n$pm2 start signal.js -i 2\n# get the log file and the id.\nOUT_LOG=`$pm2 prettylist | grep -m 1 -E \"pm_out_log_path:\" | sed \"s/.*'\\([^']*\\)',/\\1/\"`\ncat /dev/null > $OUT_LOG\n\n$pm2 sendSignal SIGUSR2 signal\nsleep 1\n\nOUT=`grep \"SIGUSR2\" \"$OUT_LOG\" | wc -l`\n[ $OUT -eq 1 ] || fail \"Signal not received by the process name\"\nsuccess \"Processes sucessfully receives the signal\"\n\n$pm2 stop signal.js\n\n# Send a process by id\n$pm2 start signal.js\n\nsleep 1\n# get the log file and the id.\nOUT_LOG=`$pm2 prettylist | grep -m 1 -E \"pm_out_log_path:\" | sed \"s/.*'\\([^']*\\)',/\\1/\"`\nID=`$pm2 prettylist | grep -E \"pm_id:\" | sed \"s/.*pm_id: \\([^,]*\\),/\\1/\"`\n\ncat /dev/null > $OUT_LOG\n\n$pm2 sendSignal SIGUSR2 $ID\n\nOUT=`grep \"SIGUSR2\" \"$OUT_LOG\" | wc -l`\n[ $OUT -eq 1 ] || fail \"Signal not received by the process name\"\nsuccess \"Processes sucessfully receives the signal\"\n"
  },
  {
    "path": "test/e2e/internals/source_map.sh",
    "content": "#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\n\ncd $file_path\n\n#\n# Fork mode\n#\nrm sm.log\n$pm2 start source-map/main.js -e sm.log --merge-logs --disable-source-map-support\nsleep 2\ncat sm.log | grep \"main.js\"\nspec \"should not take source map into account\"\n\nrm sm.log\n$pm2 delete all\n$pm2 start source-map/main.js -e sm.log --merge-logs\nsleep 2\ncat sm.log | grep \"main.ts\"\nspec \"should automatically activate source map support (detect main.ts)\"\n\nrm sm.log\n$pm2 delete all\n$pm2 start source-map/main.js -e sm.log --merge-logs --source-map-support\nsleep 2\ncat sm.log | grep \"main.ts\"\nspec \"should force source map support\"\n\n#\n# Cluster mode\n#\nrm sm.log\n$pm2 delete all\n$pm2 start source-map/main.js -e sm.log --merge-logs -i 1\nsleep 2\ncat sm.log | grep \"main.ts\"\nspec \"should automatically activate source map support (detect main.ts)\"\n\nrm sm.log\n$pm2 delete all\n$pm2 start source-map/main.js -e sm.log --merge-logs -i 1 --source-map-support\nsleep 2\ncat sm.log | grep \"main.ts\"\nspec \"should force source map support\"\n"
  },
  {
    "path": "test/e2e/internals/start-consistency.sh",
    "content": "#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\n\ncd $file_path\n\n############# TEST\n\ncd start-consistency;\n\n$pm2 start child.js\n\nLINE_NB_CLI=`$pm2 prettylist | wc -l`\n\n$pm2 delete all\n\n$pm2 start child.json\n\nLINE_NB_JSON=`$pm2 prettylist | wc -l`\n\n$pm2 prettylist | grep \"vizion: true\"\nspec \"Vizion\"\n\n\n# if [ $LINE_NB_JSON -eq $LINE_NB_CLI ]\n# then\n#     success \"Starting a basic JSON is consistent with CLI start\"\n# else\n#     fail \"Starting a basic JSON is NOT consistent with CLI start\"\n# fi\n"
  },
  {
    "path": "test/e2e/internals/wait-ready-event.sh",
    "content": "#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\n\ncd $file_path/wait_ready_event\n\n##### start with sending event and without waiting (fork mode)\n$pm2 start http-wait-start.js\nshould 'should have started 1 forked app' 'online' 1\n$pm2 delete all\n\n##### start with sending event and ask to wait (fork mode)\n$pm2 start http-wait-start.js --wait-ready\nshould 'should have started 1 forked app' 'online' 1\n$pm2 delete all\n\n##### start without sending event and without waiting (fork mode)\n$pm2 start http-wait-start.js\nshould 'should have started 1 forked app ' 'online' 1\n$pm2 delete all\n\n##### start without sending event and ask to wait (fork mode)\n$pm2 start http-wait-start_nocb.js --wait-ready --listen-timeout=8000 &\nsleep 5\nshould 'should be 1 forked launching state app waiting for ready event' 'launching' 1\n$pm2 delete all\n\n##### start with sending event and without waiting (cluster mode)\n$pm2 start http-wait-start.js -i 1\nshould 'should have started 1 clustered app' 'online' 1\n$pm2 delete all\n\n##### start with sending event and ask to wait (cluster mode)\n$pm2 start http-wait-start.js -i 1 --wait-ready\nshould 'should have started 1 clustered app' 'online' 1\n$pm2 delete all\n\n##### start without sending event and without waiting (cluster mode)\n$pm2 start http-wait-start.js -i 1\nshould 'should have started 1 clustered app' 'online' 1\n$pm2 delete all\n\n##### start without sending event and ask to wait (cluster mode)\n$pm2 start http-wait-start_nocb.js -i 1 --wait-ready --listen-timeout=8000 &\nsleep 5\nshould 'should be 1 clustered launching state app waiting for ready event' 'launching' 1\n$pm2 delete all\n"
  },
  {
    "path": "test/e2e/internals/wrapped-fork.sh",
    "content": "#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\n\ncd $file_path\n\necho \"################## Wrapped fork ###################\"\n\necho \"Testing wrapped fork mode values\"\n\nrm path-check1.txt\nrm path-check2.txt\n\nif command -v bun >/dev/null 2>&1\nthen\n    bun path-check.js > path-check1.txt\nelse\n    node path-check.js > path-check1.txt\nfi\n\n$pm2 start path-check.js --no-autorestart -o path-check2.txt\nsleep 1\n\nOUT=`diff path-check1.txt path-check2.txt`\n\necho $OUT\n[ -z \"$OUT\" ] || fail \"The outputs are not identical\"\nsuccess \"The outputs are identical\"\n\n$pm2 kill\n"
  },
  {
    "path": "test/e2e/logs/log-create-not-exist-dir.sh",
    "content": "#!/usr/bin/env bash\n\nSRC=$(\n  cd $(dirname \"$0\")\n  pwd\n)\nsource \"${SRC}/../include.sh\"\n\ncd $file_path/log-create-not-exist-dir/\n\nLOG_PATH_PREFIX=\"${SRC}/__log-create-not-exist-dir__\"\n\n# a long path directory, to make sure that creating the directory is using synchronous mode\n# if using asynchronous mode, creating the long path directory would fail.\nLOG_FILE=\"${LOG_PATH_PREFIX}/a-deep/path/which/should/cost/lots/of/time/to/create/the/directory/out-rel.log\"\nrm -rf \"${LOG_PATH_PREFIX}\"\n$pm2 start echo.js -o ${LOG_FILE}\n\nspec \"Should have the exit code 0\";\n\nsleep 2\n\ngrep \"start\" ${LOG_FILE}\n\nspec \"Should have 'start' in the log file\"\n\ncd ${SRC}\nrm -rf \"${LOG_PATH_PREFIX}\"\n$pm2 delete all\n"
  },
  {
    "path": "test/e2e/logs/log-custom.sh",
    "content": "\n#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\n\ncd $file_path\n\nCURRENT_YEAR=`date +\"%Y\"`\n\n# CLUSTERMODE YYYY\n$pm2 start echo.js --log-date-format \"YYYY\" -o out-rel.log --merge-logs\n\n>out-rel.log\n\nsleep 2\n\ngrep $CURRENT_YEAR out-rel.log\nspec \"Should have written year in log file according to format YYYY\"\n\nrm out-rel.log\n\n$pm2 delete all\n\n# CLUSTERMODE Wrong format\n$pm2 start echo.js --log-date-format \"YYYY asdsd asd asd sad asd \" -o out-rel.log --merge-logs\n\nsleep 1\nshould 'should has not restarted' 'restart_time: 0' 1\nspec \"Should have not fail with random format\"\n\nrm out-rel.log\n\n$pm2 delete all\n\n\n# CLUSTERMODE YYYY\n$pm2 start echo.js --log-date-format \"YYYY\" -o out-rel.log --merge-logs -x\n\n>out-rel.log\n\nsleep 2\n\ngrep $CURRENT_YEAR out-rel.log\nspec \"Should have written year in log file according to format YYYY\"\n\nrm out-rel.log\n\n$pm2 delete all\n"
  },
  {
    "path": "test/e2e/logs/log-entire.sh",
    "content": "\n#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\n\nfunction head {\n  echo -e \"\\x1B[1;35m$1\\x1B[0m\"\n}\nfunction test_dir {\n  local result=\"\"\n  if [ -f \"$1-0.log\" ]; then\n    result=\"$1-0.log\"\n  else\n    result=\"$1.log\"\n  fi\n  echo \"$result\"\n}\nfunction test {\n  sleep 5\n\n  out_file=$(test_dir \"out\")\n  err_file=$(test_dir \"err\")\n  if [ ! -n \"$1\" ]; then\n    entire_file=$(test_dir \"entire\")\n  fi\n\n  COUNT=$(grep \"tick\" $out_file | wc -l)\n  echo \"$COUNT\"\n  [ \"$COUNT\" -ne 0 ] || fail \"Should have \\\"tick\\\" in out log.\"\n  success \"Should have \\\"tick\\\" in out log.\"\n\n  COUNT=$(grep \"Error\" $err_file | wc -l)\n  echo \"$COUNT\"\n  [ \"$COUNT\" -ne 0 ] || fail \"Should have \\\"Error\\\" in error log.\"\n  success \"Should have \\\"Error\\\" in error log.\"\n\n  if [ ! -n \"$1\" ]; then\n    COUNT1=$(grep \"tick\" $entire_file | wc -l)\n    echo \"$COUNT1\"\n    COUNT2=$(grep \"Error\" $entire_file | wc -l)\n    echo \"$COUN2\"\n\n    ([ \"$COUNT1\" -ne 0 ] && [ \"$COUNT2\" -ne 0 ]) || fail \"Should have \\\"tick\\\" and \\\"Error\\\" in entire log.\"\n    success \"Should have \\\"tick\\\" and \\\"Error\\\" in entire log.\"\n  fi\n  $pm2 kill\n\n  sleep 1\n\n  rm $out_file\n  rm $err_file\n\n  if [ ! -n \"$1\" ]; then\n    rm $entire_file\n  fi\n}\n\ncd $file_path\n\n$pm2 kill\n\nhead \">> START CLUSTERMODE (ENTIRE EXISTS)\"\n\n$pm2 start throw-later.js -i 1 -o out.log -e err.log -l entire.log\n\ntest\n\nhead \">> START CLUSTERMODE (ENTIRE DOES NOT EXIST)\"\n\n$pm2 start throw-later.js -i 1 -o out.log -e err.log\n\ntest \"NE\"\n\nhead \">> START CLUSTERMODE WITH --merge-logs (ENTIRE EXISTS)\"\n\n$pm2 start throw-later.js -i 1 -o out.log -e err.log -l entire.log --merge-logs\n\ntest\n\nhead \">> START CLUSTERMODE WITH --merge-logs (ENTIRE DOES NOT EXIST)\"\n\n$pm2 start throw-later.js -i 1 -o out.log -e err.log --merge-logs\n\ntest \"NE\"\n\n\nhead \">> START FORKMODE (ENTIRE EXISTS)\"\n\n$pm2 start throw-later.js -o out.log -e err.log -l entire.log\n\ntest\n\nhead \">> START FORKMODE (ENTIRE DOES NOT EXIST)\"\n\n$pm2 start throw-later.js -o out.log -e err.log\n\ntest \"NE\"\n\nhead \">> START FORKMODE WITH --merge-logs (ENTIRE EXISTS)\"\n\n$pm2 start throw-later.js -o out.log -e err.log -l entire.log --merge-logs\n\ntest\n\nhead \">> START FORKMODE WITH --merge-logs (ENTIRE DOES NOT EXIST)\"\n\n$pm2 start throw-later.js -o out.log -e err.log --merge-logs\n\ntest \"NE\"\n\nhead \">> RELOAD LOGS (ENTIRE EXISTS)\"\n\n$pm2 start throw-later.js -o out.log -e err.log -l entire.log --merge-logs\n\n$pm2 reloadLogs\n\ntest\n\nhead \">> RELOAD LOGS (ENTIRE DOES NOT EXIST)\"\n\n$pm2 start throw-later.js -o out.log -e err.log --merge-logs\n\n$pm2 reloadLogs\n\ntest \"NE\"\n\nhead \">> RESTART (ENTIRE EXISTS)\"\n\n$pm2 start throw-later.js -o out.log -e err.log -l entire.log --merge-logs\n\n$pm2 restart all\n\ntest\n\nhead \">> RESTART (ENTIRE DOES NOT EXIST)\"\n\n$pm2 start throw-later.js -o out.log -e err.log --merge-logs\n\n$pm2 restart all\n\ntest \"NE\"\n\nhead \">> RELOAD (ENTIRE EXISTS)\"\n\n$pm2 start throw-later.js -o out.log -e err.log -l entire.log --merge-logs\n\n$pm2 reload all\n\ntest\n\nhead \">> RELOAD (ENTIRE DOES NOT EXIST)\"\n\n$pm2 start throw-later.js -o out.log -e err.log --merge-logs\n\n$pm2 reload all\n\ntest \"NE\"\n\nhead \">> DESCRIBE (ENTIRE EXISTS)\"\n\n$pm2 start throw-later.js -o out.log -e err.log -l entire.log --merge-logs\n\n$pm2 jlist | grep -w \"entire.log\" 2> /dev/null\nspec \"\\\"entire log path\\\" should exists.\"\n\ntest\n\nhead \">> DESCRIBE (ENTIRE DOES NOT EXIST)\"\n\n$pm2 start throw-later.js -o out.log -e err.log --merge-logs\n\n$pm2 jlist | grep -w \"pm_log_path\" 2> /dev/null\nispec \"\\\"entire log path\\\" should not exist.\"\n\ntest \"NE\"\n\nhead \">> FLUSH (ENTIRE EXISTS)\"\n\n$pm2 start throw-later.js -o out.log -e err.log -l entire.log --merge-logs\n\n$pm2 flush\n\ntest\n\nhead \">> FLUSH (ENTIRE DOES NOT EXIST)\"\n\n$pm2 start throw-later.js -o out.log -e err.log --merge-logs\n\n$pm2 flush\n\ntest \"NE\"\n\nhead \">> JLIST (ENTIRE EXISTS)\"\n\n$pm2 start throw-later.js -o out.log -e err.log -l entire.log --merge-logs\n\n$pm2 jlist | grep -w \"pm_log_path\"\nspec \"\\\"entire log path\\\" should exists.\"\n\ntest\n\nhead \">> JLIST (ENTIRE DOES NOT EXIST)\"\n\n$pm2 start throw-later.js -o out.log -e err.log --merge-logs\n\n$pm2 jlist | grep -w \"pm_log_path\"\nispec \"\\\"entire log path\\\" should not exist.\"\n\ntest \"NE\"\n\nhead \">> START JSON (ENTIRE EXISTS)\"\n\n$pm2 start throw-later.json\n\ntest\n\nhead \">> START JSON (ENTIRE DOES NOT EXIST)\"\n\n$pm2 start throw-later1.json\n\ntest \"NE\"\n\n$pm2 kill\n"
  },
  {
    "path": "test/e2e/logs/log-json.sh",
    "content": "\n#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\n\ncd $file_path/log-json/\n\nrm output.log\n\n# fork mode json logs\n$pm2 start ecosystem.json --only one-echo\n\n! test -f output.log\n\nsleep 2\n\nnode -pe 'JSON.parse(process.argv[1])' `cat output.log`\nspec 'should have parsed valid json'\n\n$pm2 delete all\nrm output.log\n\n# cluster mode json logs\n$pm2 start ecosystem.json -i 2 --only one-echo-cluster\n\n! test -f output.log\n\nsleep 2\n\nnode -pe 'JSON.parse(process.argv[1])' `cat output.log`\nspec 'should have parsed valid json'\n\n$pm2 delete all\nrm output.log\n\nCURRENT_YEAR=`date +\"%Y\"`\n# fork mode with date\n\n$pm2 start ecosystem.json --only one-echo-date\n\n! test -f output.log\n\nsleep 2\n\nnode -pe 'JSON.parse(process.argv[1])' `cat output.log`\nspec 'should have parsed valid json'\n\nOUT=`cat output.log | grep -o \"$CURRENT_YEAR\" | wc -l`\n[ $OUT -ge 1 ] || fail \"should contains custom timestamp\"\nsuccess \"should contains custom timestamp\"\n\n$pm2 delete all\nrm output.log\n\n# cluster mode with date\n\n$pm2 start ecosystem.json --only one-echo-cluster-date\n\n! test -f output.log\n\nsleep 2\n\nnode -pe 'JSON.parse(process.argv[1])' `cat output.log`\nspec 'should have parsed valid json'\n\nOUT=`cat output.log | grep -o \"$CURRENT_YEAR\" | wc -l`\n[ $OUT -eq 1 ] || fail \"should contains custom timestamp in cluster mode\"\nsuccess \"should contains custom timestamp in cluster mode\"\n\n$pm2 delete all\nrm output.log\n"
  },
  {
    "path": "test/e2e/logs/log-namespace.sh",
    "content": "#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\n\ncd $file_path/log-namespace/\n\nLOG_PATH_PREFIX=\"${SRC}/__log-namespace__\"\n\nrm -rf \"${LOG_PATH_PREFIX}\"\nmkdir \"${LOG_PATH_PREFIX}\"\n\n$pm2 start echo.js --namespace e2e-test-log-namespace\n\nLOG_FILE_BASELINE=\"${LOG_PATH_PREFIX}/baseline-out.log\"\n$pm2 logs e2e-test-log-namespace > $LOG_FILE_BASELINE & # backgrounded - will be stopped by `$pm2 delete all`\n\nsleep 2 # should leave time for ~40 \"tick\" lines\n\n# Using -q to avoid spamming, since there will be a fair few \"tick\" matches\ngrep -q \"tick\" ${LOG_FILE_BASELINE}\nspec \"Should have 'tick' in the log file\"\n\nLOG_FILE_LINES_ZERO=\"${LOG_PATH_PREFIX}/lines-zero-out.log\"\n$pm2 logs e2e-test-log-namespace --lines 0 > $LOG_FILE_LINES_ZERO &\n\nsleep 2 # should leave time for ~40 \"tick\" lines\n\n# Using -q to avoid spamming, since there will be a fair few \"tick\" matches\ngrep -q \"tick\" ${LOG_FILE_LINES_ZERO}\nspec \"Should have 'tick' in the log file even if using --lines 0\"\n\ncd ${SRC}\nrm -rf \"${LOG_PATH_PREFIX}\"\n$pm2 delete all\n"
  },
  {
    "path": "test/e2e/logs/log-null.sh",
    "content": "\n#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\n\ncd $file_path\n\nrm ~/.pm2/logs/echo-out.log\nrm ~/.pm2/logs/echo-error.log\n\necho \">>>>>>>>>>>>>>>>>>>> LOG PATH SET TO NULL\"\n\n# set error log to null in fork\n$pm2 start echo.js -o out.log -e NULL --merge-logs\n\nsleep 1\n\ntest -f out.log\nspec \"err log should exist with null in fork mode\"\n! test -f ~/.pm2/logs/echo-error.log\nspec \"err log shouldnt exist with null in fork mode\"\n\n$pm2 delete all\nrm out.log\n\n# set error log to null in cluster\n$pm2 start echo.js -i 1 -o out.log -e NULL --merge-logs\n\nsleep 1\n\ntest -f out.log\nspec \"err log should exist with null in cluster mode\"\n! test -f ~/.pm2/logs/echo-error.log\nspec \"err log shouldnt exist with null in cluster mode\"\n\n$pm2 delete all\nrm out.log\n\n# set out log to null in fork\n$pm2 start echo.js -o NULL -e err.log --merge-logs\n\nsleep 1\n\ntest -f err.log\nspec \"err log should exist with null in fork mode\"\n! test -f ~/.pm2/logs/echo-out.log\nspec \"output log shouldnt exist with null in fork mode\"\n\n$pm2 delete all\nrm err.log\n\n# set out log to null in cluster\n$pm2 start echo.js -i 1 -o NULL -e err.log --merge-logs\n\nsleep 1\n\ntest -f err.log\nspec \"err log should exist with null in cluster mode\"\n! test -f ~/.pm2/logs/echo-out.log\nspec \"output log shouldnt exis with null in cluster mode\"\n\n$pm2 delete all\nrm err.log\n\n# set error AND out log to null in cluster\n$pm2 start echo.js -i 1 -o NULL -e NULL --merge-logs\n\nsleep 1\n\n! test -f ~/.pm2/logs/echo-out.log\nspec \"out log shouldnt exist with null in cluster mode\"\n! test -f ~/.pm2/logs/echo-error.log\nspec \"error log shouldnt exist with null in cluster mode\"\n\n$pm2 delete all\n\n# set error AND out log to null in fork\n$pm2 start echo.js -o NULL -e NULL --merge-logs\n\nsleep 1\n\n! test -f ~/.pm2/logs/echo-out.log\nspec \"out log shouldnt exist with null in fork mode\"\n! test -f ~/.pm2/logs/echo-error.log\nspec \"error log shouldnt exist with null in fork mode\"\n\n$pm2 delete all\n\nrm ~/.pm2/logs/echo-out.log\nrm ~/.pm2/logs/echo-error.log\n\necho \">>>>>>>>>>>>>>>>>>>> LOG PATH SET TO /dev/null\"\n\n# set error log to null in fork\n$pm2 start echo.js -o out.log -e /dev/null --merge-logs\n\nsleep 1\n\ntest -f out.log\nspec \"err log should exist with /dev/null in fork mode\"\n! test -f ~/.pm2/logs/echo-error.log\nspec \"err log shouldnt exist with /dev/null in fork mode\"\n\n$pm2 delete all\nrm out.log\n\n# set error log to null in cluster\n$pm2 start echo.js -i 1 -o out.log -e /dev/null --merge-logs\n\nsleep 1\n\ntest -f out.log\nspec \"err log should exist with /dev/null in cluster mode\"\n! test -f ~/.pm2/logs/echo-error.log\nspec \"err log shouldnt exist with /dev/null in cluster mode\"\n\n$pm2 delete all\nrm out.log\n\n# set out log to null in fork\n$pm2 start echo.js -o /dev/null -e err.log --merge-logs\n\nsleep 1\n\ntest -f err.log\nspec \"err log should exist with /dev/null in fork mode\"\n! test -f ~/.pm2/logs/echo-out.log\nspec \"output log shouldnt exist with /dev/null in fork mode\"\n\n$pm2 delete all\nrm err.log\n\n# set out log to null in cluster\n$pm2 start echo.js -i 1 -o /dev/null -e err.log --merge-logs\n\nsleep 1\n\ntest -f err.log\nspec \"err log should exist with /dev/null in cluster mode\"\n! test -f ~/.pm2/logs/echo-out.log\nspec \"output log shouldnt exis with /dev/null in cluster mode\"\n\n$pm2 delete all\nrm err.log\n\n# set error AND out log to null in cluster\n$pm2 start echo.js -i 1 -o /dev/null -e /dev/null --merge-logs\n\nsleep 1\n\n! test -f ~/.pm2/logs/echo-out.log\nspec \"out log shouldnt exist with /dev/null in cluster mode\"\n! test -f ~/.pm2/logs/echo-error.log\nspec \"error log shouldnt exist with /dev/null in cluster mode\"\n\n$pm2 delete all\n\n# set error AND out log to null in fork\n$pm2 start echo.js -o /dev/null -e /dev/null --merge-logs\n\nsleep 1\n\n! test -f ~/.pm2/logs/echo-out.log\nspec \"out log shouldnt exist with /dev/null in fork mode\"\n! test -f ~/.pm2/logs/echo-error.log\nspec \"error log shouldnt exist with /dev/null in fork mode\"\n\n$pm2 delete all\n"
  },
  {
    "path": "test/e2e/logs/log-reload.sh",
    "content": "#!/usr/bin/env bash\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\n\ncd $file_path\n\n## FORK MODE\n\n$pm2 kill\n\n$pm2 start echo.js -o out-rel.log -e err-rel.log -x --merge-logs\n\nsleep 2\n\ngrep \"echo.js\" out-rel.log\nspec \"Should have written te right stuff in out log in fork mode\"\n\ngrep \"echo.js-error\" err-rel.log\nspec \"Should have written te right stuff in err log in fork mode\"\n\nrm out-rel.log\nrm err-rel.log\n\n$pm2 reloadLogs\nspec \"Should have reloaded logs via CLI\"\n\nsleep 1\n\ngrep \"echo.js\" out-rel.log\nspec \"(RELOADED) Should have written the right stuff in out log in fork mode\"\n\ngrep \"echo.js-error\" err-rel.log\nspec \"(RELOADED) Should have written the right stuff in err log in fork mode\"\n\nrm out-rel.log\nrm err-rel.log\n"
  },
  {
    "path": "test/e2e/logs/log-timestamp.sh",
    "content": "#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\n\nfunction head {\n  echo -e \"\\x1B[1;35m$1\\x1B[0m\"\n}\nfunction rm_pm2log {\n  if [ \"$1\" -ne 1 ]; then\n    $pm2 kill\n    rm -rf ~/.pm2/pm2.log\n  fi\n}\nfunction grep_log {\n\n    echo \"travis\"\n    eval \"$pm2 $1 >| pm2.log\"\n    sleep 0.3\n    OUT=`cat pm2.log | grep -n \"[0-9]\\{4\\}\\-[0-9]\\{2\\}\\-[0-9]\\{2\\}\" | wc -l`\n}\nfunction no_prefix {\n  eval \"grep_log \\\"$1\\\"\"\n  echo \"line count: $OUT\"\n  [ $OUT -eq 0 ] || fail \"expect no timestamp prefix in pm2.log, but currently existing.\"\n  success \"have no timestamp prefix\"\n  rm_pm2log \"$2\"\n}\nfunction prefix {\n  eval \"grep_log \\\"$1\\\"\"\n  echo \"line count: $OUT\"\n  [ $OUT -ne 0 ] || fail \"expect have timestamp prefix in pm2.log, but currently does not exist.\"\n  success \"have timestamp prefix\"\n\n  rm_pm2log \"$2\"\n}\n\ncd $file_path\n\n$pm2 kill\n\nsleep 0.5\n\n$pm2 flush\n\nunset PM2_LOG_DATE_FORMAT\nexport PM2_LOG_DATE_FORMAT=\"\"\n\nhead \">> LIST (NO PREFIX)\"\nno_prefix \"ls\" 0\n\nhead \">> START (NO PREFIX)\"\nno_prefix \"start echo.js\" 1\n\nhead \">> RESTART (NO PREFIX)\"\nno_prefix \"restart echo\" 1\n\nhead \">> STOP (NO PREFIX)\"\nno_prefix \"stop echo\" 0\n\nhead \">> START JSON (NO PREFIX)\"\nno_prefix \"start echo-pm2.json\" 1\n\nhead \">> RESTART JSON (NO PREFIX)\"\nno_prefix \"restart echo-pm2.json\" 1\n\nhead \">> STOP-JSON (NO PREFIX)\"\nno_prefix \"stop echo-pm2.json\" 0\n\nexport PM2_LOG_DATE_FORMAT=\"YYYY-MM-DD HH:mm Z\"\n\nhead \">> LIST (PREFIX)\"\nprefix \"ls\" 0\n\nhead \">> START (PREFIX)\"\nprefix \"start echo.js\" 1\n\nhead \">> RESTART (PREFIX)\"\nprefix \"restart echo\" 1\n\nhead \">> STOP (PREFIX)\"\nprefix \"stop echo\" 0\n\nhead \">> START JSON (PREFIX)\"\nprefix \"start echo-pm2.json\" 1\n\nhead \">> RESTART JSON (PREFIX)\"\nprefix \"restart echo-pm2.json\" 1\n\nhead \">> STOP-JSON (PREFIX)\"\nprefix \"restart echo-pm2.json\" 0\n\nrm -rf pm2.log\nunset PM2_LOG_DATE_FORMAT\ntouch ~/.pm2/pm2.log\n"
  },
  {
    "path": "test/e2e/misc/cron-system.sh",
    "content": "#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\n\ncd $file_path\n\nPM2_WORKER_INTERVAL=1000 $pm2 update\n$pm2 delete all\n\n#\n# Cron wrong format detection\n#\n$pm2 start cron.js -c \"* * * asdasd\"\nispec \"Cron should throw error when pattern invalid\"\n\n#\n# Cron restart in fork mode\n#\n$pm2 start cron.js -c \"*/2 * * * * *\" --no-vizion\nspec \"Should cron restart echo.js\"\nsleep 2\nshould 'should app been restarted' 'restart_time: 0' 0\n\n$pm2 restart cron\n$pm2 reset all\nsleep 4\nshould 'should app been restarted after restart' 'restart_time: 0' 0\n\n$pm2 reset cron\n$pm2 stop cron\nsleep 4\nshould 'should app be started again' 'online' 1\n\n$pm2 delete cron\nsleep 4\nshould 'should app not be started again' 'stopped' 0\nshould 'should app not be started again' 'online' 0\n\n$pm2 delete all\n\n#\n# Cron restart in cluster mode\n#\n$pm2 start cron.js -i 1 -c \"*/2 * * * * *\"\nspec \"Should start app\"\nsleep 2\nshould 'should app been restarted' 'restart_time: 0' 0\n$pm2 reset all\nsleep 3\nshould 'should app been restarted a second time' 'restart_time: 0' 0\n\n$pm2 delete all\n\n#\n# Cron after resurect\n#\n$pm2 start cron.js -i 1 -c \"*/2 * * * * *\"\nspec \"Should start app\"\nsleep 2\nshould 'should app been restarted' 'restart_time: 0' 0\n\n$pm2 update\n$pm2 reset all\nsleep 4\nshould 'should app been restarted' 'restart_time: 0' 0\n\n$pm2 delete all\n\n#\n# Cron every sec\n#\n$pm2 start cron.js -c \"* * * * * *\"\nsleep 4\nshould 'should app been restarted' 'restart_time: 0' 0\n\n#\n# Delete cron\n#\n$pm2 restart cron --cron-restart 0\n$pm2 reset all\nsleep 2\nshould 'app stop be restarted' 'restart_time: 0' 1\n\n$pm2 delete all\n"
  },
  {
    "path": "test/e2e/misc/inside-pm2.sh",
    "content": "#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\n\ncd $file_path\n\n####################################################################\n# Check that we can start a process from inside a PM2 watched app. #\n####################################################################\n\nTEST_VARIABLE='hello1' $pm2 start startProcessInsidePm2.json\n>inside-out.log\n\n# sleep 1\n# should 'start master process' 'pm_id: 0' 2\n\nsleep 1\n\n$pm2 list\n\nshould 'child process should be started' 'pm_id: 1' 2\nshould 'restarted status should be zero' \"restart_time: 0\" 2\n\ngrep \"hello1\" inside-out.log &> /dev/null\nspec \"Child should have hello1 variable\"\n\nTEST_VARIABLE='hello2' $pm2 restart \"insideProcess\" --update-env\nsleep 1\ngrep \"hello2\" inside-out.log &> /dev/null\nspec \"Child should have hello2 variable after restart\"\n\n# Call bash script that restarts app\n$pm2 delete all\n\n$pm2 start echo.js\nsleep 1\n\nexport PM2_PATH=$pm2\n$pm2 start inside/inner_restart.sh --no-autorestart\nsleep 2\nshould 'restarted status should be one' \"restart_time: 3\" 1\n"
  },
  {
    "path": "test/e2e/misc/instance-number.sh",
    "content": "#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\ncd $file_path\n\n$pm2 start server.js -i -100\nshould 'should have started 1 processes' 'online' 1\n\n$pm2 delete all\n"
  },
  {
    "path": "test/e2e/misc/misc.sh",
    "content": "#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\n\ncd $file_path\n\n## Try to launch an app with `push` as name\n$pm2 kill\n\n$pm2 start push.json\n\n$pm2 stop push.json\n\n$pm2 list\n\n#\n# Max memory auto restart option\n#\n# --max-memory-restart option && maxMemoryRestart (via JSON file)\n#\n$pm2 kill\nPM2_WORKER_INTERVAL=1000 $pm2 start big-array.js --max-memory-restart=\"20M\"\nsleep 3\n$pm2 list\nshould 'process should have been restarted' 'restart_time: 0' 0\n\n$pm2 delete all\n\n#\n# Via JSON\n#\n$pm2 start json-reload/max-mem.json\nsleep 3\n$pm2 list\nshould 'process should been restarted' 'restart_time: 0' 0\n\n$pm2 delete all\n\n$pm2 start env.js\n\nOUT_LOG=`$pm2 prettylist | grep -m 1 -E \"pm_out_log_path:\" | sed \"s/.*'\\([^']*\\)',/\\1/\"`\n\ncat /dev/null > $OUT_LOG\n\nsleep 1\n\nOUT=`cat $OUT_LOG | head -n 1`\n\nif [ $OUT=\"undefined\" ]\nthen\n    success \"environment variable not defined\"\nelse\n    fail \"environment defined ? wtf ?\"\nfi\n\n$pm2 delete all\n\n$pm2 start env.json\n\ncat /dev/null > $OUT_LOG\n\nsleep 1\n\nOUT=`cat $OUT_LOG | head -n 1`\n\nif [ \"$OUT\" = \"undefined\" ]\nthen\n    fail \"environment variable hasnt been defined\"\nelse\n    success \"environment variable successfully defined\"\nfi\n\n\n#####################\n# Merge logs option #\n#####################\n$pm2 kill\n\nrm outmerge*\n\n$pm2 start echo.js -i 4 -o outmerge.log\n\ncat outmerge.log > /dev/null\nispec 'file outmerge.log should not exist'\n\ncat outmerge-0.log > /dev/null\nspec 'file outmerge-0.log should exist'\n\nrm outmerge*\n\n############ Now with --merge option\n\n$pm2 kill\n\nrm outmerge*\n\n$pm2 start echo.js -i 4 -o outmerge.log --merge-logs\nsleep 0.2\ncat outmerge.log > /dev/null\nspec 'file outmerge.log should exist'\n\ncat outmerge-0.log > /dev/null\nispec 'file outmerge-0.log should not exist'\n\nrm outmerge*\n"
  },
  {
    "path": "test/e2e/misc/nvm-node-version.sh",
    "content": "#!/usr/bin/env bash\n\necho \"feature in deprecation\"\nexit 0\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\n\ncd $file_path/nvm-node-version\n\nfunction getInterpreter() {\n\techo `$pm2 prettylist | grep \"exec_interpreter:\" | awk -F\"'\" '{print $2}'`\n}\n\n\n$pm2 start ecosystem.json\nsleep 1\n\nOCC=$($pm2 prettylist | grep \"exec_interpreter\" | grep 'v4.6.0\\|v6.7.0' | wc -l)\n[ $OCC -eq 2 ] || fail \"Errors in setting interpreters\"\nsuccess \"Success\"\n\n$pm2 restart ecosystem.json\n\nshould 'should have 2 apps online' 'online' 2\nOCC=$($pm2 prettylist | grep \"exec_interpreter\" | grep 'v4.6.0\\|v6.7.0' | wc -l)\n[ $OCC -eq 2 ] || fail \"Errors in setting interpreters\"\nsuccess \"Success\"\n\n$pm2 restart all\nsleep 0.5\nshould 'should have 2 apps online' 'online' 2\nOCC=$($pm2 prettylist | grep \"exec_interpreter\" | grep 'v4.6.0\\|v6.7.0' | wc -l)\n[ $OCC -eq 2 ] || fail \"Errors in setting interpreters\"\nsuccess \"Success\"\n\n# Update node.js version\n$pm2 restart ecosystem-change.json\nOCC=$($pm2 prettylist | grep \"exec_interpreter\" | grep 'v4.5.0\\|v6.7.0' | wc -l)\n[ $OCC -eq 2 ] || fail \"Errors in setting interpreters\"\nsuccess \"Success\"\n\n$pm2 restart all\nsleep 0.5\nshould 'should have 2 apps online' 'online' 2\nOCC=$($pm2 prettylist | grep \"exec_interpreter\" | grep 'v4.5.0\\|v6.7.0' | wc -l)\n[ $OCC -eq 2 ] || fail \"Errors in setting interpreters\"\nsuccess \"Success\"\n"
  },
  {
    "path": "test/e2e/misc/port-release.sh",
    "content": "#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\ncd $file_path\n\n$pm2 start cluster-pm2.json\nshould 'should have started 4 processes' 'online' 4\n\n$pm2 reload cluster-pm2.json\nshould 'should have started 4 processes' 'online' 4\n"
  },
  {
    "path": "test/e2e/misc/startup.sh",
    "content": "#!/usr/bin/env bash\n\nif [ \"$EUID\" -ne 0 ]\nthen\n    echo \"Please run as root\"\n    exit\nfi\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\n\ncd $file_path\n$pm2 startup upstart -u $USER --hp $HOME --service-name abcdef\nspec \"should startup command generation exited succesfully with custom service-name\"\ntest -e /etc/init.d/abcdef\nspec \"should have generated upstart file with custom service-name\"\n$pm2 unstartup upstart --service-name abcdef\nspec \"should have disabled startup with custom service-name\"\n! test -e /etc/init.d/abcdef\nspec \"should have deleted upstart file with custom service-name\"\n\n$pm2 startup upstart -u $USER --hp $HOME\nspec \"should startup command generation exited succesfully\"\ntest -e /etc/init.d/pm2-$USER\nspec \"should have generated upstart file\"\n$pm2 unstartup upstart\nspec \"should have disabled startup\"\n! test -e /etc/init.d/pm2-$USER\nspec \"should have deleted upstart file\"\n"
  },
  {
    "path": "test/e2e/misc/versioning-cmd.sh",
    "content": "#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\n\ncd $file_path\n\nrm -rf app-playground\n\ngit clone https://github.com/keymetrics/app-playground.git\n\ncd app-playground\n\n$pm2 start package.json\n\nCUR_HASH=`$pm2 prettylist | grep \"revision\" | cut -d: -f2 | tr -d \" ,'\"`\nHEAD_HASH=$CUR_HASH\necho \"CURRENT GIT HASH= \" $CUR_HASH\nGIT_HASH=`git rev-parse HEAD`\n[ $CUR_HASH == $GIT_HASH ] || fail \"Wrong commit\"\nspec \"Right commit after start\"\n\n#\n# Backward\n#\n\n$pm2 backward \"keymetrics tuto\"\n\nsleep 1\n\nCUR_HASH=`$pm2 prettylist | grep \"revision\" | cut -d: -f2 | tr -d \" ,'\"`\necho \"CURRENT GIT HASH= \" $CUR_HASH\nGIT_HASH=`git rev-parse HEAD`\n[ $CUR_HASH == $GIT_HASH ] || fail \"Wrong commit\"\nspec \"Right commit after backward action\"\n\n\n#\n# Backward\n#\n\n$pm2 backward \"keymetrics tuto\"\n\nsleep 1\n\nCUR_HASH=`$pm2 prettylist | grep \"revision\" | cut -d: -f2 | tr -d \" ,'\"`\nDEEP_HASH=$CUR_HASH\necho \"CURRENT GIT HASH= \" $CUR_HASH\nGIT_HASH=`git rev-parse HEAD`\n[ $CUR_HASH == $GIT_HASH ] || fail \"Wrong commit\"\nspec \"Right commit after backward action\"\n\n#\n# Forward\n#\n$pm2 forward \"keymetrics tuto\"\n\nsleep 1\n\nCUR_HASH=`$pm2 prettylist | grep \"revision\" | cut -d: -f2 | tr -d \" ,'\"`\necho \"CURRENT GIT HASH= \" $CUR_HASH\nGIT_HASH=`git rev-parse HEAD`\n[ $CUR_HASH == $GIT_HASH ] || fail \"Wrong commit\"\nspec \"Right commit after backward action\"\n\n#\n# Pull to HEAD\n#\n$pm2 pull \"keymetrics tuto\"\nsleep 1\n\nCUR_HASH=`$pm2 prettylist | grep \"revision\" | cut -d: -f2 | tr -d \" ,'\"`\necho \"CURRENT GIT HASH= \" $CUR_HASH\nGIT_HASH=`git rev-parse HEAD`\n[ $CUR_HASH == $GIT_HASH ] || fail \"Wrong commit\"\nspec \"Right commit after pullAndReload\"\n\n[ $CUR_HASH == $HEAD_HASH ] || fail \"Wrong commit\"\nspec \"Is updated with right hash\"\n\n\n#\n# Pull to commit id\n#\n$pm2 pull \"keymetrics tuto\" $DEEP_HASH\nsleep 1\n\nCUR_HASH=`$pm2 prettylist | grep \"revision\" | cut -d: -f2 | tr -d \" ,'\"`\necho \"CURRENT GIT HASH= \" $CUR_HASH\nGIT_HASH=`git rev-parse HEAD`\n[ $CUR_HASH == $GIT_HASH ] || fail \"Wrong commit\"\nspec \"Right commit after pullAndReload\"\n\n[ $CUR_HASH == $DEEP_HASH ] || fail \"Wrong commit\"\nspec \"Is updated with old hash\"\n\ncd ..\nrm -rf app-playground\n"
  },
  {
    "path": "test/e2e/misc/vizion.sh",
    "content": "#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\n\ncd $file_path\n\necho \"################## VIZION ###################\"\n\nmv git .git\n\n###############\n$pm2 kill\n$pm2 start echo.js\nsleep 1\n\nshould 'should have versioning metadata' 'jshkurti/pm2_travis' 1\n\n$pm2 delete all\n$pm2 start echo.js --no-vizion\nsleep 1\n\nshould 'should not have versioning metadata' 'jshkurti/pm2_travis' 0\n\n$pm2 delete all\n$pm2 start no-vizion.json\nsleep 1\n\nshould 'should not have versioning metadata' 'jshkurti/pm2_travis' 0\n\nmv .git git\n"
  },
  {
    "path": "test/e2e/modules/get-set.sh",
    "content": "#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\n\ncd $file_path\n\n$pm2 unset all\nspec \"Should unset all variables\"\n\nls ~/.pm2/module_conf.json\nspec \"Should file exists\"\n\n$pm2 get\n\n$pm2 set key1 val1\ncat ~/.pm2/module_conf.json | grep \"key1\"\nspec \"Should key exists\"\n\n$pm2 unset key1\ncat ~/.pm2/module_conf.json | grep \"key1\"\nispec \"Should key does not exist\"\n\nrm -rf ~/.pm2\n"
  },
  {
    "path": "test/e2e/modules/module-safeguard.sh",
    "content": "#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\n\ncd $file_path\n\n#\n# Re init module system\n#\n$pm2 kill\nrm -rf ~/.pm2\n#\n#\n#\n$pm2 ls\n$pm2 install pm2-sample-module@2.3.5\nspec \"Should have installed module\"\n\nsleep 1\nshould 'should have started module' 'online' 1\nshould 'should module be in stable state' 'restart_time: 0' 1\n#should 'should module be on the right version' \"module_version: '2.3.5'\" 1\n\n$pm2 install pm2-sample-module@2.2.5 --safe\nispec \"Should installation of unstable module fail (npm installation has failed)\"\nshould 'should have restored module to previous version and online' 'online' 1\nshould 'should module be in stable state' 'restart_time: 0' 1\n#should 'should module be on the right version' \"module_version: '2.3.5'\" 1\n\n$pm2 install pm2-sample-module@2.3.5 --safe\nspec \"Should installation of unstable module fail (module bad behavior (restart))\"\nshould 'should have restored module to previous version and online' 'online' 1\nshould 'should module be in stable state' 'restart_time: 0' 1\n#should 'should module be on the right version' \"module_version: '2.3.5'\" 1\n\n#\n# Test edge cases\n#\n$pm2 uninstall all\n"
  },
  {
    "path": "test/e2e/modules/module.sh",
    "content": "#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\n\ncd $file_path\n\n#\n# Re init module system\n#\nrm -rf ~/.pm2/node_modules\n$pm2 kill\n#\n#\n#\n\n$pm2 unset pm2-probe\n\n$pm2 set 'pm2-probe:config1xxx' true\n\n$pm2 install pm2-probe@latest\nspec \"Should install a module\"\nshould 'should app be online' 'online' 1\n\n$pm2 install pm2-probe@latest\nspec \"Should update a module\"\nshould 'should app be online' 'online' 1\n\nls ~/.pm2/modules/pm2-probe\nspec \"Module should be installed\"\n\n\n# Default configuration variable in package.json (under \"config\" attribute)\nshould 'should have default config variable via package.json' \"var2: false\" 4 3\n\n#\n# Should configuration variable be present two times\n# one time in the raw env, and a second time prefixed with the module name\n#\nexists '1# should have config variable' \"config1xxx: 'true'\" 6\n\n#\n# Change variable value\n#\n\n$pm2 set 'pm2-probe:config1xxx' false\n\nsleep 1\n\nexists '2# should have config variable' \"config1xxx: 'false'\" 4\n\n$pm2 update\nspec \"Should update successfully\"\nshould 'and module still online' 'online' 1\n\n$pm2 kill\nspec \"Should kill pm2\"\n\n$pm2 list\nspec \"Should resurrect pm2\"\nshould 'and module still online' 'online' 1\n\n\n$pm2 delete all\nshould 'should module status not be modified' 'online' 1\n\n$pm2 stop all\nshould 'should module status not be modified' 'online' 1\n\n$pm2 stop pm2-probe\nshould 'should module be possible to stop' 'stopped' 1\n\n$pm2 uninstall pm2-probe\nspec \"Should uninstall a module\"\nshould 'should module not be online' 'online' 0\n\nls ~/.pm2/modules/pm2-probe\nispec \"Module should be deleted\"\n\n$pm2 update\nshould 'should module not be online' 'online' 0\n\n#\n# Module test\n#\n\ncd module-fixture\n\n$pm2 kill\n\n# Unset all possible variables for module\n$pm2 unset example-module\n\n# Install local module in development mode\n$pm2 install .\nsleep 0.5\nspec 'Should have installed module'\n\n\n# # Override environment variable\n# $pm2 set example-module:var2 true\n# sleep 0.5\n# should 'should module been restarted after setting variable' 'restart_time: 1' 1\n\n# # 4 occurences because of a restart\n# should 'should have config variable modified' \"var2: 'true'\" 4\n\n# $pm2 set example-module:newvar true\n# sleep 0.5\n# should 'should module been restarted after setting variable' 'restart_time: 2' 1\n\n# # 4 occurences because of a restart\n# should 'should have config variable modified' \"newvar: 'true'\" 4\n"
  },
  {
    "path": "test/e2e/process-file/app-config-update.sh",
    "content": "#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\n\nexport PM2_GRACEFUL_TIMEOUT=1000\nexport PM2_GRACEFUL_LISTEN_TIMEOUT=1000\n\ncd $file_path\n\n$pm2 kill\n\n$pm2 start app-config-update/args1.json\n$pm2 prettylist | grep \"node_args: \\[\\]\"\nspec \"1 Should application have empty node argument list\"\n\n$pm2 restart app-config-update/args2.json\n$pm2 prettylist | grep \"node_args: \\[ '--harmony' \\]\"\nspec \"2 Should application have one node argument\"\n\n$pm2 delete all\n\n$pm2 start app-config-update/echo.js\n$pm2 prettylist | grep \"node_args: \\[\\]\"\nspec \"3 Should application have empty node argument list\"\n\n$pm2 restart app-config-update/echo.js --node-args=\"--harmony\"\n$pm2 prettylist | grep \"node_args: \\[ '--harmony' \\]\"\nspec \"4 Should application have one node argument\"\n\n# Variation with pm2 start that restarts an app\n$pm2 start echo --node-args=\"--harmony\"\n$pm2 prettylist | grep \"node_args: \\[ '--harmony' \\]\"\nspec \"5 Should application have one node argument\"\n\n#\n# Rename\n#\n$pm2 restart 0 --name=\"new-name\"\n$pm2 reset all\n$pm2 restart new-name\nshould '6 should restart processes with new name' 'restart_time: 1' 1\n\n$pm2 start 0 --name=\"new-name-2\"\n$pm2 reset all\n$pm2 restart new-name-2\nshould '7 should restart processes with new name' 'restart_time: 1' 1\n\n$pm2 delete all\n\n########## RELOAD/CLUSTER MODE #########\n\n$pm2 start app-config-update/echo.js -i 1\n$pm2 prettylist | grep \"node_args: \\[\\]\"\nspec \"Should application have empty node argument list\"\n\n$pm2 reload app-config-update/echo.js --node-args=\"--harmony\"\n$pm2 prettylist | grep \"node_args: \\[ '--harmony' \\]\"\nspec \"Should application have one node argument\"\n\n$pm2 prettylist | grep \"node_args\"\nspec \"Should have found parameter\"\n# Now set node-args to null\n$pm2 reload app-config-update/echo.js --node-args=null\n# Should not find node_args anymore\n$pm2 prettylist | grep \"node_args\"\nispec \"Should have deleted cli parameter when passing null\"\n\n$pm2 reload echo --name=\"new-name\"\n$pm2 reset all\n$pm2 restart new-name\nshould 'should reload processes with new name' 'restart_time: 1' 1\n"
  },
  {
    "path": "test/e2e/process-file/append-env-to-name.sh",
    "content": "#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\n\ncd $file_path\n\n## Test #1\n\n$pm2 start append-env-to-name.json --env dev\nshould 'have started app with name web-dev' \"name: 'web-dev'\" 3\n\n$pm2 start append-env-to-name.json --env prod\nshould 'have started same app with name : web-prod' \"name: 'web-prod'\" 3\n"
  },
  {
    "path": "test/e2e/process-file/homogen-json-action.sh",
    "content": "#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\n\ncd $file_path\n\n$pm2 kill\n\ncd homogen-json-action\n\n$pm2 start all.json\nshould 'should start process' 'online' 6\nshould 'should all script been restarted 0 time' 'restart_time: 0' 6\n\n$pm2 start all.json\nshould 'should smart restart processes' 'online' 6\nshould 'should all script been restarted one time' 'restart_time: 1' 6\n\n$pm2 restart all.json\nshould 'should all script been restarted one time' 'restart_time: 2' 6\n\n$pm2 reload all.json\nshould 'should all script been restarted one time' 'restart_time: 3' 6\n\n# With restart should equal a start\n$pm2 delete all\n\n$pm2 restart all.json\nshould 'should start process' 'online' 6\nshould 'should all script been restarted 0 time' 'restart_time: 0' 6\n\n# With reload should equal a start\n$pm2 delete all\n\n$pm2 reload all.json\nshould 'should start process' 'online' 6\nshould 'should all script been restarted 0 time' 'restart_time: 0' 6\n"
  },
  {
    "path": "test/e2e/process-file/js-configuration.sh",
    "content": "#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\ncd $file_path/js-configuration\n\n$pm2 start ecosystem.config.js\nshould 'should have started 1 processes' 'online' 1\n"
  },
  {
    "path": "test/e2e/process-file/json-file.sh",
    "content": "#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\ncd $file_path\n\necho -e \"\\033[1mRunning tests for json files :\\033[0m\"\n\n## alias \"apps\" to \"pm2\" = nicer for package.json\n$pm2 start pm2-ecosystem.json\nshould 'should start processes' 'online' 6\n\n$pm2 delete all.json\nshould 'should delete all processes' 'name' 0\n\n$pm2 kill\n\nPM2_WORKER_INTERVAL=90000 $pm2 start all.json\nshould 'should start processes' 'online' 6\n\n$pm2 stop all.json\nshould 'should stop processes' 'stopped' 6\n\n$pm2 delete all.json\nshould 'should delete all processes' 'name' 0\n\n$pm2 start all.json\nshould 'should start processes' 'online' 6\n\n$pm2 restart all.json\nshould 'should stop processes' 'online' 6\nshould 'should all script been restarted one time' 'restart_time: 1' 6\n\n$pm2 reload all.json\nsleep 1\nshould 'should reload processes' 'online' 6\nshould 'should all script been restarted one time' 'restart_time: 2' 6\n\n##\n## Smart restart\n##\n$pm2 start all.json\nsleep 1\nshould 'should smart restart processes' 'online' 6\nshould 'should all script been restarted one time' 'restart_time: 3' 6\n\n$pm2 stop all.json\nsleep 1\nshould 'should stop processes' 'stopped' 6\n\n$pm2 start all.json\nshould 'should smart restart processes' 'online' 6\n\n# $pm2 stop all.json\n# sleep 1\n# should 'should stop processes' 'stopped' 6\n\n# $pm2 start all\n# should 'should smart restart processes' 'online' 6\n\n$pm2 kill\n\n########## JS style\n\nPM2_WORKER_INTERVAL=90000 $pm2 start configuration.json\nshould 'should start processes' 'online' 6\n\n$pm2 stop configuration.json\nshould 'should stop processes' 'stopped' 6\n\n$pm2 delete configuration.json\nshould 'should start processes' 'online' 0\n\n$pm2 start configuration.json\nshould 'should start processes' 'online' 6\n\n$pm2 restart configuration.json\nshould 'should stop processes' 'online' 6\nshould 'should all script been restarted one time' 'restart_time: 1' 6\n\n$pm2 delete configuration.json\nshould 'should delete processes' 'online' 0\n\n########## PIPE command\n\n$pm2 kill\n\ncat all.json | $pm2 start -\nshould 'should start processes' 'online' 6\n\n$pm2 kill\n\n######### --only <app_name> option\n\n$pm2 start all.json --only echo\nshould 'should start processes' 'online' 1\n\n$pm2 start all.json --only child\nshould 'should start processes' 'online' 5\n\n$pm2 restart all.json --only child\nshould 'should start processes' 'online' 5\nshould 'should all script been restarted one time' 'restart_time: 1' 4\n\n$pm2 delete all.json --only echo\nshould 'should start processes' 'online' 4\n\n$pm2 reload all.json --only child\nshould 'should all script been restarted one time' 'restart_time: 2' 4\n\n######## multu only\n\n$pm2 start all.json --only \"echo,child\"\nshould 'should start processes' 'online' 5\n\n$pm2 kill\n"
  },
  {
    "path": "test/e2e/process-file/json-reload.sh",
    "content": "\n#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\n\ncd $file_path\n\ncd json-reload\n\n#\n# Max memory auto restart option\n#\n# --max-memory-restart option && maxMemoryRestart (via JSON file)\n#\n$pm2 kill\n\nPM2_WORKER_INTERVAL=1000 $pm2 start max-mem-0.json\nsleep 2\n$pm2 list\nshould 'process should has not been restarted' 'restart_time: 0' 1\n\n$pm2 restart max-mem.json\n\nsleep 2\n$pm2 list\nshould 'process should has been restarted' 'restart_time: 0' 0\n\n#\n# Date format change\n#\n$pm2 delete all\n\nCURRENT_YEAR=`date +\"%Y\"`\n>echo-test.log\n\n$pm2 start echo-pre.json\nsleep 1\ngrep $CURRENT_YEAR echo-test.log\nspec \"Should have written year in log file according to format YYYY\"\ngrep \"ok\" echo-test.log\nspec \"Should have written new string depending on ECHO_MSG\"\n\n$pm2 restart echo-post.json\n>echo-test.log\nsleep 1\n\ngrep $CURRENT_YEAR echo-test.log\nispec \"Should have written year in log file according to format\"\n\ngrep \"YAY\" echo-test.log\nspec \"Should have written new string depending on ECHO_MSG\"\n\n# Switch to production environment\n$pm2 restart echo-post.json --env production\n>echo-test.log\nsleep 1\ngrep \"WOW\" echo-test.log\nspec \"Should have written new string depending on ECHO_MSG\"\n\n#\n# Switch to production environment\n#\n$pm2 reload echo-post.json --env production\n>echo-test.log\nsleep 1\ngrep \"WOW\" echo-test.log\nspec \"Should have written new string depending on ECHO_MSG\"\n\n#\n# Go back to original environment\n#\n$pm2 restart echo-post.json\nsleep 1\ngrep \"YAY\" echo-test.log\nspec \"Should have written new string depending on ECHO_MSG\"\n"
  },
  {
    "path": "test/e2e/process-file/yaml-configuration.sh",
    "content": "#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\ncd $file_path/yaml-configuration\n\n$pm2 start non-existent.yaml\nshould 'should have started 0 processes because file unknown' 'online' 0\n\n$pm2 start malformated.yml\nshould 'should have started 0 processes because file malformated' 'online' 0\n\n$pm2 start apps.yaml\nshould 'should have started 6 processes' 'online' 6\n\n$pm2 restart all\nshould 'should have restarted 6 processes' 'restart_time: 1' 6\n\n$pm2 restart apps.yaml\nshould 'should have restarted 6 processes' 'restart_time: 2' 6\n\n$pm2 reload all\nshould 'should have reloaded 6 processes' 'restart_time: 3' 6\n\n$pm2 reload apps.yaml\nshould 'should have reloaded 6 processes' 'restart_time: 4' 6\n\n$pm2 stop all\nshould 'should have reloaded 6 processes' 'stopped' 6\n\n$pm2 start apps.yaml\n$pm2 delete all\nshould 'should have deleted 6 processes' 'online' 0\n"
  },
  {
    "path": "test/e2e/pull.sh",
    "content": "#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/../include.sh\"\n\ncd $file_path\n\n$pm2 interact kill\n\n#\n# Testing pull,forward,backward methods\n#\n\nif [ $TRAVIS ]\nthen\n    git config --global user.email \"jshkurti@student.42.fr\"\n    git config --global user.name \"jshkurti\"\nfi\n\nrm -rf ./app-playground\n\ngit clone https://github.com/keymetrics/app-playground.git\n\ncd app-playground\n\ngit checkout hotfix\n\n# set max_memory_restart to 160M by default. MAC does not support `sed -i`\necho `sed -e 's/\"160\"/\"160M\"/' process.json` >| process.json\n\n$pm2 start ./process.json --name app\nsleep 5\n\n\nOUT=`$pm2 ls | grep errored | wc -l`\n[ $OUT -eq 1 ] || fail \"Process should be errored because node_modules are missing\"\nsuccess \"Process should be errored because node_modules are missing\"\n\nOUT=`$pm2 info 0 | grep remote | egrep -oh 'https://([^ ]+)'`\n[ $OUT = \"https://github.com/keymetrics/app-playground.git\" ] || fail \"Remote URL should be right\"\nsuccess \"Remote URL should be right\"\n\nOUT=`$pm2 backward app | wc -l`\n[ $OUT -eq 13 ] || fail \"Backward method should work properly and print adequate output\"\nsuccess \"Backward method should work properly and print adequate output\"\n\nOUT=`$pm2 forward app | wc -l`\n[ $OUT -eq 13 ] || fail \"Forward method should work properly and print adequate output\"\nsuccess \"Forward method should work properly and print adequate output\"\n\nOUT=`$pm2 forward app | wc -l`\n[ $OUT -eq 2 ] || fail \"Forward method should fail and thus print 2-lined output\"\nsuccess \"Forward method should fail and thus print 2-lined output\"\n\nOUT=`$pm2 pull app | wc -l`\n[ $OUT -eq 2 ] || fail \"Pull method should 'fail' because it is already up-to-date\"\nsuccess \"Pull method should 'fail' because it is already up-to-date\"\n\nexport PM2_WORKER_INTERVAL=1000\n\n$pm2 kill\n\n$pm2 start ./process.json --name app\nsleep 5\n\n#\n# Testing refresh-versioning worker\n#\n\nOUT=`$pm2 jlist | egrep -oh '\"unstaged\":true' | wc -c`\n[ $OUT -eq 16 ] || fail \"Worker: unstaged flag should be true\"\nsuccess \"Worker: unstaged flag should be true\"\n\ngit add --all\ngit commit -m 'staged now'\nsleep 5\nOUT=`$pm2 jlist | egrep -oh '\"unstaged\":false' | wc -c`\n[ $OUT -eq 17 ] || fail \"Worker: unstaged flag should be false this time\"\nsuccess \"Worker: unstaged flag should be false this time\"\n\nOUT=`$pm2 jlist | egrep -oh '\"ahead\":true' | wc -c`\n[ $OUT -eq 13 ] || fail \"Worker: ahead flag should be true\"\nsuccess \"Worker: ahead flag should be true\"\n\nOUT=`$pm2 pull app 83dfc32383a84e146005d8981bcae2c52a5b123b | egrep -oh 'Current commit 83dfc32383a84e146005d8981bcae2c52a5b123b' | wc -c`\n[ $OUT -eq 56 ] || fail \"Commit ID should be correct\"\nsuccess \"Commit ID should be correct\"\n\n$pm2 kill\ncd ..\nrm -rf ./app-playground\n"
  },
  {
    "path": "test/e2e.sh",
    "content": "#!/usr/bin/env bash\n\nexport PM2_SILENT=\"true\"\n\nSRC=$(cd $(dirname \"$0\"); pwd)\nsource \"${SRC}/e2e/include.sh\"\n\n# Abort script at first error\nset -e\n\ntouch e2e_time\n> e2e_time\n\n# CLI\nrunTest ./test/e2e/cli/reload.sh\nrunTest ./test/e2e/cli/start-app.sh\nrunTest ./test/e2e/cli/operate-regex.sh\n#runTest ./test/e2e/cli/bun.sh\nrunTest ./test/e2e/cli/app-configuration.sh\nrunTest ./test/e2e/cli/binary.sh\nrunTest ./test/e2e/cli/startOrX.sh\nrunTest ./test/e2e/cli/reset.sh\nrunTest ./test/e2e/cli/env-refresh.sh\n\nrunTest ./test/e2e/cli/extra-lang.sh\nrunTest ./test/e2e/cli/python-support.sh\nrunTest ./test/e2e/cli/multiparam.sh\nrunTest ./test/e2e/cli/smart-start.sh\nrunTest ./test/e2e/cli/args.sh\nrunTest ./test/e2e/cli/attach.sh\nrunTest ./test/e2e/cli/serve.sh\n\nrunTest ./test/e2e/esmodule.sh\n\nrunTest ./test/e2e/cli/monit.sh\nrunTest ./test/e2e/cli/cli-actions-1.sh\nrunTest ./test/e2e/cli/cli-actions-2.sh\nrunTest ./test/e2e/cli/dump.sh\nrunTest ./test/e2e/cli/resurrect.sh\nrunTest ./test/e2e/cli/watch.sh\nrunTest ./test/e2e/cli/right-exit-code.sh\nrunTest ./test/e2e/cli/fork.sh\nrunTest ./test/e2e/cli/piped-config.sh\n\n# PROCESS FILES\nrunTest ./test/e2e/process-file/json-file.sh\nrunTest ./test/e2e/process-file/yaml-configuration.sh\nrunTest ./test/e2e/process-file/json-reload.sh\nrunTest ./test/e2e/process-file/app-config-update.sh\nrunTest ./test/e2e/process-file/js-configuration.sh\n\n# BINARIES\n\n# INTERNALS\nrunTest ./test/e2e/internals/wait-ready-event.sh\nrunTest ./test/e2e/internals/daemon-paths-override.sh\n\n\nif [ \"$IS_BUN\" = false ]; then\n    # runTest ./test/e2e/binaries/pm2-dev.sh\n    # runTest ./test/e2e/binaries/pm2-runtime.sh\n\n    runTest ./test/e2e/process-file/homogen-json-action.sh\n    runTest ./test/e2e/internals/source_map.sh\n    runTest ./test/e2e/internals/wrapped-fork.sh\n    runTest ./test/e2e/logs/log-json.sh\n    runTest ./test/e2e/misc/inside-pm2.sh\n    #runTest ./test/e2e/misc/versioning-cmd.sh\nfi\n\nrunTest ./test/e2e/internals/infinite-loop.sh\nrunTest ./test/e2e/internals/options-via-env.sh\n#runTest ./test/e2e/internals/promise.sh\nrunTest ./test/e2e/internals/increment-var.sh\nrunTest ./test/e2e/internals/start-consistency.sh\n\n# MISC\n#runTest ./test/e2e/misc/vizion.sh\nrunTest ./test/e2e/misc/misc.sh\nrunTest ./test/e2e/misc/instance-number.sh\nrunTest ./test/e2e/misc/startup.sh\nrunTest ./test/e2e/misc/nvm-node-version.sh\nrunTest ./test/e2e/misc/port-release.sh\n## TMP DISABLE\n#runTest ./test/e2e/misc/cron-system.sh\n\n# LOGS\nrunTest ./test/e2e/logs/log-custom.sh\nrunTest ./test/e2e/logs/log-reload.sh\nrunTest ./test/e2e/logs/log-entire.sh\nrunTest ./test/e2e/logs/log-null.sh\n\nrunTest ./test/e2e/logs/log-create-not-exist-dir.sh\nrunTest ./test/e2e/logs/log-namespace.sh\n\n# MODULES\nrunTest ./test/e2e/modules/get-set.sh\nrunTest ./test/e2e/modules/module.sh\nrunTest ./test/e2e/modules/module-safeguard.sh\n\n$pm2 kill\n\necho \"============== e2e test finished ==============\"\ncat e2e_time\n\n# cat ~/.pm2/pm2.log | grep \"PM2 global error caught\"\n# spec \"PM2 Daemon should not have thrown any global error\"\n"
  },
  {
    "path": "test/fixtures/001-test.js",
    "content": "\n\n\nsetInterval(function() {\n  console.log('log message from echo.js');\n  console.error('err msg from echo.js');\n}, 50);\n"
  },
  {
    "path": "test/fixtures/all.json",
    "content": "[{\n  \"name\"      : \"echo\",\n  \"script\"    : \"./echo.js\"\n},{\n  \"name\"      : \"child\",\n  \"script\"    : \"./child.js\",\n  \"instances\" : \"4\",\n  \"error_file\" : \"./child-err.log\",\n  \"out_file\" : \"./child-out.log\"\n},{\n  \"name\"      : \"api-2\",\n  \"script\"    : \"./server.js\"\n}]\n"
  },
  {
    "path": "test/fixtures/all2.json",
    "content": "[{\n  \"name\"      : \"echo\",\n  \"script\"    : \"./echo.js\",\n  \"instances\" : \"1\",\n  \"env_production\" : {\n    \"NODE_ENV\" : \"production\",\n    \"TOTO\" : \"heymoto\"\n  },\n  \"env_test\" : {\n    \"NODE_ENV\" : \"test\",\n    \"TOTO\"     : \"heyamota\"\n  }\n},{\n  \"name\"       : \"child\",\n  \"script\"     : \"./child.js\",\n  \"instances\"  : \"1\",\n  \"error_file\" : \"./child-err.log\",\n  \"out_file\"   : \"./child-out.log\"\n},{\n  \"name\"      : \"api-2\",\n  \"script\"    : \"./server.js\",\n  \"instances\" : \"2\"\n}]\n"
  },
  {
    "path": "test/fixtures/app-config-update/args1.json",
    "content": "{\n  \"script\" : \"echo.js\",\n  \"node_args\" : \"\"\n}\n"
  },
  {
    "path": "test/fixtures/app-config-update/args2.json",
    "content": "{\n  \"script\" : \"echo.js\",\n  \"node_args\" : \"--harmony\"\n}\n"
  },
  {
    "path": "test/fixtures/app-config-update/echo.js",
    "content": "\nvar i = 0;\nsetInterval(function() {\n  console.log('ok', i++);\n}, 2000);\n\nconsole.log('ok');\n"
  },
  {
    "path": "test/fixtures/append-env-to-name.json",
    "content": "[\n  {\n    \"name\": \"web\",\n    \"script\": \"echo.js\",\n    \"append_env_to_name\": true,\n    \"env_dev\": {\n      \"NODE_ENV\": \"dev\",\n      \"PORT\": 4000\n    },\n    \"env_prod\": {\n      \"NODE_ENV\": \"prod\",\n      \"PORT\": 3000\n    }\n  }\n]"
  },
  {
    "path": "test/fixtures/args/echo.js",
    "content": "\nsetInterval(function() {\n  console.log('echo.js');\n}, 5000);\n\nsetInterval(function() {\n  console.error('echo.js-error');\n}, 5000);\n"
  },
  {
    "path": "test/fixtures/args/params_check.js",
    "content": "\nprocess.argv.shift();\nprocess.argv.shift();\n\nprocess.argv.forEach(function(val, index) {\n  console.log(val);\n});\n\nsetInterval(function() { }, 1000);\n"
  },
  {
    "path": "test/fixtures/args.js",
    "content": "\n\nif (process.argv.indexOf('-d') == -1 || process.argv.indexOf('-a') == -1) {\n  process.exit();\n} else {\n  setInterval(function() {\n    console.log('ok');\n  }, 500);\n}\n"
  },
  {
    "path": "test/fixtures/bashscript.sh",
    "content": "while true; do\n    ls -l\n    sleep 5\ndone\n\n"
  },
  {
    "path": "test/fixtures/big-array-es6.js",
    "content": "\n\nvar obj = {};\nvar i = 0;\n\nsetInterval(function() {\n  obj[i] = Array.apply(null, new Array(99999)).map(String.prototype.valueOf,\"hi\");\n  i++;\n}, 2);\n\n\n(function testHarmony() {\n  //\n  // Harmony test\n  //\n  try {\n    var assert = require('assert')\n    , s = new Set();\n    s.add('a');\n    assert.ok(s.has('a'));\n    console.log('● ES6 mode'.green);\n  } catch(e) {}\n})();\n"
  },
  {
    "path": "test/fixtures/big-array-listen.js",
    "content": "\n\nvar obj = {};\nvar i = 0;\n\nvar http = require('http');\n\nhttp.createServer(function(req, res) {\n  res.writeHead(200);\n  res.end('hey');\n}).listen(8000);\n\nprocess.on('message', function(msg) {\n  if (msg == 'shutdown') {\n    console.log('Closing all connections...');\n    setTimeout(function() {\n      console.log('Finished closing connections');\n      process.exit(0);\n    }, 100);\n  }\n});\n\nsetInterval(function() {\n  obj[i] = Array.apply(null, new Array(99999)).map(String.prototype.valueOf,\"hi\");\n  i++;\n}, 2);\n"
  },
  {
    "path": "test/fixtures/big-array.js",
    "content": "\nvar obj = {};\nvar i = 0;\n\nsetInterval(function() {\n  obj[i] = Array.apply(null, new Array(99999)).map(String.prototype.valueOf,\"hi\");\n  i++;\n}, 2);\n"
  },
  {
    "path": "test/fixtures/binary-js-file",
    "content": "setInterval(function () {console.log(\"test\")}, 5000)"
  },
  {
    "path": "test/fixtures/binary-js-file.js",
    "content": "setInterval(function () {console.log(\"test\")}, 5000)"
  },
  {
    "path": "test/fixtures/binary-py-file.py",
    "content": "import time\n\nwhile True:\n    print 'hello'\n    time.sleep(1)\n"
  },
  {
    "path": "test/fixtures/c-compile/hello.c",
    "content": "#include <stdio.h>\nint main()\n{\n  // printf() displays the string inside quotation\n  printf(\"Hello World\\n\");\n  return 0;\n}\n"
  },
  {
    "path": "test/fixtures/change_cwd.json",
    "content": "[{\n  \"name\"      : \"iminpath1\",\n  \"script\"    : \"iminpath1.js\",\n  \"cwd\"       : \"path1\",\n  \"out_file\" : \"./iminpath1.log\"\n},{\n  \"name\" : \"iminpath2\",\n  \"script\"    : \"iminpath2.js\",\n  \"cwd\"       : \"path1/path2\",\n  \"out_file\" : \"./iminpath2.log\"\n}]\n"
  },
  {
    "path": "test/fixtures/child.js",
    "content": "\nvar http = require('http');\n\nhttp.createServer(function(req, res) {\n  res.writeHead(200);\n  res.end('hey');\n}).listen(0);\n\nprocess.on('message', function(msg) {\n  if (msg == 'shutdown') {\n    console.log('Closing all connections...');\n    setTimeout(function() {\n      console.log('Finished closing connections');\n      process.exit(0);\n    }, 100);\n  }\n});\n"
  },
  {
    "path": "test/fixtures/child_no_http.js",
    "content": "var pmx  = require('@pm2/io').init({\n  http: false\n});\n\nvar http = require('http');\n\nhttp.createServer(function(req, res) {\n  res.writeHead(200);\n  res.end('hey');\n}).listen(8000);\n\nprocess.on('message', function(msg) {\n  if (msg == 'shutdown') {\n    console.log('Closing all connections...');\n    setTimeout(function() {\n      console.log('Finished closing connections');\n      process.exit(0);\n    }, 100);\n  }\n});\n"
  },
  {
    "path": "test/fixtures/cluster/sigint_catcher.js",
    "content": "\n\nsetInterval(function() {\n  // Do nothing to keep process alive\n}, 1000);\n\nprocess.on('SIGINT', function () {\n  console.log('SIGINT cb called');\n});\n"
  },
  {
    "path": "test/fixtures/cluster-alias.json",
    "content": "{\n    \"apps\": [{\n        \"name\" : \"serv-clust\",\n        \"script\" : \"server.js\",\n        \"instances\" : \"4\",\n        \"exec_mode\" : \"cluster\",\n        \"env_test\" : {\n            \"NODE_ENV\" : \"TEST\"\n        }\n    }]\n}\n  "
  },
  {
    "path": "test/fixtures/cluster-pm2.json",
    "content": "{\n  \"name\" : \"serv-clust\",\n  \"script\" : \"server.js\",\n  \"max\" : \"10\",\n  \"instances\" : \"4\",\n  \"env_test\" : {\n    \"NODE_ENV\" : \"TEST\"\n  }\n}\n"
  },
  {
    "path": "test/fixtures/conf.json",
    "content": "// TG!\n[{\n  // Application 1\n  name      : \"echo\",\n  script    : 'toto.js',\n  env : {\n    \"TOTO\" : process.env.NODE_ENV || 'vazytg'\n  }\n}]\n"
  },
  {
    "path": "test/fixtures/configuration.js",
    "content": "[{\n  name      : \"echo\",\n  script    : [\".\", \"/\", \"e\", \"cho.js\"].join('')\n},{\n  name      : \"child\",\n  script    : \"./child.js\",\n  instances : \"4\",\n  error_file : \"./child-err.log\",\n  out_file : \"./child-out.log\"\n},{\n  name      : \"api-2\",\n  script    : \"./server.js\"\n}]\n"
  },
  {
    "path": "test/fixtures/configuration.json",
    "content": "// TG!\n[{\n  // Application 1\n  name      : \"echo\",\n  script    : [\".\", \"/\", \"e\", \"cho.js\"].join('')\n},{\n  // APplication 2\n  name      : \"child\",\n  script    : \"./child.js\",\n  instances : \"4\",\n  error_file : \"./child-err.log\",\n  out_file : \"./child-out.log\"\n},{\n  name      : \"api-2\",\n  script    : \"./server.js\"\n}]\n"
  },
  {
    "path": "test/fixtures/containerizer/Dockerfile.dev",
    "content": "FROM mhart/alpine-node:latest\n\nRUN apk update && apk add git && rm -rf /var/cache/apk/*\nRUN npm install pm2@next -g\nRUN mkdir -p /var/app\n\nWORKDIR /var/app\n\nCOPY ./package.json /var/app\nRUN npm install\n\n## DEVELOPMENT MODE\nENV NODE_ENV=development\nCMD [\"pm2-docker\", \"start\", \"index.js\"]\n"
  },
  {
    "path": "test/fixtures/containerizer/Dockerfile.prod",
    "content": "FROM mhart/alpine-node:latest\n\nRUN apk update && apk add git && rm -rf /var/cache/apk/*\nRUN npm install pm2@next -g\nRUN mkdir -p /var/app\n\nWORKDIR /var/app\n\nCOPY ./package.json /var/app\nRUN npm install\n\n## PRODUCTION MODE\nENV NODE_ENV=production\nCOPY . /var/app/\nCMD [\"pm2-docker\", \"start\", \"index.js\"]\n"
  },
  {
    "path": "test/fixtures/cron-stop.js",
    "content": "setTimeout(() => {\n  process.exit(0)\n}, 1000)\n"
  },
  {
    "path": "test/fixtures/cron.js",
    "content": "\nsetInterval(function() {\n  console.log('ok');\n}, 500);\n"
  },
  {
    "path": "test/fixtures/cron_no_autorestart.js",
    "content": "console.log('ok')\n"
  },
  {
    "path": "test/fixtures/custom_actions/index.js",
    "content": "\nvar pmx = require('@pm2/io');\n\npmx.action('ping', function(reply) {\n  return reply({ 'pong' : 'hehe' })\n});\n\npmx.action('param', function(data, reply) {\n  return reply(data)\n});\n\nsetInterval(function() {\n}, 1000);\n"
  },
  {
    "path": "test/fixtures/delayed_exit.js",
    "content": "var exit = function() {\n  console.log(\"Delay exit in 2 secs\");\n  setTimeout(function(){\n    process.exit();\n  }, 2000);\n};\n\nprocess.on('SIGTERM', function() {\n  console.log('Got SIGTERM signal.');\n  exit();\n});\n\nprocess.on('SIGINT', function() {\n  console.log('Got SIGINT signal');\n  exit();\n});\n\nsetInterval(function keepMeAlive() {}, 1000);\n"
  },
  {
    "path": "test/fixtures/docker/expressor/Dockerfile.bak",
    "content": "FROM keymetrics/pm2:latest\n\nRUN mkdir -p /var/app\n\nWORKDIR /var/app\n\nCOPY ./package.json /var/app\nRUN npm install\n## DEVELOPMENT MODE\nENV NODE_ENV=development\nCMD [\"pm2-dev\", \"process.json\", \"--env\", \"development\"]"
  },
  {
    "path": "test/fixtures/docker/expressor/app.js",
    "content": "\nvar express = require('express');\nvar app = express();\n\napp.get('/', function (req, res) {\n  res.send('Hello Bro! yes');\n});\n\napp.listen(3000, function () {\n  console.log('Example app listening on port 3000!');\n});\n"
  },
  {
    "path": "test/fixtures/docker/expressor/package.json",
    "content": "{\n  \"name\": \"@keymetrics/expressor\",\n  \"version\": \"1.0.0\",\n  \"description\": \"\",\n  \"main\": \"index.js\",\n  \"scripts\": {\n    \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n  },\n  \"author\": \"\",\n  \"license\": \"ISC\",\n  \"dependencies\": {\n    \"express\": \"^4.14.0\",\n    \"passport\" : \"*\",\n    \"redis\" : \"*\"\n  }\n}\n"
  },
  {
    "path": "test/fixtures/docker/expressor/process.json",
    "content": "{\n  apps : [{\n    script : 'app.js',\n    name : 'Express',\n    instances : '1'\n  }, {\n    script : 'worker.js'\n  }]\n}\n"
  },
  {
    "path": "test/fixtures/docker/expressor/worker.js",
    "content": "\nsetInterval(function() {\n  console.log('Worker finished his job');\n}, 1000);\n"
  },
  {
    "path": "test/fixtures/echo-env.js",
    "content": "\nsetInterval(function() {\n  console.log(process.env.ECHO_MSG || 'ok');\n}, 100);\n"
  },
  {
    "path": "test/fixtures/echo-pm2.json",
    "content": "{\n  \"name\" : \"echo\",\n  \"script\" : \"echo.js\",\n  \"options\": [\"\"]\n}\n"
  },
  {
    "path": "test/fixtures/echo-post.json",
    "content": "{\n  \"name\" : \"echo-json\",\n  \"script\" : \"echo-env.js\",\n  \"log_date_format\"    : \"DD\",\n  \"out_file\" : \"echo-test.log\",\n  \"merge_logs\" : true,\n  \"env\" : {\n    \"ECHO_MSG\" : \"YAY\"\n  },\n  \"env_production\" : {\n    \"ECHO_MSG\" : \"WOW\"\n  },\n  \"env_testing\" : {\n    \"ECHO_MSG\" : \"TEST\"\n  }\n}\n"
  },
  {
    "path": "test/fixtures/echo-to-pm2.json",
    "content": "{\n  \"name\" : \"echo-to\",\n  \"script\" : \"echo-to.js\",\n  \"options\": [\"\"]\n}\n"
  },
  {
    "path": "test/fixtures/echo.coffee",
    "content": "#!/usr/bin/env coffee\n\nsetInterval (-> console.log 'ok'), 500"
  },
  {
    "path": "test/fixtures/echo.js",
    "content": "\nsetInterval(function() {\n  console.log('echo.js');\n}, 50);\n\nsetInterval(function() {\n  console.error('echo.js-error');\n}, 50);\n"
  },
  {
    "path": "test/fixtures/echo2.js",
    "content": "\nvar http = require('http');\n\nhttp.createServer(function(req, res) {\n  res.writeHead(200);\n  res.end('hey');\n}).listen(0);\n\nprocess.on('message', function(msg) {\n  if (msg == 'shutdown') {\n    console.log('Closing all connections...');\n    setTimeout(function() {\n      console.log('Finished closing connections');\n      process.exit(0);\n    }, 100);\n  }\n});\n"
  },
  {
    "path": "test/fixtures/echo3.js",
    "content": "\n\nvar http = require('http');\n\nhttp.createServer(function(req, res) {\n  res.writeHead(200);\n  res.end('hey');\n}).listen(0);\n\nprocess.on('message', function(msg) {\n  if (msg == 'shutdown') {\n    console.log('Closing all connections...');\n    setTimeout(function() {\n      console.log('Finished closing connections');\n      process.exit(0);\n    }, 100);\n  }\n});\n"
  },
  {
    "path": "test/fixtures/echoto-pm2.json",
    "content": "{\n  \"name\" : \"echoto\",\n  \"script\" : \"echoto.js\",\n  \"options\": [\"\"]\n}\n"
  },
  {
    "path": "test/fixtures/ecosystem/ecosystem.config.js",
    "content": "\nmodule.exports = {\n  script: 'unknown.js'\n}\n"
  },
  {
    "path": "test/fixtures/ecosystem.config.js",
    "content": "module.exports = {\n  apps : [{\n    script: 'index.js',\n    watch: '.'\n  }, {\n    script: './service-worker/',\n    watch: ['./service-worker']\n  }],\n\n  deploy : {\n    production : {\n      user : 'SSH_USERNAME',\n      host : 'SSH_HOSTMACHINE',\n      ref  : 'origin/master',\n      repo : 'GIT_REPOSITORY',\n      path : 'DESTINATION_PATH',\n      'pre-deploy-local': '',\n      'post-deploy' : 'npm install && pm2 reload ecosystem.config.js --env production',\n      'pre-setup': ''\n    }\n  }\n};\n"
  },
  {
    "path": "test/fixtures/ecosystem.json",
    "content": "{\n  /**\n   * Application configuration section\n   * http://pm2.keymetrics.io/docs/usage/application-declaration/\n   */\n  apps : [\n\n    // First application\n    {\n      name      : \"API\",\n      script    : \"app.js\",\n      env: {\n        COMMON_VARIABLE: \"true\"\n      },\n      env_production : {\n        NODE_ENV: \"production\"\n      }\n    },\n\n    // Second application\n    {\n      name      : \"WEB\",\n      script    : \"web.js\"\n    }\n  ],\n\n  /**\n   * Deployment section\n   * http://pm2.keymetrics.io/docs/usage/deployment/\n   */\n  deploy : {\n    production : {\n      user : \"node\",\n      host : \"212.83.163.1\",\n      ref  : \"origin/master\",\n      repo : \"git@github.com:repo.git\",\n      path : \"/var/www/production\",\n      \"post-deploy\" : \"npm install && pm2 startOrRestart ecosystem.json --env production\"\n    },\n    dev : {\n      user : \"node\",\n      host : \"212.83.163.1\",\n      ref  : \"origin/master\",\n      repo : \"git@github.com:repo.git\",\n      path : \"/var/www/development\",\n      \"post-deploy\" : \"npm install && pm2 startOrRestart ecosystem.json --env dev\",\n      env  : {\n        NODE_ENV: \"dev\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "test/fixtures/ecosystem.json5",
    "content": "{\n  /**\n   * This is a sample configuration file for PM2\n   */\n\n  /**\n   * Here we declare the apps that must be managed by PM2\n   * All options are listed here:\n   * https://github.com/Unitech/PM2/blob/master/ADVANCED_README.md#json-app-declaration\n   *\n   */\n  apps : [\n\n    // First application\n    {\n      name      : \"API\",\n      script    : \"app.js\",\n      env: {\n        COMMON_VARIABLE: \"true\"\n      },\n      env_production : {\n        NODE_ENV: \"production\"\n      }\n    },\n\n    // Second application\n    {\n      name      : \"WEB\",\n      script    : \"web.js\"\n    }\n\n  ],\n\n\n  /**\n   * PM2 help you to deploy apps over your servers\n   * For more help go to :\n   * https://github.com/Unitech/PM2/blob/master/ADVANCED_README.md#deployment-pm2--090\n   */\n  deploy : {\n    production : {\n      user : \"node\",\n      host : \"212.83.163.1\",\n      ref  : \"origin/master\",\n      repo : \"git@github.com:repo.git\",\n      path : \"/var/www/production\",\n      \"post-deploy\" : \"pm2 startOrRestart ecosystem.json5 --env production\"\n    },\n    dev : {\n      user : \"node\",\n      host : \"212.83.163.1\",\n      ref  : \"origin/master\",\n      repo : \"git@github.com:repo.git\",\n      path : \"/var/www/development\",\n      \"post-deploy\" : \"pm2 startOrRestart ecosystem.json5 --env dev\",\n      env  : {\n        NODE_ENV: \"dev\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "test/fixtures/empty.js",
    "content": "setInterval(function () {}, 1000);\n"
  },
  {
    "path": "test/fixtures/env-ecosystem.json",
    "content": "{\n  \"apps\" : [{\n    \"name\"      : \"env2\",\n    \"script\"    : \"./env.js\",\n    \"out_file\"  : \"out-env.log\",\n    \"merge_logs\" : true,\n    \"env\": {\n      \"NODE_ENV\": \"production\"\n    }\n  }],\n  \"deploy\" : {\n    \"production\" : {\n      \"user\" : \"node\",\n      \"host\" : \"212.83.163.1\",\n      \"ref\"  : \"origin/master\",\n      \"repo\" : \"git@github.com:repo.git\",\n      \"path\" : \"/var/www/production\",\n      \"env\"  : {\n        \"TEST_VARIABLE\": \"No worries!\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "test/fixtures/env-refreshed.json",
    "content": "[{\n  \"name\"      : \"appname\",\n  \"script\"    : \"./env.js\",\n  \"out_file\"  : \"out-env.log\",\n  \"merge_logs\" : true,\n  \"env\": {\n    \"NODE_ENV\": \"production\",\n    \"TEST_VARIABLE\": { \"HEYYYY\": true }\n  }\n}]\n"
  },
  {
    "path": "test/fixtures/env-switching/app.json",
    "content": "{\n  script : './child.js',\n  // Default environment\n  env : {\n    NODE_ENV : 'normal'\n  },\n  // Prod\n  env_production : {\n    NODE_ENV : 'production'\n  },\n  // Test\n  env_test : {\n    NODE_ENV : 'test'\n  }\n}\n"
  },
  {
    "path": "test/fixtures/env-switching/child.js",
    "content": "\nsetInterval(function() {\n  console.log(process.env.NODE_ENV);\n}, 1000);\n"
  },
  {
    "path": "test/fixtures/env.js",
    "content": "setInterval(function() {\n  console.log(process.env.TEST_VARIABLE);\n}, 100);\n"
  },
  {
    "path": "test/fixtures/env.json",
    "content": "[{\n  \"name\"      : \"appname\",\n  \"script\"    : \"./env.js\",\n  \"out_file\"  : \"out-env.log\",\n  \"merge_logs\" : true,\n  \"env\": {\n    \"NODE_ENV\": \"production\",\n    \"TEST_VARIABLE\": \"YES\"\n  }\n}]\n"
  },
  {
    "path": "test/fixtures/esmodules/mjs/circle.mjs",
    "content": "const PI = 3.14159265359;\n\nexport function area(radius) {\n  return (radius ** 2) * PI;\n}\n\nexport function circumference(radius) {\n  return 2 * radius * PI;\n}\n"
  },
  {
    "path": "test/fixtures/esmodules/mjs/index.mjs",
    "content": "import { area, circumference } from './circle.mjs';\n\nconst r = 3;\n\nconsole.log(`Circle with radius ${r} has\n  area: ${area(r)};\n  circunference: ${circumference(r)}`);\n\nsetInterval(() => {\n}, 1000)\n"
  },
  {
    "path": "test/fixtures/esmodules/packagemodule/circle.js",
    "content": "const PI = 3.14159265359;\n\nexport function area(radius) {\n  return (radius ** 2) * PI;\n}\n\nexport function circumference(radius) {\n  return 2 * radius * PI;\n}\n"
  },
  {
    "path": "test/fixtures/esmodules/packagemodule/index.js",
    "content": "import { area, circumference } from './circle.js';\n\nconst r = 3;\n\nconsole.log(`Circle with radius ${r} has\n  area: ${area(r)};\n  circunference: ${circumference(r)}`);\n\nsetInterval(() => {\n}, 1000)\n"
  },
  {
    "path": "test/fixtures/esmodules/packagemodule/package.json",
    "content": "{\n  \"name\": \"import\",\n  \"version\": \"1.0.0\",\n  \"description\": \"\",\n  \"main\": \"index.js\",\n  \"type\":\"module\",\n  \"scripts\": {\n    \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n  },\n  \"author\": \"\",\n  \"license\": \"ISC\"\n}\n"
  },
  {
    "path": "test/fixtures/events/custom_action.js",
    "content": "\nvar axm = require('@pm2/io');\n\naxm.action('refresh:db', function(reply) {\n  console.log('Refreshing');\n  reply({success : true, subobj : { a : 'b'}});\n});\n"
  },
  {
    "path": "test/fixtures/events/custom_action_with_params.js",
    "content": "\nvar axm = require('@pm2/io');\n\naxm.action('refresh:db', { comment : 'Refresh the database' }, function(reply) {\n  console.log('Refreshing');\n  reply({success : true});\n});\n\naxm.action('chanme:ladb', { comment : 'Refresh la BIG database' }, function(reply) {\n  console.log('Refreshing BIG DB');\n  reply({success : true});\n});\n\naxm.action('rm:rf', { comment : 'Delete moi ca plus vite que ca !' }, function(reply) {\n  console.log('RMING RFING');\n  reply({success : true});\n});\n\naxm.action('rm:roff', function(reply) {\n  console.log('RMING RFING');\n  reply({success : true});\n});\n"
  },
  {
    "path": "test/fixtures/events/own_event.js",
    "content": "\nvar axm = require('@pm2/io');\n\nsetInterval(function() {\n  axm.emit('user:register', {\n    user : 'toto@gmail.com',\n    mail : 'hey@gmail.com'\n  });\n}, 200);\n"
  },
  {
    "path": "test/fixtures/exitcode42.js",
    "content": "setInterval(function() {\n    console.log('BYE');\n    process.exit(42);\n  }, 500);\n"
  },
  {
    "path": "test/fixtures/extra-lang/app-python.config.js",
    "content": "module.exports = {\n  apps : [{\n    name: 'echo-python-1',\n    cmd: 'echo.py',\n    max_memory_restart: '1G',\n    env: {\n      NODE_ENV: 'development'\n    },\n    env_production : {\n      NODE_ENV: 'production'\n    }\n  },{\n    name: 'echo-python-max',\n    cmd: 'echo.py',\n    instances: 4,\n    env: {\n      NODE_ENV: 'development'\n    },\n    env_production : {\n      NODE_ENV: 'production'\n    }\n  }]\n};\n"
  },
  {
    "path": "test/fixtures/extra-lang/apps.json",
    "content": "{\n  apps: [{\n    \"interpreter\"      : \"/usr/bin/python3\",\n    \"name\"             : \"echo-python\",\n    \"script\"           : \"echo.py\",\n    \"log\"              : \"python-app.log\",\n    \"combine_logs\"     : true,\n    \"env\" : {\n      DEFAULT_STR : \"Python\"\n    },\n    \"env_production\" : {\n      DEFAULT_STR : \"PythonProduction\"\n    }\n  }, {\n    \"exec_interpreter\" : \"/usr/bin/php\",\n    \"name\"             : \"echo-php\",\n    \"script\"           : \"echo.php\",\n    \"output\"           : \"php-app-out.log\",\n    \"error\"            : \"php-error.log\",\n    \"combine_logs\"     : true\n  }]\n}\n"
  },
  {
    "path": "test/fixtures/extra-lang/echo.php",
    "content": "<?php\nwhile(1)\n    {\n        print \"PHP\\n\";\n        error_log(\"ERROR\");\n        sleep(1);\n    }\n?>"
  },
  {
    "path": "test/fixtures/extra-lang/echo.py",
    "content": "#!/usr/bin/python\nimport os\nimport time\n\nwhile 1:\n    try:\n        print(os.environ['DEFAULT_STR'])\n    except KeyError:\n        print('RAWPython')\n    time.sleep(1)\n"
  },
  {
    "path": "test/fixtures/git/COMMIT_EDITMSG",
    "content": "first commit\n"
  },
  {
    "path": "test/fixtures/git/HEAD",
    "content": "ref: refs/heads/master\n"
  },
  {
    "path": "test/fixtures/git/config",
    "content": "[core]\n\trepositoryformatversion = 0\n\tfilemode = true\n\tbare = false\n\tlogallrefupdates = true\n[remote \"origin\"]\n\turl = https://github.com/jshkurti/pm2_travis.git\n\tfetch = +refs/heads/*:refs/remotes/origin/*\n[branch \"master\"]\n\tremote = origin\n\tmerge = refs/heads/master\n"
  },
  {
    "path": "test/fixtures/git/description",
    "content": "Unnamed repository; edit this file 'description' to name the repository.\n"
  },
  {
    "path": "test/fixtures/git/hooks/applypatch-msg.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to check the commit log message taken by\n# applypatch from an e-mail message.\n#\n# The hook should exit with non-zero status after issuing an\n# appropriate message if it wants to stop the commit.  The hook is\n# allowed to edit the commit message file.\n#\n# To enable this hook, rename this file to \"applypatch-msg\".\n\n. git-sh-setup\ntest -x \"$GIT_DIR/hooks/commit-msg\" &&\n\texec \"$GIT_DIR/hooks/commit-msg\" ${1+\"$@\"}\n:\n"
  },
  {
    "path": "test/fixtures/git/hooks/commit-msg.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to check the commit log message.\n# Called by \"git commit\" with one argument, the name of the file\n# that has the commit message.  The hook should exit with non-zero\n# status after issuing an appropriate message if it wants to stop the\n# commit.  The hook is allowed to edit the commit message file.\n#\n# To enable this hook, rename this file to \"commit-msg\".\n\n# Uncomment the below to add a Signed-off-by line to the message.\n# Doing this in a hook is a bad idea in general, but the prepare-commit-msg\n# hook is more suited to it.\n#\n# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\\(.*>\\).*$/Signed-off-by: \\1/p')\n# grep -qs \"^$SOB\" \"$1\" || echo \"$SOB\" >> \"$1\"\n\n# This example catches duplicate Signed-off-by lines.\n\ntest \"\" = \"$(grep '^Signed-off-by: ' \"$1\" |\n\t sort | uniq -c | sed -e '/^[ \t]*1[ \t]/d')\" || {\n\techo >&2 Duplicate Signed-off-by lines.\n\texit 1\n}\n"
  },
  {
    "path": "test/fixtures/git/hooks/post-update.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to prepare a packed repository for use over\n# dumb transports.\n#\n# To enable this hook, rename this file to \"post-update\".\n\nexec git update-server-info\n"
  },
  {
    "path": "test/fixtures/git/hooks/pre-applypatch.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to verify what is about to be committed\n# by applypatch from an e-mail message.\n#\n# The hook should exit with non-zero status after issuing an\n# appropriate message if it wants to stop the commit.\n#\n# To enable this hook, rename this file to \"pre-applypatch\".\n\n. git-sh-setup\ntest -x \"$GIT_DIR/hooks/pre-commit\" &&\n\texec \"$GIT_DIR/hooks/pre-commit\" ${1+\"$@\"}\n:\n"
  },
  {
    "path": "test/fixtures/git/hooks/pre-commit.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to verify what is about to be committed.\n# Called by \"git commit\" with no arguments.  The hook should\n# exit with non-zero status after issuing an appropriate message if\n# it wants to stop the commit.\n#\n# To enable this hook, rename this file to \"pre-commit\".\n\nif git rev-parse --verify HEAD >/dev/null 2>&1\nthen\n\tagainst=HEAD\nelse\n\t# Initial commit: diff against an empty tree object\n\tagainst=4b825dc642cb6eb9a060e54bf8d69288fbee4904\nfi\n\n# If you want to allow non-ASCII filenames set this variable to true.\nallownonascii=$(git config --bool hooks.allownonascii)\n\n# Redirect output to stderr.\nexec 1>&2\n\n# Cross platform projects tend to avoid non-ASCII filenames; prevent\n# them from being added to the repository. We exploit the fact that the\n# printable range starts at the space character and ends with tilde.\nif [ \"$allownonascii\" != \"true\" ] &&\n\t# Note that the use of brackets around a tr range is ok here, (it's\n\t# even required, for portability to Solaris 10's /usr/bin/tr), since\n\t# the square bracket bytes happen to fall in the designated range.\n\ttest $(git diff --cached --name-only --diff-filter=A -z $against |\n\t  LC_ALL=C tr -d '[ -~]\\0' | wc -c) != 0\nthen\n\tcat <<\\EOF\nError: Attempt to add a non-ASCII file name.\n\nThis can cause problems if you want to work with people on other platforms.\n\nTo be portable it is advisable to rename the file.\n\nIf you know what you are doing you can disable this check using:\n\n  git config hooks.allownonascii true\nEOF\n\texit 1\nfi\n\n# If there are whitespace errors, print the offending file names and fail.\nexec git diff-index --check --cached $against --\n"
  },
  {
    "path": "test/fixtures/git/hooks/pre-push.sample",
    "content": "#!/bin/sh\n\n# An example hook script to verify what is about to be pushed.  Called by \"git\n# push\" after it has checked the remote status, but before anything has been\n# pushed.  If this script exits with a non-zero status nothing will be pushed.\n#\n# This hook is called with the following parameters:\n#\n# $1 -- Name of the remote to which the push is being done\n# $2 -- URL to which the push is being done\n#\n# If pushing without using a named remote those arguments will be equal.\n#\n# Information about the commits which are being pushed is supplied as lines to\n# the standard input in the form:\n#\n#   <local ref> <local sha1> <remote ref> <remote sha1>\n#\n# This sample shows how to prevent push of commits where the log message starts\n# with \"WIP\" (work in progress).\n\nremote=\"$1\"\nurl=\"$2\"\n\nz40=0000000000000000000000000000000000000000\n\nIFS=' '\nwhile read local_ref local_sha remote_ref remote_sha\ndo\n\tif [ \"$local_sha\" = $z40 ]\n\tthen\n\t\t# Handle delete\n\t\t:\n\telse\n\t\tif [ \"$remote_sha\" = $z40 ]\n\t\tthen\n\t\t\t# New branch, examine all commits\n\t\t\trange=\"$local_sha\"\n\t\telse\n\t\t\t# Update to existing branch, examine new commits\n\t\t\trange=\"$remote_sha..$local_sha\"\n\t\tfi\n\n\t\t# Check for WIP commit\n\t\tcommit=`git rev-list -n 1 --grep '^WIP' \"$range\"`\n\t\tif [ -n \"$commit\" ]\n\t\tthen\n\t\t\techo \"Found WIP commit in $local_ref, not pushing\"\n\t\t\texit 1\n\t\tfi\n\tfi\ndone\n\nexit 0\n"
  },
  {
    "path": "test/fixtures/git/hooks/pre-rebase.sample",
    "content": "#!/bin/sh\n#\n# Copyright (c) 2006, 2008 Junio C Hamano\n#\n# The \"pre-rebase\" hook is run just before \"git rebase\" starts doing\n# its job, and can prevent the command from running by exiting with\n# non-zero status.\n#\n# The hook is called with the following parameters:\n#\n# $1 -- the upstream the series was forked from.\n# $2 -- the branch being rebased (or empty when rebasing the current branch).\n#\n# This sample shows how to prevent topic branches that are already\n# merged to 'next' branch from getting rebased, because allowing it\n# would result in rebasing already published history.\n\npublish=next\nbasebranch=\"$1\"\nif test \"$#\" = 2\nthen\n\ttopic=\"refs/heads/$2\"\nelse\n\ttopic=`git symbolic-ref HEAD` ||\n\texit 0 ;# we do not interrupt rebasing detached HEAD\nfi\n\ncase \"$topic\" in\nrefs/heads/??/*)\n\t;;\n*)\n\texit 0 ;# we do not interrupt others.\n\t;;\nesac\n\n# Now we are dealing with a topic branch being rebased\n# on top of master.  Is it OK to rebase it?\n\n# Does the topic really exist?\ngit show-ref -q \"$topic\" || {\n\techo >&2 \"No such branch $topic\"\n\texit 1\n}\n\n# Is topic fully merged to master?\nnot_in_master=`git rev-list --pretty=oneline ^master \"$topic\"`\nif test -z \"$not_in_master\"\nthen\n\techo >&2 \"$topic is fully merged to master; better remove it.\"\n\texit 1 ;# we could allow it, but there is no point.\nfi\n\n# Is topic ever merged to next?  If so you should not be rebasing it.\nonly_next_1=`git rev-list ^master \"^$topic\" ${publish} | sort`\nonly_next_2=`git rev-list ^master           ${publish} | sort`\nif test \"$only_next_1\" = \"$only_next_2\"\nthen\n\tnot_in_topic=`git rev-list \"^$topic\" master`\n\tif test -z \"$not_in_topic\"\n\tthen\n\t\techo >&2 \"$topic is already up-to-date with master\"\n\t\texit 1 ;# we could allow it, but there is no point.\n\telse\n\t\texit 0\n\tfi\nelse\n\tnot_in_next=`git rev-list --pretty=oneline ^${publish} \"$topic\"`\n\t/usr/bin/perl -e '\n\t\tmy $topic = $ARGV[0];\n\t\tmy $msg = \"* $topic has commits already merged to public branch:\\n\";\n\t\tmy (%not_in_next) = map {\n\t\t\t/^([0-9a-f]+) /;\n\t\t\t($1 => 1);\n\t\t} split(/\\n/, $ARGV[1]);\n\t\tfor my $elem (map {\n\t\t\t\t/^([0-9a-f]+) (.*)$/;\n\t\t\t\t[$1 => $2];\n\t\t\t} split(/\\n/, $ARGV[2])) {\n\t\t\tif (!exists $not_in_next{$elem->[0]}) {\n\t\t\t\tif ($msg) {\n\t\t\t\t\tprint STDERR $msg;\n\t\t\t\t\tundef $msg;\n\t\t\t\t}\n\t\t\t\tprint STDERR \" $elem->[1]\\n\";\n\t\t\t}\n\t\t}\n\t' \"$topic\" \"$not_in_next\" \"$not_in_master\"\n\texit 1\nfi\n\n<<\\DOC_END\n\nThis sample hook safeguards topic branches that have been\npublished from being rewound.\n\nThe workflow assumed here is:\n\n * Once a topic branch forks from \"master\", \"master\" is never\n   merged into it again (either directly or indirectly).\n\n * Once a topic branch is fully cooked and merged into \"master\",\n   it is deleted.  If you need to build on top of it to correct\n   earlier mistakes, a new topic branch is created by forking at\n   the tip of the \"master\".  This is not strictly necessary, but\n   it makes it easier to keep your history simple.\n\n * Whenever you need to test or publish your changes to topic\n   branches, merge them into \"next\" branch.\n\nThe script, being an example, hardcodes the publish branch name\nto be \"next\", but it is trivial to make it configurable via\n$GIT_DIR/config mechanism.\n\nWith this workflow, you would want to know:\n\n(1) ... if a topic branch has ever been merged to \"next\".  Young\n    topic branches can have stupid mistakes you would rather\n    clean up before publishing, and things that have not been\n    merged into other branches can be easily rebased without\n    affecting other people.  But once it is published, you would\n    not want to rewind it.\n\n(2) ... if a topic branch has been fully merged to \"master\".\n    Then you can delete it.  More importantly, you should not\n    build on top of it -- other people may already want to\n    change things related to the topic as patches against your\n    \"master\", so if you need further changes, it is better to\n    fork the topic (perhaps with the same name) afresh from the\n    tip of \"master\".\n\nLet's look at this example:\n\n\t\t   o---o---o---o---o---o---o---o---o---o \"next\"\n\t\t  /       /           /           /\n\t\t /   a---a---b A     /           /\n\t\t/   /               /           /\n\t       /   /   c---c---c---c B         /\n\t      /   /   /             \\         /\n\t     /   /   /   b---b C     \\       /\n\t    /   /   /   /             \\     /\n    ---o---o---o---o---o---o---o---o---o---o---o \"master\"\n\n\nA, B and C are topic branches.\n\n * A has one fix since it was merged up to \"next\".\n\n * B has finished.  It has been fully merged up to \"master\" and \"next\",\n   and is ready to be deleted.\n\n * C has not merged to \"next\" at all.\n\nWe would want to allow C to be rebased, refuse A, and encourage\nB to be deleted.\n\nTo compute (1):\n\n\tgit rev-list ^master ^topic next\n\tgit rev-list ^master        next\n\n\tif these match, topic has not merged in next at all.\n\nTo compute (2):\n\n\tgit rev-list master..topic\n\n\tif this is empty, it is fully merged to \"master\".\n\nDOC_END\n"
  },
  {
    "path": "test/fixtures/git/hooks/prepare-commit-msg.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to prepare the commit log message.\n# Called by \"git commit\" with the name of the file that has the\n# commit message, followed by the description of the commit\n# message's source.  The hook's purpose is to edit the commit\n# message file.  If the hook fails with a non-zero status,\n# the commit is aborted.\n#\n# To enable this hook, rename this file to \"prepare-commit-msg\".\n\n# This hook includes three examples.  The first comments out the\n# \"Conflicts:\" part of a merge commit.\n#\n# The second includes the output of \"git diff --name-status -r\"\n# into the message, just before the \"git status\" output.  It is\n# commented because it doesn't cope with --amend or with squashed\n# commits.\n#\n# The third example adds a Signed-off-by line to the message, that can\n# still be edited.  This is rarely a good idea.\n\ncase \"$2,$3\" in\n  merge,)\n    /usr/bin/perl -i.bak -ne 's/^/# /, s/^# #/#/ if /^Conflicts/ .. /#/; print' \"$1\" ;;\n\n# ,|template,)\n#   /usr/bin/perl -i.bak -pe '\n#      print \"\\n\" . `git diff --cached --name-status -r`\n#\t if /^#/ && $first++ == 0' \"$1\" ;;\n\n  *) ;;\nesac\n\n# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\\(.*>\\).*$/Signed-off-by: \\1/p')\n# grep -qs \"^$SOB\" \"$1\" || echo \"$SOB\" >> \"$1\"\n"
  },
  {
    "path": "test/fixtures/git/hooks/update.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to blocks unannotated tags from entering.\n# Called by \"git receive-pack\" with arguments: refname sha1-old sha1-new\n#\n# To enable this hook, rename this file to \"update\".\n#\n# Config\n# ------\n# hooks.allowunannotated\n#   This boolean sets whether unannotated tags will be allowed into the\n#   repository.  By default they won't be.\n# hooks.allowdeletetag\n#   This boolean sets whether deleting tags will be allowed in the\n#   repository.  By default they won't be.\n# hooks.allowmodifytag\n#   This boolean sets whether a tag may be modified after creation. By default\n#   it won't be.\n# hooks.allowdeletebranch\n#   This boolean sets whether deleting branches will be allowed in the\n#   repository.  By default they won't be.\n# hooks.denycreatebranch\n#   This boolean sets whether remotely creating branches will be denied\n#   in the repository.  By default this is allowed.\n#\n\n# --- Command line\nrefname=\"$1\"\noldrev=\"$2\"\nnewrev=\"$3\"\n\n# --- Safety check\nif [ -z \"$GIT_DIR\" ]; then\n\techo \"Don't run this script from the command line.\" >&2\n\techo \" (if you want, you could supply GIT_DIR then run\" >&2\n\techo \"  $0 <ref> <oldrev> <newrev>)\" >&2\n\texit 1\nfi\n\nif [ -z \"$refname\" -o -z \"$oldrev\" -o -z \"$newrev\" ]; then\n\techo \"usage: $0 <ref> <oldrev> <newrev>\" >&2\n\texit 1\nfi\n\n# --- Config\nallowunannotated=$(git config --bool hooks.allowunannotated)\nallowdeletebranch=$(git config --bool hooks.allowdeletebranch)\ndenycreatebranch=$(git config --bool hooks.denycreatebranch)\nallowdeletetag=$(git config --bool hooks.allowdeletetag)\nallowmodifytag=$(git config --bool hooks.allowmodifytag)\n\n# check for no description\nprojectdesc=$(sed -e '1q' \"$GIT_DIR/description\")\ncase \"$projectdesc\" in\n\"Unnamed repository\"* | \"\")\n\techo \"*** Project description file hasn't been set\" >&2\n\texit 1\n\t;;\nesac\n\n# --- Check types\n# if $newrev is 0000...0000, it's a commit to delete a ref.\nzero=\"0000000000000000000000000000000000000000\"\nif [ \"$newrev\" = \"$zero\" ]; then\n\tnewrev_type=delete\nelse\n\tnewrev_type=$(git cat-file -t $newrev)\nfi\n\ncase \"$refname\",\"$newrev_type\" in\n\trefs/tags/*,commit)\n\t\t# un-annotated tag\n\t\tshort_refname=${refname##refs/tags/}\n\t\tif [ \"$allowunannotated\" != \"true\" ]; then\n\t\t\techo \"*** The un-annotated tag, $short_refname, is not allowed in this repository\" >&2\n\t\t\techo \"*** Use 'git tag [ -a | -s ]' for tags you want to propagate.\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\trefs/tags/*,delete)\n\t\t# delete tag\n\t\tif [ \"$allowdeletetag\" != \"true\" ]; then\n\t\t\techo \"*** Deleting a tag is not allowed in this repository\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\trefs/tags/*,tag)\n\t\t# annotated tag\n\t\tif [ \"$allowmodifytag\" != \"true\" ] && git rev-parse $refname > /dev/null 2>&1\n\t\tthen\n\t\t\techo \"*** Tag '$refname' already exists.\" >&2\n\t\t\techo \"*** Modifying a tag is not allowed in this repository.\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\trefs/heads/*,commit)\n\t\t# branch\n\t\tif [ \"$oldrev\" = \"$zero\" -a \"$denycreatebranch\" = \"true\" ]; then\n\t\t\techo \"*** Creating a branch is not allowed in this repository\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\trefs/heads/*,delete)\n\t\t# delete branch\n\t\tif [ \"$allowdeletebranch\" != \"true\" ]; then\n\t\t\techo \"*** Deleting a branch is not allowed in this repository\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\trefs/remotes/*,commit)\n\t\t# tracking branch\n\t\t;;\n\trefs/remotes/*,delete)\n\t\t# delete tracking branch\n\t\tif [ \"$allowdeletebranch\" != \"true\" ]; then\n\t\t\techo \"*** Deleting a tracking branch is not allowed in this repository\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\t*)\n\t\t# Anything else (is there anything else?)\n\t\techo \"*** Update hook: unknown type of update to ref $refname of type $newrev_type\" >&2\n\t\texit 1\n\t\t;;\nesac\n\n# --- Finished\nexit 0\n"
  },
  {
    "path": "test/fixtures/git/info/exclude",
    "content": "# git ls-files --others --exclude-from=.git/info/exclude\n# Lines that start with '#' are comments.\n# For a project mostly in C, the following would be a good set of\n# exclude patterns (uncomment them if you want to use them):\n# *.[oa]\n# *~\n"
  },
  {
    "path": "test/fixtures/git/logs/HEAD",
    "content": "0000000000000000000000000000000000000000 7ae6d7c64d39da2167c33993e1f4f0233e8eb6f0 jshkurti <jshkurti@student.42.fr> 1429209718 +0200\tcommit (initial): first commit\n"
  },
  {
    "path": "test/fixtures/git/logs/refs/heads/master",
    "content": "0000000000000000000000000000000000000000 7ae6d7c64d39da2167c33993e1f4f0233e8eb6f0 jshkurti <jshkurti@student.42.fr> 1429209718 +0200\tcommit (initial): first commit\n"
  },
  {
    "path": "test/fixtures/git/logs/refs/remotes/origin/master",
    "content": "0000000000000000000000000000000000000000 7ae6d7c64d39da2167c33993e1f4f0233e8eb6f0 jshkurti <jshkurti@student.42.fr> 1429209819 +0200\tupdate by push\n"
  },
  {
    "path": "test/fixtures/git/objects/7a/e6d7c64d39da2167c33993e1f4f0233e8eb6f0",
    "content": "x\u0001K\n \u0010@\u0014/\u0004\u0019?PBbt$\u0001\u001d_h\u0005{^>\t\u0018\u001f/ҙJBڜȔ\u0014*\":*\tdc*T\n{[U\u001d\u001ec.\rn?\u000f_]j_VGo\u0002\\ZR/Yև2;/"
  },
  {
    "path": "test/fixtures/git/refs/heads/master",
    "content": "7ae6d7c64d39da2167c33993e1f4f0233e8eb6f0\n"
  },
  {
    "path": "test/fixtures/git/refs/remotes/origin/master",
    "content": "7ae6d7c64d39da2167c33993e1f4f0233e8eb6f0\n"
  },
  {
    "path": "test/fixtures/graceful-exit-no-listen.js",
    "content": "\n/*\n * Example of graceful exit that does not listen\n *\n * $ pm2 reload all\n */\n\nprocess.on('message', function(msg) {\n  if (msg == 'shutdown') {\n    console.log('Closing all connections...');\n    setTimeout(function() {\n      console.log('Finished closing connections');\n      process.exit(0);\n    }, 1500);\n  }\n});\n\nsetInterval(function ()\n{\n  console.log('tick');\n}, 4000);\n"
  },
  {
    "path": "test/fixtures/graceful-exit-send.js",
    "content": "\n/*\n * Example of graceful exit that does not listen but sends 'online'\n *\n * $ pm2 reload all\n */\n\nprocess.on('message', function(msg) {\n  if (msg == 'shutdown') {\n    console.log('Closing all connections...');\n    setTimeout(function() {\n      console.log('Finished closing connections');\n      process.exit(0);\n    }, 1500);\n  }\n});\n\nsetInterval(function ()\n{\n  console.log('tick');\n}, 4000);\n\nsetTimeout(function ()\n{\n  process.send('online');\n}, 2000);\n"
  },
  {
    "path": "test/fixtures/graceful-exit.js",
    "content": "\n/*\n * Example of graceful exit\n *\n * $ pm2 reload all\n */\n\nprocess.on('message', function(msg) {\n  if (msg == 'shutdown') {\n    console.log('Closing all connections...');\n    setTimeout(function() {\n      console.log('Finished closing connections');\n      process.exit(0);\n    }, 1500);\n  }\n});\n\nvar http = require('http');\n\nhttp.createServer(function(req, res) {\n  res.writeHead(200);\n  res.end('hey');\n}).listen(8000, function() {\n  console.log('listening');\n});\n"
  },
  {
    "path": "test/fixtures/homogen-json-action/all.json",
    "content": "{\n  \"apps\" : [{\n    \"script\" : \"http.js\",\n    \"name\" : \"http\",\n    \"instances\" : 4\n  }, {\n    \"script\" : \"http.js\",\n    \"name\" : \"http2\",\n    \"instances\" : 2\n  }]\n}\n"
  },
  {
    "path": "test/fixtures/homogen-json-action/http.js",
    "content": "var pmx = require('@pm2/io').init({\n  http : true\n});\n\nvar http = require('http');\n\nvar server = http.createServer(function(req, res) {\n  res.writeHead(200);\n  res.end('hey');\n}).listen(8000, function() {\n  console.log('App listening on port 8000');\n});\n"
  },
  {
    "path": "test/fixtures/http.js",
    "content": "\nvar http = require('http');\n\nvar app = http.createServer(function(req, res) {\n  res.writeHead(200);\n  res.end('hey');\n})\n\nvar listener = app.listen(0, function() {\n  console.log('Listening on port ' + listener.address().port);\n});\n\nprocess.on('message', function(msg) {\n  if (msg == 'shutdown') {\n    console.log('Closing all connections...');\n    setTimeout(function() {\n      console.log('Finished closing connections');\n      process.exit(0);\n    }, 100);\n  }\n});\n"
  },
  {
    "path": "test/fixtures/increment-var/ecosystem.json",
    "content": "[\n  {\n    \"name\" : \"sample-normal\",\n    \"instances\": \"2\",\n    \"exec_mode\": \"cluster\",\n    \"script\" : \"sample.js\",\n    \"out_file\": \"NULL\",\n    \"error_file\": \"NULL\"\n  },\n  {\n    \"name\" : \"sample-other-instance\",\n    \"instances\": \"2\",\n    \"instance_var\": \"APP_ID\",\n    \"exec_mode\": \"cluster\",\n    \"script\" : \"sample.js\",\n    \"out_file\": \"NULL\",\n    \"error_file\": \"NULL\"\n  },\n  {\n    \"name\" : \"sample-increment\",\n    \"instances\": \"2\",\n    \"increment_var\": \"PORT\",\n    \"exec_mode\": \"cluster\",\n    \"script\" : \"sample.js\",\n    \"out_file\": \"NULL\",\n    \"error_file\": \"NULL\",\n    \"env\": {\n      \"PORT\": 3000\n    }\n  },\n  {\n    \"name\" : \"sample-normal-fork\",\n    \"instances\": \"2\",\n    \"exec_mode\": \"fork\",\n    \"script\" : \"sample.js\",\n    \"out_file\": \"NULL\",\n    \"error_file\": \"NULL\"\n  },\n  {\n    \"name\" : \"sample-other-instance-fork\",\n    \"instances\": \"2\",\n    \"instance_var\": \"APP_ID\",\n    \"exec_mode\": \"fork\",\n    \"script\" : \"sample.js\",\n    \"out_file\": \"NULL\",\n    \"error_file\": \"NULL\"\n  },\n  {\n    \"name\" : \"sample-increment-fork\",\n    \"instances\": \"2\",\n    \"increment_var\": \"PORT\",\n    \"exec_mode\": \"fork\",\n    \"script\" : \"sample.js\",\n    \"out_file\": \"NULL\",\n    \"error_file\": \"NULL\",\n    \"env\": {\n      \"PORT\": 3000\n    }\n  }\n]"
  },
  {
    "path": "test/fixtures/increment-var/sample.js",
    "content": "\nsetInterval(function () {}, 1000);\n"
  },
  {
    "path": "test/fixtures/inside/echo.js",
    "content": "\nsetInterval(function() {\n  console.log('echo.js');\n}, 500);\n\nsetInterval(function() {\n  console.error('echo.js-error');\n}, 500);\n"
  },
  {
    "path": "test/fixtures/inside/inner_restart.sh",
    "content": "#!/bin/bash\n\n$PM2_PATH restart echo\n$PM2_PATH restart echo\n$PM2_PATH restart echo\n"
  },
  {
    "path": "test/fixtures/inside/reload_inside.js",
    "content": "\n\n\nvar PM2 = require('../../..');\n\nvar pm2 = new PM2.custom({\n  cwd : __dirname\n});\n\nPM2.reload('echo', function(err, app) {\n  if (err) throw err;\n});\n"
  },
  {
    "path": "test/fixtures/inside/restart_inside.js",
    "content": "\n\nvar PM2 = require('../../..');\n\nvar pm2 = new PM2.custom({\n  cwd : __dirname\n});\n\nPM2.restart('echo', function(err, app) {\n  if (err) throw err;\n});\n"
  },
  {
    "path": "test/fixtures/inside/start_inside.js",
    "content": "\nvar PM2 = require('../../..');\n\nvar pm2 = new PM2.custom({\n  cwd : __dirname\n});\n\nPM2.start('./echo.js', function(err, app) {\n  if (err) throw err;\n});\n"
  },
  {
    "path": "test/fixtures/insidePm2Process.js",
    "content": "setInterval(function(){\n\tconsole.log(process.env.TEST_VARIABLE);\n}, 500);"
  },
  {
    "path": "test/fixtures/interface/child.js",
    "content": "\n\n\nvar http = require('http');\n\nhttp.createServer(function(req, res) {\n  res.writeHead(200);\n  res.end('hey');\n}).listen(8000);\n\nprocess.on('message', function(msg) {\n  if (msg == 'shutdown') {\n    console.log('Closing all connections...');\n    setTimeout(function() {\n      console.log('Finished closing connections');\n      process.exit(0);\n    }, 100);\n  }\n});\n"
  },
  {
    "path": "test/fixtures/interface/http_transaction.js",
    "content": "\nvar axm = require('@pm2/io');\n\naxm.init({\n  http: true\n})\n\nvar http = require('http');\n\nhttp.createServer(function(req, res) {\n  res.writeHead(200);\n  setTimeout(function() {\n    res.end('transaction');\n  }, 1000);\n}).listen(9010);\n\nsetInterval(function() {\n  request(['/user', '/bla', '/user/lol/delete', '/POST/POST'][Math.floor((Math.random() * 4))]);\n}, 100);\n\nfunction makeid() {\n  var text = \"\";\n  var possible = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\";\n\n  for( var i=0; i < 5; i++ )\n    text += possible.charAt(Math.floor(Math.random() * possible.length));\n\n  return text;\n}\n\nfunction request(path) {\n  var options = {\n    hostname: '127.0.0.1'\n    ,port: 9010\n    ,path: path || '/users'\n    ,method: 'GET'\n    ,headers: { 'Content-Type': 'application/json' }\n  };\n\n  var req = http.request(options, function(res) {\n    res.setEncoding('utf8');\n    res.on('data', function (data) {\n      console.log(data); // I can't parse it because, it's a string. why?\n    });\n  });\n  req.on('error', function(e) {\n    console.log('problem with request: ' + e.message);\n  });\n  req.end();\n}\n"
  },
  {
    "path": "test/fixtures/interface/human_event.js",
    "content": "\nvar axm = require('@pm2/io');\n\nsetInterval(function() {\n  axm.emit('content:page:created', {\n    msg : 'A CMS page has been created',\n    user : 'Francois Debiole'\n  });\n}, 200);\n"
  },
  {
    "path": "test/fixtures/interface/log_out.js",
    "content": "\n\nconsole.log('outmsg');\n\nconsole.error('errmsg');\n\nsetInterval(function() {}, 100);\n"
  },
  {
    "path": "test/fixtures/interface/process_exception.js",
    "content": "\nvar axm = require('@pm2/io');\n\n//axm.catchAll();\n\nsetTimeout(function() {\n  throw new Error('Exit');\n}, 200);\n"
  },
  {
    "path": "test/fixtures/interface/process_exception_with_logs.js",
    "content": "\nvar pmx = require('@pm2/io');\n\npmx.action('exception', function(reply) {\n  console.log('Im going to crash');\n  console.log('I will crash muhahah');\n  throw new Error('CRASHED');\n\n  return reply({ sucess: true});\n});\n\nsetInterval(function() {\n}, 1000);\n"
  },
  {
    "path": "test/fixtures/interface/promise_rejection.js",
    "content": "\nsetTimeout(() => {\n  var p = new Promise(function(resolve, reject) {\n    //setTimeout(function() {\n    //throw new Error('fail')\n    abc = asdsad;\n\n    return resolve('ok')\n    //}, 200)\n  })\n\n  p.then(function(e) {\n  })\n}, 100)\n"
  },
  {
    "path": "test/fixtures/interpreter/echo.coffee",
    "content": "#!/usr/bin/env coffee\n\nsetInterval (-> console.log 'ok'), 500"
  },
  {
    "path": "test/fixtures/interpreter/echo.ls",
    "content": "#!/usr/bin/env lsc\n\nsetInterval (-> console.log 'Hello Livescript!'), 500\n"
  },
  {
    "path": "test/fixtures/interpreter/echo.ts",
    "content": "\nclass Greeter {\n  constructor(public greeting: string) { }\n  greet() {\n    return this.greeting;\n  }\n};\n\nvar greeter = new Greeter(\"Hello Typescript!\");\n\nconsole.log(greeter.greet());\n"
  },
  {
    "path": "test/fixtures/interpreter/echo.tsx",
    "content": "\nclass Greeter {\n  constructor(public greeting: string) { }\n  greet() {\n    return this.greeting;\n  }\n};\n\nvar greeter = new Greeter(\"Hello Typescript!\");\n\nconsole.log(greeter.greet());\n"
  },
  {
    "path": "test/fixtures/js-configuration/app.js",
    "content": "\nsetInterval(function() {\n  console.log('ok');\n}, 1000);\n"
  },
  {
    "path": "test/fixtures/js-configuration/ecosystem.config.js",
    "content": "module.exports = {\n  /**\n   * Application configuration section\n   * http://pm2.keymetrics.io/docs/usage/application-declaration/\n   */\n  apps : [\n\n    // First application\n    {\n      name      : \"API\",\n      script    : \"app.js\",\n      env: {\n        COMMON_VARIABLE: \"true\"\n      },\n      env_production : {\n        NODE_ENV: \"production\"\n      }\n    }\n  ],\n\n  /**\n   * Deployment section\n   * http://pm2.keymetrics.io/docs/usage/deployment/\n   */\n  deploy : {\n    production : {\n      user : \"node\",\n      host : \"212.83.163.1\",\n      ref  : \"origin/master\",\n      repo : \"git@github.com:repo.git\",\n      path : \"/var/www/production\",\n      \"post-deploy\" : \"npm install && pm2 startOrRestart ecosystem.config.js --env production\"\n    },\n    dev : {\n      user : \"node\",\n      host : \"212.83.163.1\",\n      ref  : \"origin/master\",\n      repo : \"git@github.com:repo.git\",\n      path : \"/var/www/development\",\n      \"post-deploy\" : \"npm install && pm2 startOrRestart ecosystem.config.js --env dev\",\n      env  : {\n        NODE_ENV: \"dev\"\n      }\n    }\n  }\n};\n"
  },
  {
    "path": "test/fixtures/json-reload/big-array.js",
    "content": "\nvar obj = {};\nvar i = 0;\n\nsetInterval(function() {\n  obj[i] = Array.apply(null, new Array(9999)).map(String.prototype.valueOf,\"hi\");\n  i++;\n}, 40);\n"
  },
  {
    "path": "test/fixtures/json-reload/echo-env.js",
    "content": "\nsetInterval(function() {\n  console.log(process.env.ECHO_MSG || 'ok');\n}, 100);\n"
  },
  {
    "path": "test/fixtures/json-reload/echo-post.json",
    "content": "{\n  \"name\" : \"echo-json\",\n  \"script\" : \"echo-env.js\",\n  \"log_date_format\"    : \"DD\",\n  \"out_file\" : \"echo-test.log\",\n  \"merge_logs\" : true,\n  \"env\" : {\n    \"ECHO_MSG\" : \"YAY\"\n  },\n  \"env_production\" : {\n    \"ECHO_MSG\" : \"WOW\"\n  }\n}\n"
  },
  {
    "path": "test/fixtures/json-reload/echo-pre.json",
    "content": "{\n  \"name\" : \"echo-json\",\n  \"script\" : \"echo-env.js\",\n  \"log_date_format\"    : \"YYYY-MM-DD HH:mm Z\",\n  \"out_file\" : \"echo-test.log\",\n  \"merge_logs\" : true\n}\n"
  },
  {
    "path": "test/fixtures/json-reload/max-mem-0.json",
    "content": "{\n  \"name\" : \"max_mem\",\n  \"script\" : \"big-array.js\",\n  \"max_memory_restart\" : \"300M\"\n}\n"
  },
  {
    "path": "test/fixtures/json-reload/max-mem.json",
    "content": "{\n  \"name\" : \"max_mem\",\n  \"script\" : \"big-array.js\",\n  \"max_memory_restart\" : \"19M\"\n}\n"
  },
  {
    "path": "test/fixtures/killnotsofast.js",
    "content": "setInterval(function() {\n  console.log('ALIVE');\n}, 500);\n"
  },
  {
    "path": "test/fixtures/killtoofast.js",
    "content": "\nsetInterval(function() {\n  console.log('BOUM');\n  process.exit(1);\n}, 30);\n"
  },
  {
    "path": "test/fixtures/listen-timeout/wait-ready.js",
    "content": "\nvar http = require('http');\n\nvar server = http.createServer(function(req, res) {\n  res.writeHead(200);\n  res.end('hey');\n}).listen(process.env.PORT || 8000, function() {\n  console.log('App listening on port %d in env %s', process.env.PORT || 8000, process.env.NODE_ENV);\n\n  // 1# Notify application ready\n  setTimeout(function() {\n    process.send('ready');\n  }, 1000);\n\n});\n\n// // 2# Handle on Exit\nprocess.on('SIGINT', function() {\n  console.log('Cleanup on exit');\n\n  server.on('close', function() {\n    console.log('Connections closed');\n    process.exit(0);\n  });\n\n  server.close();\n});\n"
  },
  {
    "path": "test/fixtures/local_require.js",
    "content": "var paths = require('module').globalPaths;\n\nif (Array.isArray(paths)) {\n  var found = false;\n  paths.forEach(function(elem) {\n    if (elem === process.env.NODE_PATH) {\n      found = true;\n    }\n  });\n\n  if (!found)\n    process.exit(1);\n  else\n    setInterval(function keepAlive() {}, 10000);\n}\nelse {\n  process.exit(1);\n}\n"
  },
  {
    "path": "test/fixtures/log-create-not-exist-dir/echo.js",
    "content": "console.log(\"start\");\nsetInterval(function () {\n  console.log(\"tick\");\n}, 50);\n"
  },
  {
    "path": "test/fixtures/log-json/ecosystem.json",
    "content": "[{\n  \"name\" : \"one-echo\",\n  \"script\" : \"one-echo.js\",\n  \"log_type\": \"json\",\n  \"log_file\": \"output.log\",\n  \"merge_logs\": true,\n  \"out_file\": \"NULL\",\n  \"error_file\": \"NULL\",\n  \"autorestart\": false\n},\n{\n  \"name\" : \"one-echo-cluster\",\n  \"script\" : \"one-echo.js\",\n  \"exec_mode\": \"cluster_mode\",\n  \"log_type\": \"json\",\n  \"log_file\": \"output.log\",\n  \"merge_logs\": true,\n  \"out_file\": \"NULL\",\n  \"error_file\": \"NULL\",\n  \"autorestart\": false\n},\n{\n  \"name\" : \"one-echo-date\",\n  \"script\" : \"one-echo.js\",\n  \"log_type\": \"json\",\n  \"log_file\": \"output.log\",\n  \"log_date_format\": \"YYYY\",\n  \"merge_logs\": true,\n  \"out_file\": \"NULL\",\n  \"error_file\": \"NULL\",\n  \"autorestart\": false\n},\n{\n  \"name\" : \"one-echo-cluster-date\",\n  \"script\" : \"one-echo.js\",\n  \"exec_mode\": \"cluster_mode\",\n  \"log_type\": \"json\",\n  \"log_file\": \"output.log\",\n  \"log_date_format\": \"YYYY\",\n  \"merge_logs\": true,\n  \"out_file\": \"NULL\",\n  \"error_file\": \"NULL\",\n  \"autorestart\": false\n}]"
  },
  {
    "path": "test/fixtures/log-json/one-echo.js",
    "content": "console.log('echo')"
  },
  {
    "path": "test/fixtures/log-namespace/echo.js",
    "content": "console.log(\"start\");\nsetInterval(function () {\n  console.log(\"tick\");\n}, 50);\n"
  },
  {
    "path": "test/fixtures/mjs/ecosystem.config.js",
    "content": "module.exports = {\n\n  apps : [\n\n\n    {\n      name      : 'Test es6 modules',\n      script    : 'index.mjs',\n      node_args : '--experimental-modules'\n    }\n  ],\n\n\n};\n"
  },
  {
    "path": "test/fixtures/mjs/index.mjs",
    "content": "import {test} from './test'\n\ntest();\n"
  },
  {
    "path": "test/fixtures/mjs/package.json",
    "content": "{\n  \"name\": \"test-es6-pm2\",\n  \"version\": \"1.0.0\",\n  \"description\": \"A simple project to test pm2 es6-modules pm2 support\",\n  \"main\": \"index.mjs\",\n  \"scripts\": {\n    \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\",\n    \"start\": \"node --experimental-modules index.mjs\"\n  },\n  \"keywords\": [\n    \"es6-modules\",\n    \"import\",\n    \"export\",\n    \"pm2\"\n  ],\n  \"author\": \"vpotseluyko\",\n  \"license\": \"ISC\"\n}\n"
  },
  {
    "path": "test/fixtures/mjs/test.mjs",
    "content": "export function test() {\n  setInterval(() => console.log(`Hello es6 from pm2`), 1000);\n}\n"
  },
  {
    "path": "test/fixtures/module-fixture/package.json",
    "content": "{\n  \"name\": \"example-module\",\n  \"version\": \"0.3.21\",\n  \"description\": \"Keymetrics++ and PM2 adapter\",\n  \"main\": \"scoped-action.js\",\n  \"dependencies\": {\n  },\n  \"scripts\": {\n    \"test\": \"DEBUG='axm:*' mocha test/*.mocha.js\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/keymetrics/pmx.git\"\n  },\n  \"config\" : {\n    \"aconfig-var\" : true,\n    \"var2\" : false\n  },\n  \"author\": \"Keymetrics I/O\",\n  \"license\": \"MIT\"\n}\n"
  },
  {
    "path": "test/fixtures/module-fixture/scoped-action.js",
    "content": "\nvar pmx = require('@pm2/io');\n\n\nvar conf = pmx.initModule({\n\n  widget : {\n    type             : 'generic',\n    logo             : 'https://app.keymetrics.io/img/logo/keymetrics-300.png',\n\n    // 0 = main element\n    // 1 = secondary\n    // 2 = main border\n    // 3 = secondary border\n    theme            : ['#141A1F', '#222222', '#3ff', '#3ff'],\n\n    el : {\n      probes  : true,\n      actions : true\n    },\n\n    block : {\n      actions : true,\n      issues  : true,\n      meta    : true\n    }\n\n    // Status\n    // Green / Yellow / Red\n  }\n});\n\n\npmx.scopedAction('testo', function(data, emitter) {\n  var i = setInterval(function() {\n    emitter.send('datard');\n  }, 100);\n\n  setTimeout(function() {\n\n    emitter.end('end');\n    clearInterval(i);\n  }, 3000);\n});\n\nvar spawn = require('child_process').spawn;\n\npmx.scopedAction('long running lsof', function(data, res) {\n  var child = spawn('lsof', []);\n\n  child.stdout.on('data', function(chunk) {\n    chunk.toString().split('\\n').forEach(function(line) {\n      res.send(line);\n    });\n  });\n\n  child.stdout.on('end', function(chunk) {\n    res.end('end');\n  });\n\n});\n\n\npmx.action('simple action', function(reply) {\n  return reply({success:true});\n});\n\npmx.action('simple with arg', function(opts,reply) {\n  return reply(opts);\n});\n"
  },
  {
    "path": "test/fixtures/multi-echo.json",
    "content": "[{\n  \"name\" : \"echo2\",\n  \"script\" : \"echo.js\",\n  \"max\" : \"1\"\n}, {\n  \"name\" : \"echo3\",\n  \"script\" : \"echo.js\",\n  \"max\" : \"1\"\n}]\n"
  },
  {
    "path": "test/fixtures/network.js",
    "content": "\nvar http = require('http');\nvar i = 0;\n\nhttp.createServer(function(req, res) {\n  res.writeHead(200);\n  res.end(\"hello world\\n\" + i++);\n}).listen(8004);\n"
  },
  {
    "path": "test/fixtures/no-restart.json",
    "content": "[\n  {\n    \"name\"        : \"no_restart_app\",\n    \"script\"      : \"./killtoofast.js\",\n    \"autorestart\" : false\n  }\n]\n"
  },
  {
    "path": "test/fixtures/no-vizion.json",
    "content": "[\n  {\n    \"name\"        : \"no_vizion\",\n    \"script\"      : \"./echo.js\",\n    \"vizion\" : false\n  }\n]\n"
  },
  {
    "path": "test/fixtures/no_cwd_change.json",
    "content": "[{\n  \"name\"      : \"iminpath1\",\n  \"script\"    : \"iminpath1.js\",\n  \"out_file\" : \"./iminpath1.log\"\n},{\n  \"name\" : \"iminpath2\",\n  \"script\"    : \"iminpath2.js\",\n  \"out_file\" : \"./iminpath2.log\"\n}]\n"
  },
  {
    "path": "test/fixtures/nvm-node-version/ecosystem-change.json",
    "content": "{\n  /**\n   * Application configuration section\n   * http://pm2.keymetrics.io/docs/usage/application-declaration/\n   */\n  apps : [\n\n    // First application\n    {\n      name      : \"http-4.6\",\n      script    : \"http.js\",\n      interpreter  : \"node@4.5.0\"\n    },\n    {\n      name      : \"http-6.7\",\n      script    : \"http.js\",\n      force     : true,\n      env : {\n        PORT : 8002\n      },\n      interpreter  : \"node@6.7.0\"\n    },\n  ]\n}\n"
  },
  {
    "path": "test/fixtures/nvm-node-version/ecosystem.json",
    "content": "{\n  /**\n   * Application configuration section\n   * http://pm2.keymetrics.io/docs/usage/application-declaration/\n   */\n  apps : [\n\n    // First application\n    {\n      name      : \"http-4.6\",\n      script    : \"http.js\",\n      interpreter  : \"node@4.6.0\"\n    },\n    {\n      name      : \"http-6.7\",\n      script    : \"http.js\",\n      force     : true,\n      env : {\n        PORT : 8002\n      },\n      interpreter  : \"node@6.7.0\"\n    },\n  ]\n}\n"
  },
  {
    "path": "test/fixtures/nvm-node-version/http.js",
    "content": "\nvar http = require('http');\n\nvar server = http.createServer(function(req, res) {\n  res.writeHead(200);\n  res.end('hey');\n}).listen(process.env.PORT || 8000, function() {\n  console.log('App listening on port %d in env %s', process.env.PORT || 8000, process.env.NODE_ENV);\n});\n"
  },
  {
    "path": "test/fixtures/path-check.js",
    "content": "console.log(__filename);\nconsole.log(module.filename);\nconsole.log(module.parent);\nconsole.log(module.children);\nconsole.log(__dirname);\nconsole.log(module);\nconsole.log(process.env.PWD);\nconsole.log(require.main.filename);\nconsole.log(require.main === module);\n"
  },
  {
    "path": "test/fixtures/path-resolution/echo.js",
    "content": "\nsetInterval(function() {\n  console.log('echo.js');\n}, 50);\n\nsetInterval(function() {\n  console.error('echo.js-error');\n}, 50);\n"
  },
  {
    "path": "test/fixtures/path-resolution/ecosystem.config.js",
    "content": "module.exports = {\n  /**\n   * Application configuration section\n   * http://pm2.keymetrics.io/docs/usage/application-declaration/\n   */\n  apps : [\n    {\n      name      : \"test\",\n      script    : \"./echo.js\",\n      out_file  : '~/echo-out.log',\n      error_file : '~/echo-err.log',\n      pid_file : '~/echo-pid.log'\n    }\n  ]\n}\n"
  },
  {
    "path": "test/fixtures/path-resolution/ecosystem2.config.js",
    "content": "module.exports = {\n  /**\n   * Application configuration section\n   * http://pm2.keymetrics.io/docs/usage/application-declaration/\n   */\n  apps : [\n    {\n      name      : \"test\",\n      script    : \"./echo.js\",\n      out_file  : 'echo-out.log',\n      error_file : 'echo-err.log',\n      pid_file : 'echo-pid.log'\n    }\n  ]\n}\n"
  },
  {
    "path": "test/fixtures/path-resolution/ecosystem3.config.js",
    "content": "module.exports = {\n  /**\n   * Application configuration section\n   * http://pm2.keymetrics.io/docs/usage/application-declaration/\n   */\n  apps : [\n    {\n      name      : \"test-cluster\",\n      script    : \"./echo.js\",\n      out_file  : 'echo-out.log',\n      error_file : 'echo-err.log',\n      instances: 4\n    }\n  ]\n}\n"
  },
  {
    "path": "test/fixtures/path1/iminpath1.js",
    "content": "\nconsole.log(process.cwd());\n"
  },
  {
    "path": "test/fixtures/path1/path2/iminpath2.js",
    "content": "\nconsole.log(process.cwd());\n"
  },
  {
    "path": "test/fixtures/pm2-dev/app.js",
    "content": "var http = require('http');\n\nhttp.createServer(function(req, res) {\n  res.writeHead(200);\n  res.end('hey');\n}).listen(0);\n\nprocess.on('message', function(msg) {\n  if (msg == 'shutdown') {\n    console.log('Closing all connections...');\n    setTimeout(function() {\n      console.log('Finished closing connections');\n      process.exit(0);\n    }, 100);\n  }\n});\n"
  },
  {
    "path": "test/fixtures/pm2-dev/app.json",
    "content": "{\n  \"script\": \"app.js\",\n  \"watch\": [\"server\", \"client\"],\n  \"ignore_watch\" : [\"node_modules\", \"client/img\"]\n}\n"
  },
  {
    "path": "test/fixtures/pm2-dev/exited_app.js",
    "content": "\nthrow new Error('exit')\n"
  },
  {
    "path": "test/fixtures/pm2-ecosystem.json",
    "content": "{\n  \"pm2\" : [{\n    \"name\"      : \"echo\",\n    \"script\"    : \"./echo.js\"\n  },{\n    \"name\"      : \"child\",\n    \"script\"    : \"./child.js\",\n    \"instances\" : \"4\",\n    \"error_file\" : \"./child-err.log\",\n    \"out_file\" : \"./child-out.log\"\n  },{\n    \"name\"      : \"api-2\",\n    \"script\"    : \"./server.js\"\n  }]\n}\n"
  },
  {
    "path": "test/fixtures/probes.js",
    "content": "\n\nvar pmx = require('@pm2/io');\nvar conf = pmx.init();\n\nvar http = require('http');\n\nhttp.createServer(function(req, res) {\n  res.writeHead(200);\n  res.end('hey');\n}).listen(8000);\n\n\nvar value_to_inspect = 0;\n\n/**\n * .metric, .counter, .meter, .histogram are also available (cf doc)\n */\nvar val = pmx.metric({\n  name : 'test-probe',\n  value : function() {\n    return value_to_inspect;\n  },\n  /**\n   * Here we set a default value threshold, to receive a notification\n   * These options can be overriden via Keymetrics or via pm2\n   * More: http://bit.ly/1O02aap\n   */\n  alert : {\n    mode     : 'threshold',\n    value    : 20,\n    msg      : 'test-probe alert!',\n    action   : function(val) {\n      // Besides the automatic alert sent via Keymetrics\n      // You can also configure your own logic to do something\n      console.log('Value has reached %d', val);\n    }\n  }\n});\n\nsetInterval(function() {\n  // Then we can see that this value increase over the time in Keymetrics\n  value_to_inspect++;\n}, 30);\n\nprocess.on('message', function(msg) {\n  if (msg == 'shutdown') {\n    console.log('Closing all connections...');\n    setTimeout(function() {\n      console.log('Finished closing connections');\n      process.exit(0);\n    }, 500);\n  }\n});\n"
  },
  {
    "path": "test/fixtures/process.json",
    "content": "\n"
  },
  {
    "path": "test/fixtures/promise/empty-rejection.js",
    "content": "\nsetTimeout(function() {\n  Promise.reject();\n}, 1000);\n\nsetInterval(function() {\n}, 1000);\n"
  },
  {
    "path": "test/fixtures/promise/rejection.js",
    "content": "\nsetTimeout(function() {\n  Promise.reject(new Error('Errorla'));\n}, 1000);\n\nsetInterval(function() {\n}, 1000);\n"
  },
  {
    "path": "test/fixtures/push.json",
    "content": "[{\n  \"name\" : \"push\",\n  \"script\" : \"echo.js\",\n  \"max\" : \"1\"\n}]\n"
  },
  {
    "path": "test/fixtures/python-script.py",
    "content": "import time\n\nif __name__ == \"__main__\":\n    while 1:\n        print('Script.py: alive')\n        time.sleep(1)\n"
  },
  {
    "path": "test/fixtures/quit.js",
    "content": "\nconsole.log('HEY!');\n\nsetTimeout(function() {\n  process.exit(1);\n}, 3000);\n"
  },
  {
    "path": "test/fixtures/send-data-process/return-data.js",
    "content": "\nprocess.on('message', function(packet) {\n  if (packet.topic == 'process:msg') {\n    process.send({\n      topic : 'process:msg',\n      data : {\n        success : true\n      }\n    });\n  }\n});\n"
  },
  {
    "path": "test/fixtures/serve/404.html",
    "content": "your file doesnt exist believe me"
  },
  {
    "path": "test/fixtures/serve/ecosystem-serve.json",
    "content": "{\n  \"static\": [\n    {\n      \"path\": \".\",\n      \"spa\": true,\n      \"basic_auth\": {\n        \"username\": \"user\",\n        \"password\": \"pass\"\n      },\n      \"port\": 8081\n    }\n  ]\n}"
  },
  {
    "path": "test/fixtures/serve/ecosystem.json",
    "content": "[{\n  \"name\"      : \"frontend\",\n  \"script\"    : \"serve\",\n  \"env\": {\n    \"PM2_SERVE_PORT\": 8081,\n    \"PM2_SERVE_PATH\": \".\"\n  }\n}]\n"
  },
  {
    "path": "test/fixtures/serve/index.html",
    "content": "good shit right there"
  },
  {
    "path": "test/fixtures/serve/other.html",
    "content": "iam another file\nwith 2 lines\n"
  },
  {
    "path": "test/fixtures/server.js",
    "content": "var http = require('http');\n\nhttp.createServer(function(req, res) {\n  res.writeHead(200);\n  res.end(\"hello world\\n\");\n}).listen(8020);\n"
  },
  {
    "path": "test/fixtures/signal-send.js",
    "content": "\nsetInterval(function() {\n}, 1000);\n\nprocess.on('message', function (msg) {\n  console.log(msg);\n});\n"
  },
  {
    "path": "test/fixtures/signal.js",
    "content": "\n\nsetInterval(function() {\n  console.log('ok');\n}, 1000);\n\nprocess.on('SIGUSR2', function () {\n  console.log('SIGUSR2');\n});\n"
  },
  {
    "path": "test/fixtures/signals/delayed_send.js",
    "content": "\nvar http = require('http');\n\nsetInterval(function() {\n  // Do nothing to keep process alive\n}, 1000);\n\nhttp.createServer(function(req, res) {\n  res.writeHead(200);\n  res.end('hey');\n}).listen(0);\n\nprocess.on('message', function (msg) {\n  if (msg === 'shutdown') {\n    console.log('shutdown message received but forbid exit');\n  }\n});\n"
  },
  {
    "path": "test/fixtures/signals/delayed_sigint.js",
    "content": "\nvar http = require('http');\n\nsetInterval(function() {\n  // Do nothing to keep process alive\n}, 1000);\n\nhttp.createServer(function(req, res) {\n  res.writeHead(200);\n  res.end('hey');\n}).listen(0);\n\nprocess.on('SIGINT', function () {\n  console.log('SIGINT cb called but forbid exit');\n});\n"
  },
  {
    "path": "test/fixtures/sort/http.js",
    "content": "\nvar http = require('http');\n\nvar app = http.createServer(function(req, res) {\n  res.writeHead(200);\n  res.end('hey');\n})\n\nvar listener = app.listen(0, function() {\n  console.log('Listening on port ' + listener.address().port);\n});\n\nprocess.on('message', function(msg) {\n  if (msg == 'shutdown') {\n    console.log('Closing all connections...');\n    setTimeout(function() {\n      console.log('Finished closing connections');\n      process.exit(0);\n    }, 100);\n  }\n});\n"
  },
  {
    "path": "test/fixtures/sort/other.js",
    "content": "\nvar http = require('http');\n\nvar app = http.createServer(function(req, res) {\n  res.writeHead(200);\n  res.end('hey');\n})\n\nvar listener = app.listen(0, function() {\n  console.log('Listening on port ' + listener.address().port);\n});\n\nprocess.on('message', function(msg) {\n  if (msg == 'shutdown') {\n    console.log('Closing all connections...');\n    setTimeout(function() {\n      console.log('Finished closing connections');\n      process.exit(0);\n    }, 100);\n  }\n});\n"
  },
  {
    "path": "test/fixtures/source-map/main.js",
    "content": "\"use strict\";\nvar models_1 = require('./models');\nconsole.log(models_1.UserMessage);\nsetTimeout(function () {\n    throw new Error('errr');\n}, 1000);\n//# sourceMappingURL=main.js.map\n"
  },
  {
    "path": "test/fixtures/source-map/models.js",
    "content": "'use strict';\nvar UserMessage = (function () {\n    function UserMessage(payload) {\n        var data = JSON.parse(payload);\n        if (!data.name || !data.message) {\n            throw new Error('Invalid message payload received: ' + payload);\n        }\n        this.data = data;\n    }\n    Object.defineProperty(UserMessage.prototype, \"name\", {\n        get: function () {\n            return this.data.name;\n        },\n        enumerable: true,\n        configurable: true\n    });\n    Object.defineProperty(UserMessage.prototype, \"message\", {\n        get: function () {\n            return this.data.message;\n        },\n        enumerable: true,\n        configurable: true\n    });\n    return UserMessage;\n}());\nexports.UserMessage = UserMessage;\n//# sourceMappingURL=models.js.map"
  },
  {
    "path": "test/fixtures/start-app/ecosystem-bun.config.js",
    "content": "module.exports = {\n  apps : [{\n    cmd: \"bun -e 'setTimeout(function() { }, 100000); console.log(process.env.TEST)'\",\n    log: 'test-conf.log',\n    merge_logs: true,\n    env: {\n      TEST: 'test_val'\n    }\n  }]\n};\n"
  },
  {
    "path": "test/fixtures/start-app/ecosystem.config.js",
    "content": "module.exports = {\n  apps : [{\n    cmd: \"node -e 'setTimeout(function() { }, 100000); console.log(process.env.TEST)'\",\n    log: 'test-conf.log',\n    merge_logs: true,\n    env: {\n      TEST: 'test_val'\n    }\n  }]\n};\n"
  },
  {
    "path": "test/fixtures/start-app/http.js",
    "content": "\nvar http = require('http');\n\nvar app = http.createServer(function(req, res) {\n  res.writeHead(200);\n  res.end('hey');\n})\n\nvar listener = app.listen(0, function() {\n  console.log('Listening on port ' + listener.address().port);\n});\n\nprocess.on('message', function(msg) {\n  if (msg == 'shutdown') {\n    console.log('Closing all connections...');\n    setTimeout(function() {\n      console.log('Finished closing connections');\n      process.exit(0);\n    }, 100);\n  }\n});\n"
  },
  {
    "path": "test/fixtures/start-consistency/child.js",
    "content": "\nrequire('@pm2/io').init({\n  http : true\n});\n\nvar http = require('http');\n\nvar server = http.createServer(function(req, res) {\n  res.writeHead(200);\n  res.end('hey');\n}).listen(8000);\n"
  },
  {
    "path": "test/fixtures/start-consistency/child.json",
    "content": "{\n  script:\"./child.js\",\n  name  :\"child\"\n}\n"
  },
  {
    "path": "test/fixtures/startProcessInsidePm2.js",
    "content": "var PM2 = require('../..');\n\n/**\n * New gen API\n */\nvar pm2 = new PM2.custom();\n\n//console.log(process.env);\n\npm2.connect(function(err) {\n\n  console.error(' ----------------------' );\n\n\tpm2.start('./insidePm2Process.js', {\n    name: 'insideProcess',\n    'output': './inside-out.log',\n    merge_logs: true\n  }, function(err, proc){\n\t\tif(err){\n\t\t\tconsole.log(err);\n\t\t\treturn process.exit(1);\n\t\t}\n\t});\n\n\n});\n"
  },
  {
    "path": "test/fixtures/startProcessInsidePm2.json",
    "content": "[{\n\t\"name\"      : \"masterProcess\",\n\t\"script\"    : \"./startProcessInsidePm2.js\",\n\t\"exec_mode\" : \"fork_mode\",\n\t\"min_uptime\" : \"100\",\n\t\"max_restarts\" : \"50\"\n}]"
  },
  {
    "path": "test/fixtures/stdin/stdin.js",
    "content": "\nconst readline = require('readline');\n\nconst rl = readline.createInterface({\n  input: process.stdin,\n  output: process.stdout\n});\n\nrl.prompt();\n\nrl.on('line', function(line) {\n  console.log('Line %s received', line);\n});\n\nsetInterval(function() {\n}, 100);\n"
  },
  {
    "path": "test/fixtures/stdout-stderr.js",
    "content": "\nprocess.stdout.write('outwrite', 'utf8', function() {\n  console.log('outcb');\n});\n\nprocess.stderr.write('errwrite', 'utf8', function() {\n  console.log('errcb');\n});\n\nsetInterval(function() {\n  process.stdout.write('outwrite', 'utf8', function() {\n    console.log('outcb');\n  });\n\n  process.stderr.write('errwrite', 'utf8', function() {\n    console.log('errcb');\n  });\n}, 1000)\n"
  },
  {
    "path": "test/fixtures/stop-exit-codes.json",
    "content": "[\n    {\n      \"name\"        : \"stop_exit_codes_app\",\n      \"script\"      : \"./exitcode42.js\",\n      \"stop_exit_codes\" : [12, 42]\n    }\n  ]\n"
  },
  {
    "path": "test/fixtures/throw-later.js",
    "content": "var timer = setInterval(function(){\n  console.log('tick', Date.now());\n}, 100);\n\nsetTimeout(function(){\n  clearInterval(timer);\n  throw new Error('error has been caught')\n}, 350);\n"
  },
  {
    "path": "test/fixtures/throw-later.json",
    "content": "{\n  \"apps\" : [{\n    \"name\"        : \"throw-later\",\n    \"script\"      : \"throw-later.js\",\n    \"error_file\"  : \"err.log\",\n    \"out_file\"    : \"out.log\",\n    \"log_file\"    : \"entire.log\",\n    \"merge_logs\"  : true\n  }]\n}\n"
  },
  {
    "path": "test/fixtures/throw-later1.json",
    "content": "{\n  \"apps\" : [{\n    \"name\"        : \"throw-later\",\n    \"script\"      : \"throw-later.js\",\n    \"error_file\"  : \"err.log\",\n    \"out_file\"    : \"out.log\",\n    \"merge_logs\"  : true\n  }]\n}\n"
  },
  {
    "path": "test/fixtures/throw-string.js",
    "content": "\nfunction crash() {\n  throw new Error('crashed');\n}\n\ncrash();\n"
  },
  {
    "path": "test/fixtures/throw.js",
    "content": "\nthrow new Error('Exit error message');\n"
  },
  {
    "path": "test/fixtures/toto.js",
    "content": "\nconsole.log(process.env.TOTO);\n"
  },
  {
    "path": "test/fixtures/wait_ready_event/http-wait-start.js",
    "content": "\nvar http = require('http');\n\nvar app = http.createServer(function(req, res) {\n  res.writeHead(200);\n  res.end('hey');\n})\n\n\nvar listener = app.listen(0, function() {\n  console.log('Listening on port ' + listener.address().port);\n  process.send('ready');\n});\n\nprocess.on('message', function(msg) {\n  if (msg == 'shutdown') {\n    console.log('Closing all connections...');\n    setTimeout(function() {\n      console.log('Finished closing connections');\n      process.exit(0);\n    }, 100);\n  }\n});\n"
  },
  {
    "path": "test/fixtures/wait_ready_event/http-wait-start_nocb.js",
    "content": "\nvar http = require('http');\n\nvar app = http.createServer(function(req, res) {\n  res.writeHead(200);\n  res.end('hey');\n})\n\n\nvar listener = app.listen(0, function() {\n  console.log('Listening on port ' + listener.address().port);\n});\n\nprocess.on('message', function(msg) {\n  if (msg == 'shutdown') {\n    console.log('Closing all connections...');\n    setTimeout(function() {\n      console.log('Finished closing connections');\n      process.exit(0);\n    }, 100);\n  }\n});\n"
  },
  {
    "path": "test/fixtures/watch/app-watch-rename.json",
    "content": "{\n  apps: [{\n    script : 'http.js',\n    name : 'http',\n    watch : true\n  }]\n}\n"
  },
  {
    "path": "test/fixtures/watch/app-watch.json",
    "content": "{\n  apps: [{\n    script : 'http.js',\n    name : 'http',\n    watch : true\n  }]\n}\n"
  },
  {
    "path": "test/fixtures/watch/app.json",
    "content": "{\n  apps: [{\n    script : 'http.js',\n    name : 'http',\n    watch : false,\n    env : {\n      SHOULDBETHERE : 'toto'\n    },\n    env_production : {\n      SHOULDBETHERE : 'TYPEENV PRODUCTION'\n    }\n  }]\n}\n"
  },
  {
    "path": "test/fixtures/watch/http.js",
    "content": "\nvar http = require('http');\n\nvar app = http.createServer(function(req, res) {\n  res.writeHead(200);\n  res.end('hey');\n})\n\nconsole.log(process.env.TOTO_ENV);\n\nvar listener = app.listen(0, function() {\n  console.log('Listening on port ' + listener.address().port);\n});\n\nprocess.on('message', function(msg) {\n  if (msg == 'shutdown') {\n    console.log('Closing all connections...');\n    setTimeout(function() {\n      console.log('Finished closing connections');\n      process.exit(0);\n    }, 100);\n  }\n});\n"
  },
  {
    "path": "test/fixtures/watcher/donotwatchme.dir/.gitkeep",
    "content": ""
  },
  {
    "path": "test/fixtures/watcher/server-watch.bak.js",
    "content": "var http = require('http');\n\nhttp.createServer(function(req, res) {\n  res.writeHead(200);\n  res.end('hey');\n}).listen(8010);\n"
  },
  {
    "path": "test/fixtures/watcher/server-watch.json",
    "content": "[{\n  \"name\"      : \"server-watch\",\n  \"script\"    : \"./server-watch.bak.js\",\n  \"watch\"     : [\"**\"],\n  \"ignore_watch\" : [\"*.log\"],\n  \"watch_options\" : {\n    \"followSymlinks\": true\n  }\n}]\n"
  },
  {
    "path": "test/fixtures/yaml-configuration/apps.yaml",
    "content": "apps:\n  - name: test\n    script: echo.js\n  - name: HTTP\n    script: child.js\n    instances: 4\n  - name: PythonApp\n    script: echo.py\n    interpreter: /usr/bin/python3\n    interpreter_args: -u\n    env:\n      DEFAULT_STR: 'PYTHONENV'\n"
  },
  {
    "path": "test/fixtures/yaml-configuration/apps.yml",
    "content": "apps:\n  - name: test\n    script: echo.js\n  - name: HTTP\n    script: child.js\n    instances: 4\n  - name: PythonApp\n    script: echo.py\n    interpreter: /usr/bin/python3\n    interpreter_args: -u\n"
  },
  {
    "path": "test/fixtures/yaml-configuration/child.js",
    "content": "\n\n\nvar http = require('http');\n\nhttp.createServer(function(req, res) {\n  res.writeHead(200);\n  res.end('hey');\n}).listen(8000);\n\nprocess.on('message', function(msg) {\n  if (msg == 'shutdown') {\n    console.log('Closing all connections...');\n    setTimeout(function() {\n      console.log('Finished closing connections');\n      process.exit(0);\n    }, 100);\n  }\n});\n"
  },
  {
    "path": "test/fixtures/yaml-configuration/echo.js",
    "content": "\nsetInterval(function() {\n  console.log('ok');\n}, 100);\n\nsetInterval(function() {\n  console.error('thisnok');\n}, 100);\n"
  },
  {
    "path": "test/fixtures/yaml-configuration/echo.py",
    "content": "#!/usr/bin/python\nimport os\nimport time\n\nwhile 1:\n    try:\n        print(os.environ['DEFAULT_STR'])\n    except KeyError:\n        print('RAWPython')\n    time.sleep(1)\n"
  },
  {
    "path": "test/fixtures/yaml-configuration/malformated.yml",
    "content": "{\n \"ASDSDASD\": \"ASDSADSAD\"\n}\n"
  },
  {
    "path": "test/helpers/apps.js",
    "content": "\nvar path = require('path');\nvar CLI  = require('../..');\n\nvar APPS = {};\n\n/**\n * Description\n * @method forkPM2\n * @return pm2\n */\nAPPS.forkPM2 = function(cb) {\n  var pm2 = require('child_process').fork('lib/Satan.js', [], {\n    env : process.env,\n    silent : process.env.DEBUG ? false : true\n  });\n\n  pm2.unref();\n\n  pm2.on('message', function() {\n    return cb(null, pm2);\n  });\n};\n\nAPPS.startSomeApps = function(pm2, cb) {\n  pm2.start({\n    script : './events/custom_action.js',\n    name   : 'custom-action'\n  }, cb);\n};\n\n/**\n * Description\n * @method launchApp\n * @param {} ipm2\n * @param {} script\n * @param {} name\n * @param {} cb\n * @return\n */\nAPPS.launchApp = function(ipm2, script, name, cb) {\n  ipm2.rpc.prepare({\n    pm_exec_path    : path.resolve(process.cwd(), 'test/fixtures/' + script),\n    pm_err_log_path : path.resolve(process.cwd(), 'test/' + name + 'err.log'),\n    pm_out_log_path : path.resolve(process.cwd(), 'test/' + name + '.log'),\n    pm_pid_path     : path.resolve(process.cwd(), 'test/child'),\n    exec_mode : 'cluster_mode',\n    name : name\n  }, cb);\n};\n\n/**\n * Description\n * @method launchAppFork\n * @param {} ipm2\n * @param {} script\n * @param {} name\n * @param {} cb\n * @return\n */\nAPPS.launchAppFork = function(ipm2, script, name, cb) {\n  ipm2.rpc.prepare({\n    pm_exec_path    : path.resolve(process.cwd(), 'test/fixtures/' + script),\n    pm_err_log_path : path.resolve(process.cwd(), 'test/errLogasdasd.log'),\n    pm_out_log_path : path.resolve(process.cwd(), 'test/outLogasdasd.log'),\n    pm_pid_path     : path.resolve(process.cwd(), 'test/child'),\n    exec_mode : 'fork_mode',\n    name : name\n  }, cb);\n};\n\nmodule.exports = APPS;\n"
  },
  {
    "path": "test/helpers/plan.js",
    "content": "\nvar assert = require('assert');\n\n/**\n * Description\n * @method Plan\n * @param {} count\n * @param {} done\n * @return\n */\nfunction Plan(count, done) {\n  this.done = done;\n  this.count = count;\n}\n\n/**\n * Description\n * @method ok\n * @param {} expression\n * @return\n */\nPlan.prototype.ok = function(expression) {\n  assert(expression);\n\n  if (this.count === 0) {\n    assert(false, 'Too many assertions called');\n  } else {\n    this.count--;\n  }\n\n  if (this.count === 0) {\n    this.done();\n  }\n};\n\nmodule.exports = Plan;\n"
  },
  {
    "path": "test/interface/README.md",
    "content": "\n## Structure\n\n.\n├── bus.fork.spec.mocha.js\n├── bus.spec.mocha.js\n├── custom-actions.mocha.js\n├── fixtures\n│   ├── http_transaction.js\n│   ├── human_event.js\n│   ├── log_out.js\n│   └── process_exception.js\n├── interactor.connect.mocha.js\n├── interactor.connect.two.mocha.js\n├── interactor.daemonizer.mocha.js\n├── password.mocha.js\n├── README.md\n├── remote.mocha.js\n└── scoped_pm2_actions.mocha.js\n\n\n### Test about pmx.notify not working\n\n- bus.fork.spec.mocha.js\n- bus.spec.mocha.js\n\n\n\n\n- bus.spec.mocha.js: event system sent (wo agent)\n- custom-actions.mocha.js: connect and try custom actions\n- interactor.daemonizer.mocha.js: basic methods testing for automatic re read of agent conf (wo agent)\n- interactor.connect.mocha.js:\n"
  },
  {
    "path": "test/interface/bus.fork.spec.mocha.js",
    "content": "\nvar should = require('should');\nvar PM2    = require('../..');\nvar Plan   = require('../helpers/plan.js');\n\nvar PROCESS_ARCH  = Object.keys({\n  pm_id  : 0,\n  name   : 'app'\n  // server: 'server name' - attached in interactor\n});\n\nvar PROCESS_EVENT = Object.keys({\n  event   : 'process event name',\n  manually: true,\n  process : PROCESS_ARCH,\n  at      : new Date()\n});\n\nvar LOG_EVENT = Object.keys({\n  data : 'string',\n  process : PROCESS_ARCH,\n  at  : new Date()\n});\n\nvar ERROR_EVENT = Object.keys({\n  at : new Date(),\n  data : {\n    stack : '\\n',\n    message : 'error'\n  },\n  process : PROCESS_ARCH\n});\n\nvar HUMAN_EVENT = Object.keys({\n  at      : new Date(),\n  process : PROCESS_ARCH,\n  data    : {\n    __name : 'event:name'\n  }\n});\n\nvar TRANSACTION_HTTP_EVENT = Object.keys({\n  data : {\n    url     : '/user/root',\n    method  : 'POST',\n    time    : 234,\n    code    : 200\n  },\n  at      : new Date(),\n  process : PROCESS_ARCH\n});\n\nprocess.on('uncaughtException', function(e) {\n  console.log(e.stack);\n  process.exit(1);\n});\n\ndescribe('PM2 BUS / RPC', function() {\n  var pm2 = new PM2.custom({\n    cwd         : __dirname + '/../fixtures/interface'\n  });\n  var pm2_bus;\n\n  after(function(done) {\n    pm2.delete('all', () => done());\n  });\n\n  before(function(done) {\n    pm2.connect(function() {\n      pm2.launchBus(function(err, bus) {\n        pm2_bus = bus;\n        setTimeout(done, 1000);\n      });\n    });\n  });\n\n  describe('Events', function() {\n    afterEach(function(done) {\n      pm2_bus.off('*');\n\n      pm2.delete('all', function(err, ret) {\n        done();\n      });\n    });\n\n    it('should (process:event) when start process get online event and start event with right properties', function(done) {\n      var plan = new Plan(2, done);\n\n      pm2_bus.on('*', function(event, data) {\n        if (event == 'process:event') {\n          event.should.eql('process:event');\n          data.should.have.properties(PROCESS_EVENT);\n          data.process.should.have.properties(PROCESS_ARCH);\n          plan.ok(true);\n        }\n      });\n\n      pm2.start('./child.js', {}, function(err, data) {\n        should(err).be.null();\n      });\n    });\n\n    it('should (log:out log:err)', function(done) {\n      var plan = new Plan(2, done);\n\n      pm2_bus.on('*', function(event, data) {\n        if (event == 'log:out') {\n          event.should.eql('log:out');\n\n          data.should.have.properties(LOG_EVENT);\n          plan.ok(true);\n        }\n        if (event == 'log:err') {\n          event.should.eql('log:err');\n\n          data.should.have.properties(LOG_EVENT);\n          plan.ok(true);\n        }\n      });\n\n      pm2.start('./log_out.js', {}, function(err, data) {\n        should(err).be.null();\n      });\n    });\n\n    it('should (process:exception)', function(done) {\n      var plan = new Plan(1, done);\n\n      pm2_bus.on('*', function(event, data) {\n        if (event == 'process:exception') {\n          data.should.have.properties(ERROR_EVENT);\n          data.process.should.have.properties(PROCESS_ARCH);\n          plan.ok('true');\n        }\n      });\n\n      pm2.start('./process_exception.js', {}, function(err, data) {\n        should(err).be.null();\n      });\n    });\n\n    it('should (human:event)', function(done) {\n\n      pm2_bus.on('*', function(event, data) {\n\n        if (event == 'human:event') {\n          data.should.have.properties(HUMAN_EVENT);\n          data.process.should.have.properties(PROCESS_ARCH);\n          return done();\n        }\n      });\n\n      pm2.start('./human_event.js', {}, function(err, data) {\n        should(err).be.null();\n      });\n    });\n\n    it('should (process:exception) with promise', function(done) {\n      var plan = new Plan(1, done);\n\n      pm2_bus.on('*', function(event, data) {\n        console.log(event)\n        if (event == 'process:exception') {\n          data.should.have.properties(ERROR_EVENT);\n          data.process.should.have.properties(PROCESS_ARCH);\n          plan.ok(true);\n        }\n      });\n\n      pm2.start('./promise_rejection.js', {}, function(err, data) {\n        should(err).be.null();\n      });\n    });\n  });\n\n});\n"
  },
  {
    "path": "test/interface/bus.spec.mocha.js",
    "content": "\nvar should = require('should');\nvar PM2    = require('../..');\nvar Plan   = require('../helpers/plan.js');\n\nconst PATH_FIXTURES = process.cwd() + '/test/interface/fixtures/';\n\nvar PROCESS_ARCH  = Object.keys({\n  pm_id  : 0,\n  name   : 'app'\n  // server: 'server name' - attached in interactor\n});\n\nvar PROCESS_EVENT = Object.keys({\n  event   : 'process event name',\n  manually: true,\n  process : PROCESS_ARCH,\n  at      : new Date()\n});\n\nvar LOG_EVENT = Object.keys({\n  data : 'string',\n  process : PROCESS_ARCH,\n  at  : new Date()\n});\n\nvar ERROR_EVENT = Object.keys({\n  at : new Date(),\n  data : {\n    stack : '\\n',\n    message : 'error'\n  },\n  process : PROCESS_ARCH\n});\n\nvar HUMAN_EVENT = Object.keys({\n  at      : new Date(),\n  process : PROCESS_ARCH,\n  data    : {\n    __name : 'event:name'\n  }\n});\n\nvar TRANSACTION_HTTP_EVENT = Object.keys({\n  data : {\n    url     : '/user/root',\n    method  : 'POST',\n    time    : 234,\n    code    : 200\n  },\n  at      : new Date(),\n  process : PROCESS_ARCH\n});\n\nprocess.on('uncaughtException', function(e) {\n  console.log(e.stack);\n  process.exit(1);\n});\n\ndescribe('PM2 BUS / RPC', function() {\n  var pm2 = new PM2.custom({\n    cwd         : __dirname + '/../fixtures/interface'\n  });\n  var pm2_bus;\n\n  after(function(done) {\n    pm2.delete('all', () => done());\n  });\n\n  before(function(done) {\n    pm2.connect(function() {\n      pm2.launchBus(function(err, bus) {\n        pm2_bus = bus;\n      });\n      done();\n    });\n  });\n\n  describe('Events', function() {\n    afterEach(function(done) {\n      pm2_bus.off('*');\n\n      pm2.delete('all', function(err, ret) {\n        done();\n      });\n    });\n\n    it('should (process:event) when start process get online event and start event with right properties', function(done) {\n      var plan = new Plan(2, done);\n\n      pm2_bus.on('*', function(event, data) {\n        if (event == 'process:event') {\n          event.should.eql('process:event');\n          data.should.have.properties(PROCESS_EVENT);\n          data.process.should.have.properties(PROCESS_ARCH);\n          plan.ok(true);\n        }\n      });\n\n      pm2.start('./child.js', {instances : 1}, function(err, data) {\n        should(err).be.null();\n      });\n    });\n\n    it('should (log:out log:err)', function(done) {\n      var plan = new Plan(2, done);\n\n      pm2_bus.on('*', function(event, data) {\n        if (event == 'log:out') {\n          event.should.eql('log:out');\n\n          data.should.have.properties(LOG_EVENT);\n          plan.ok(true);\n        }\n        if (event == 'log:err') {\n          event.should.eql('log:err');\n\n          data.should.have.properties(LOG_EVENT);\n          plan.ok(true);\n        }\n      });\n\n      pm2.start('./log_out.js', {instances : 1}, function(err, data) {\n        should(err).be.null();\n      });\n    });\n\n    it('should (process:exception)', function(done) {\n      var plan = new Plan(1, done);\n      var called = false\n\n      pm2_bus.on('*', function(event, data) {\n        if (event == 'process:exception') {\n          if (called) return\n          called = true\n          data.should.have.properties(ERROR_EVENT);\n          data.process.should.have.properties(PROCESS_ARCH);\n          plan.ok(true);\n        }\n      });\n\n      pm2.start('./process_exception.js', {instances : 1}, function(err, data) {\n        should(err).be.null();\n      });\n    });\n\n    it('should (process:exception) with promise', function(done) {\n      var called = false\n      pm2_bus.on('*', function(event, data) {\n        if (event == 'process:exception') {\n          if (called) return\n          called = true\n          data.should.have.properties(ERROR_EVENT);\n          data.process.should.have.properties(PROCESS_ARCH);\n          return done()\n        }\n      });\n\n      pm2.start('./promise_rejection.js', {instances: 1}, function(err, data) {\n        should(err).be.null();\n      });\n    });\n\n    it('should (human:event)', function(done) {\n      var called = false\n      pm2_bus.on('*', function(event, data) {\n        if (event == 'human:event') {\n          if (called) return\n          called = true\n          data.should.have.properties(HUMAN_EVENT);\n          data.process.should.have.properties(PROCESS_ARCH);\n          return done();\n        }\n      });\n\n      pm2.start('./human_event.js', {instances : 1}, function(err, data) {\n        should(err).be.null();\n      });\n    });\n\n  });\n\n});\n"
  },
  {
    "path": "test/interface/mocha.opts",
    "content": "--timeout 25000\n"
  },
  {
    "path": "test/interface/utility.mocha.js",
    "content": "\nvar assert = require('assert');\nvar Utility = require('../../lib/Utility.js');\n\ndescribe('Utility', function() {\n  describe('.getCanonicModuleName', function () {\n    it('should get null without invalid parameters', function() {\n      assert(Utility.getCanonicModuleName() === null);\n      assert(Utility.getCanonicModuleName(/aa/) === null);\n      assert(Utility.getCanonicModuleName(111) === null);\n      assert(Utility.getCanonicModuleName({}) === null);\n    });\n\n    it('should works with all notation', function() {\n      assert(Utility.getCanonicModuleName('ma-zal/pm2-slack') === 'pm2-slack');\n      assert(Utility.getCanonicModuleName('pm2-slack@1.0.0') === 'pm2-slack');\n      assert(Utility.getCanonicModuleName('pm2-slack-1.0.0.tgz') === 'pm2-slack');\n      assert(Utility.getCanonicModuleName('ma-zal/pm2-slack') === 'pm2-slack');\n      assert(Utility.getCanonicModuleName('ma-zal/pm2-slack#own-branch') === 'pm2-slack');\n      assert(Utility.getCanonicModuleName('pm2-slack') === 'pm2-slack');\n      assert(Utility.getCanonicModuleName('@org/pm2-slack') === '@org/pm2-slack');\n      assert(Utility.getCanonicModuleName('@org/pm2-slack@latest') === '@org/pm2-slack');\n      assert(Utility.getCanonicModuleName('git+https://github.com/user/pm2-slack') === 'pm2-slack');\n      assert(Utility.getCanonicModuleName('git+https://github.com/user/pm2-slack.git') === 'pm2-slack');\n      assert(Utility.getCanonicModuleName('file:///home/user/pm2-slack') === 'pm2-slack');\n      assert(Utility.getCanonicModuleName('file://./pm2-slack') === 'pm2-slack');\n      assert(Utility.getCanonicModuleName('file:///home/user/pm2-slack/') === 'pm2-slack');\n      assert(Utility.getCanonicModuleName('http-server') === 'http-server');\n      assert(Utility.getCanonicModuleName('http://registry.com:12/modules/my-module?test=true') === 'my-module');\n      assert(Utility.getCanonicModuleName('http://registry.com:12/modules/http-my-module?test=true') === 'http-my-module');\n    });\n  });\n\n});\n"
  },
  {
    "path": "test/parallel.js",
    "content": "\nconst forEachLimit = require('async/forEachLimit')\nconst fs = require('fs')\nconst exec = require('child_process').exec\nconst path = require('path')\nconst chalk = require('ansis')\nconst Table = require('cli-table-redemption');\n\nconst testFolder = './test/e2e/'\n\nconst CONCURRENT_TEST = 3\nconst DOCKER_IMAGE_NAME = 'pm2-test'\n\nvar timings = {};\n\nfunction run(cmd, cb) {\n  exec(cmd, function(err, stdout, stderr) {\n    if (err) {\n      console.log(`Retrying ${cmd}`)\n      return exec(cmd, function(err, stdout, stderr) {\n        if (err) return cb(stdout.split('\\n'));\n        return cb(null);\n      })\n    }\n    return cb(null)\n  })\n}\n\nfunction buildContainer(cb) {\n  exec(`docker build -t ${DOCKER_IMAGE_NAME} -f test/Dockerfile .`, cb)\n}\n\nfunction listAllTest(cb) {\n  var test_suite = []\n\n  fs.readdir(testFolder, (err, folders) => {\n    forEachLimit(folders, 4, (folder, next) => {\n      var fold = path.join(testFolder, folder)\n      fs.readdir(fold, (err, files) => {\n        if (err) return next()\n        files.forEach((file) => {\n          test_suite.push(path.join(fold, file))\n        })\n        next()\n      })\n    }, function() {\n      launchTestSuite(test_suite, cb)\n    })\n  })\n}\n\nfunction launchTestSuite(files, cb) {\n  forEachLimit(files, CONCURRENT_TEST, function(file, next) {\n    var cmd = `docker run -v ${path.resolve(__dirname, '..')}:/var/pm2 ${DOCKER_IMAGE_NAME} bash ${file}`\n\n    console.log(chalk.bold(`Running test ${file}`))\n    timings[file] = new Date().getTime()\n\n    run(cmd, function(err) {\n      if (err) {\n        // Display Error\n        console.error(chalk.bold.red(`${'='.repeat(25)} Test File ${file} has failed ${'='.repeat(25)}`))\n        console.error(chalk.bold('Output (stderr):'))\n        err.forEach(function(line) {\n          console.error(line)\n        })\n        console.error(chalk.bold.red(`${'='.repeat(80)}`))\n        return next(err)\n      }\n\n      timings[file] = new Date().getTime() - timings[file]\n\n      console.log(chalk.bold.green(`✓ Test ${file} success`))\n      return next();\n    })\n  }, (err) => {\n    if (err) {\n      console.log('Test Suite has failed')\n      cb(err)\n    }\n    console.log('Test Suite passed succesfully')\n    cb()\n  })\n}\n\nbuildContainer(function(err) {\n  if (err) {\n    console.error(err)\n    process.exit(1)\n  }\n  console.log(`Container ${DOCKER_IMAGE_NAME} has been built`)\n\n  return listAllTest(function(err) {\n\n    var table = new Table({\n      head: ['Test', 'Duration'],\n      style : {'padding-left' : 1, head : ['cyan', 'bold'], compact : true}\n    })\n\n    Object.keys(timings).forEach(function(test) {\n      table.push([test, timings[test]])\n    })\n\n    console.log(table.toString());\n\n    if (err) {\n      return console.error(chalk.bold.red('Test suite failed'))\n    }\n    console.log(chalk.bold.blue('Test suite succeeded'))\n  })\n})\n"
  },
  {
    "path": "test/pm2_check_dependencies.sh",
    "content": "#!/usr/bin/env bash\n\nSRC=$(cd $(dirname \"$0\"); pwd)\n\n# Abort script at first error\nset -e\n\necho \"Checking dependencies ....\";\n\n\ndependencies=(\"php\" \"nvm\" \"node\" \"python\");\ndeclare -A depCmd;\ndepCmd=([nvm]=\"echo $NVM_DIR >/dev/null\");\n\n\nerror=false;\n\nfor i in \"${dependencies[@]}\"; do\n    currentDepInstalled=true;\n\n    if [ ${depCmd[$i]+_} ]; then\n      eval ${depCmd[$i]} || { error=true; currentDepInstalled=false;}\n    else\n      command -v $i >/dev/null 2>&1 || { error=true; currentDepInstalled=false;}\n    fi\n\n    if [ \"$currentDepInstalled\" = true ]; then\n      echo -e '\\E[32m'\"\\033[1m[OK]\\033[0m\" $i;\n    else\n      echo -e '\\E[31m'\"\\033[1m[KO]\\033[0m\" $i;\n    fi\ndone\n\n\nif [ \"$error\" = true ]; then\n  echo \"Aborting.\";\n  exit 1;\nfi\n\n\n"
  },
  {
    "path": "test/programmatic/api.backward.compatibility.mocha.js",
    "content": "\nprocess.chdir(__dirname);\n\nvar PM2 = require('../..');\nvar should = require('should');\n\ndescribe('API backward compatibility checks', function() {\n  describe('Backward compatibility', function() {\n    it('should start pm2 in no daemon mode', function(done) {\n      PM2.connect(true, function(err) {\n        should(PM2.daemon_mode).be.false();\n        should(PM2.Client.daemon_mode).be.false();\n        done();\n      });\n    });\n\n    it('should be able to start a script', function(done) {\n      PM2.start('./../fixtures/child.js', function(err) {\n        should(err).be.null();\n        done();\n      });\n    });\n\n    it('should list one process', function(done) {\n      PM2.list(function(err, list) {\n        should(err).be.null();\n        should(list.length).eql(1);\n        done();\n      });\n    });\n\n    it('should kill PM2 in no daemon', function(done) {\n      PM2.kill(done);\n    });\n  });\n});\n"
  },
  {
    "path": "test/programmatic/api.mocha.js",
    "content": "\nprocess.chdir(__dirname);\n\nvar PM2 = require('../..');\nvar should = require('should');\n\ndescribe('API checks', function() {\n  describe('PM2 API case#1', function() {\n    before(function(done) {\n      PM2.delete('all', function() { done() });\n    });\n\n    after(function(done) {\n      PM2.kill(done);\n    });\n\n    it('should instanciate a new pm2 with old api', function() {\n      should.exists(PM2.pm2_home);\n      should(PM2.daemon_mode).be.true();\n      PM2.cwd.should.eql(process.cwd());\n      should.exists(PM2.Client);\n    });\n\n    it('should connect to PM2', function(done) {\n      PM2.connect(done);\n    });\n\n    it('should start a script', function(done) {\n      PM2.start('./../fixtures/child.js', function(err) {\n        should(err).be.null();\n        PM2.list(function(err, list) {\n          should(err).be.null();\n          should(list.length).eql(1);\n          done();\n        });\n      });\n    });\n\n    it('should stop app by id', function(done) {\n      PM2.stop(0, done);\n    });\n\n    it('should start app by id', function(done) {\n      PM2.restart(0, done);\n    });\n\n\n    it('should fail if starting same script again', function(done) {\n      PM2.start('./../fixtures/child.js', function(err) {\n        should(err).not.be.null();\n        PM2.list(function(err, list) {\n          should(err).be.null();\n          should(list.length).eql(1);\n          done();\n        });\n      });\n    });\n\n    it('should FORCE starting same script again', function(done) {\n      PM2.start('./../fixtures/child.js', {force :true }, function(err) {\n        should(err).be.null();\n        PM2.list(function(err, list) {\n          should(err).be.null();\n          should(list.length).eql(2);\n          done();\n        });\n      });\n    });\n\n    it('should delete ALL', function(done) {\n      PM2.delete('all', function(err) {\n        should(err).be.null();\n        PM2.list(function(err, list) {\n          should(err).be.null();\n          should(list.length).eql(0);\n          done();\n        });\n      });\n    });\n  });\n\n  describe('PM2 API case#2 (JSON style)', function() {\n    before(function(done) {\n      PM2.delete('all', function() { done() });\n    });\n\n    after(function(done) {\n      PM2.kill(done);\n    });\n\n    it('should start script in cluster mode, 4 instances', function(done) {\n      PM2.start({\n        script : './../fixtures/child.js',\n        instances : 4,\n        name : 'http-test'\n      }, function(err) {\n        should(err).be.null();\n        PM2.list(function(err, list) {\n          should(err).be.null();\n          should(list.length).eql(4);\n          done();\n        });\n      });\n    });\n\n    it('should stop app', function(done) {\n      PM2.stop('http-test', function(err, procs) {\n        should(err).be.null();\n        procs.length.should.eql(4);\n        PM2.list(function(err, list) {\n          should(list.length).eql(4);\n          list.forEach(function(proc) {\n            proc.pm2_env.status.should.eql('stopped');\n          });\n          done();\n        });\n      });\n    });\n\n    it('should restart all apps', function(done) {\n      PM2.restart('http-test', function(err, procs) {\n        should(err).be.null();\n        PM2.list(function(err, list) {\n          should(list.length).eql(4);\n          list.forEach(function(proc) {\n            proc.pm2_env.status.should.eql('online');\n          });\n          done();\n        });\n      });\n    });\n  });\n\n  describe('Should keep environment variables', function() {\n    it('should start app with treekill', function(done) {\n      PM2.start({\n        script : './../fixtures/child.js',\n        instances : 1,\n        treekill : false,\n        name : 'http-test'\n      }, function(err) {\n        should(err).be.null();\n        PM2.list(function(err, list) {\n          should(err).be.null();\n          should(list.length).eql(1);\n          should(list[0].pm2_env.treekill).be.false;\n          done();\n        });\n      });\n    });\n\n    it('should restart app and treekill still at false', function(done) {\n      PM2.restart('http-test', function() {\n        PM2.list(function(err, list) {\n          should(err).be.null();\n          should(list.length).eql(1);\n          should(list[0].pm2_env.treekill).be.false;\n          done();\n        });\n      });\n    });\n\n  });\n\n  describe('Issue #2337', function() {\n    before(function(done) {\n      PM2.delete('all', function() { done() });\n    });\n\n    after(function(done) {\n      PM2.kill(done);\n    });\n\n    it('should start two app with same name', function(done) {\n      PM2.start({\n        script : './../fixtures/child.js',\n        instances : 2,\n        exec_mode : 'fork',\n        name : 'http-test'\n      }, function(err) {\n        should(err).be.null();\n        PM2.list(function(err, list) {\n          should(err).be.null();\n          list.forEach(function(proc) {\n            proc.pm2_env.exec_mode.should.eql('fork_mode');\n          });\n          should(list.length).eql(2);\n          done();\n        });\n      });\n    });\n\n    it('should stop first app', function(done) {\n      PM2.stop(0, done);\n    });\n\n    it('should force start a 3rd script', function(done) {\n      PM2.start('./../fixtures/child.js', {\n        force : true,\n        name : 'toto'\n      }, function() {\n        PM2.list(function(err, list) {\n          list.length.should.eql(3);\n          done();\n        });\n      });\n    });\n  });\n\n\n  describe('PM2 auto connect feature', function() {\n    after(function(done) {\n      PM2.kill(function() {\n        done();\n      });\n    });\n\n    it('should instanciate a new pm2 with old api', function() {\n      should.exists(PM2.pm2_home);\n      should(PM2.daemon_mode).be.true();\n      PM2.cwd.should.eql(process.cwd());\n      should.exists(PM2.Client);\n    });\n\n    it('should be able to start a script without connect', function(done) {\n      PM2.start('./../fixtures/child.js', function(err) {\n        should(err).be.null();\n        done();\n      });\n    });\n\n    it('should do random commands', function(done) {\n      PM2.list(function(err, list) {\n        should(err).be.null();\n        should(list.length).eql(1);\n        PM2.delete('all', function(err) {\n          should(err).be.null();\n          PM2.list(function(err, list) {\n            should(err).be.null();\n            should(list.length).eql(0);\n            done();\n          });\n        });\n      });\n    });\n\n  });\n\n  describe('Custom PM2 instance', function() {\n    var pm2;\n\n    after(function(done) {\n      pm2.kill(done);\n    });\n\n    it('should create new custom PM2 instance', function() {\n      pm2 = new PM2.custom({\n        daemon_mode : true\n      });\n      should.exists(pm2.pm2_home);\n      should(pm2.daemon_mode).be.true();\n      pm2.cwd.should.eql(process.cwd());\n      should.exists(pm2.Client);\n    });\n\n    it('should be able to start a script without connect', function(done) {\n      pm2.start('./../fixtures/child.js', function(err) {\n        should(err).be.null();\n        done();\n      });\n    });\n\n    it('should do random commands', function(done) {\n      pm2.list(function(err, list) {\n        should(err).be.null();\n        should(list.length).eql(1);\n        pm2.delete('all', function(err) {\n          should(err).be.null();\n          pm2.list(function(err, list) {\n            should(err).be.null();\n            should(list.length).eql(0);\n            done();\n          });\n        });\n      });\n    });\n  });\n\n  describe('Should start pm2 in do daemon mode', function() {\n    var pm2;\n\n    after(function(done) {\n      pm2.kill(done);\n    });\n\n    it('should create new custom PM2 instance', function() {\n      pm2 = new PM2.custom({\n        daemon_mode : false\n      });\n\n      should.exists(pm2.pm2_home);\n      should(pm2.daemon_mode).be.false();\n      pm2.cwd.should.eql(process.cwd());\n      should.exists(pm2.Client);\n    });\n  });\n\n  describe('Launch modules', function() {\n    var Modularizer = require('../../lib/API/Modules/Modularizer');\n    var module = 'pm2-server-monit';\n\n    after(function(done) {\n      Modularizer.uninstall(PM2, module, done);\n    });\n\n    it('Should start up modules', function(done) {\n      PM2.connect(true, function(err) {\n        should(err).be.null();\n\n        Modularizer.install(PM2, module, function() {\n          PM2.stop(module, function() {\n            should(err).be.null();\n\n            PM2.launchModules(function(err) {\n              should(err).be.null();\n\n              PM2.list(function(err, list) {\n                should(err).be.null();\n                should(list[0].name).eql(module);\n                should(list[0].pm2_env.status).eql('online');\n                done();\n              });\n            });\n          });\n        });\n      });\n    });\n  });\n\n});\n"
  },
  {
    "path": "test/programmatic/auto_restart.mocha.js",
    "content": "\nconst pm2 = require('../..');\nconst should = require('should');\nconst path = require('path')\n\ndescribe('PM2 auto restart on uncaughtexception', function() {\n  var test_path = path.join(__dirname, 'fixtures', 'auto-restart')\n\n  after((done) => {\n    pm2.delete('all', () => { done() })\n  })\n\n  before((done) => {\n    pm2.uninstall('all', () => {\n      pm2.delete('all', () => { done() })\n    })\n  })\n\n  it('should start a failing app in fork mode', function(done) {\n    pm2.start({\n      script: path.join(test_path, 'throw.js'),\n    }, (err, apps) => {\n      setTimeout(function() {\n        pm2.list((err, list) => {\n          should(list[0].pm2_env.restart_time).aboveOrEqual(0)\n          pm2.delete('throw', () => {\n            done()\n          })\n        })\n      }, 200)\n    })\n  })\n\n  it('should start a failing app in cluster mode', function(done) {\n    pm2.start({\n      script: path.join(test_path, 'throw.js'),\n      instances: 2\n    }, (err, apps) => {\n      setTimeout(function() {\n        pm2.list((err, list) => {\n          should(list[0].pm2_env.restart_time).aboveOrEqual(0)\n          pm2.delete('throw', () => {\n            done()\n          })\n        })\n      }, 200)\n    })\n  })\n})\n"
  },
  {
    "path": "test/programmatic/client.mocha.js",
    "content": "\nvar should = require('should');\nvar Client = require('../../lib/Client');\n\ndescribe('Client Daemon', function() {\n  var client;\n\n  it('should instanciate a new client', function() {\n    client = new Client({ independent : true });\n    should.exist(client.rpc_socket_file);\n    should.exist(client.pub_socket_file);\n    should.exist(client.pm2_home);\n    should.exist(client.daemon_mode);\n  });\n\n  it('should start a deamon', function(done) {\n    client.start(done);\n  });\n\n  it('should launch bus system', function(done) {\n    client.launchBus(done);\n  });\n\n  it('should get exposed methods', function(done) {\n    client.getExposedMethods(done);\n  });\n\n  it('should execute a daemon function', function(done) {\n    client.executeRemote('ping', {}, function(err, res) {\n      res.msg.should.eql('pong');\n      done(err);\n    });\n  });\n\n  it('should disconnwct bus', function(done) {\n    client.disconnectBus(done);\n  });\n\n  it('should kill daemon', function(done) {\n    client.killDaemon(done);\n  });\n\n  // It is the job of the CLI\n  describe.skip('Custom PM2 Home location', function() {\n    it('should instanciate a PM2 on another folder', function(done) {\n      client = new Client({\n        pm2_home : '/tmp/test'\n      });\n      should(client.pm2_home).eql('/tmp/test')\n\n      client.start(done);\n    });\n\n    it('should kill daemon', function(done) {\n      client.killDaemon(done);\n    });\n  });\n\n\n});\n"
  },
  {
    "path": "test/programmatic/cluster.mocha.js",
    "content": "\nprocess.env.NODE_ENV = 'test';\n\nvar PM2    = require('../..');\nvar should = require('should');\nvar path   = require('path');\nvar Plan   = require('../helpers/plan.js');\n\nprocess.chdir(__dirname);\n\ndescribe('Cluster programmatic tests', function() {\n  var pm2 = new PM2.custom({\n    cwd : '../fixtures'\n  });\n\n  after(function(done) {\n    pm2.kill(done)\n  });\n\n  describe('Start with different instances number parameter', function() {\n\n    afterEach(function(done) {\n      pm2.delete('all', done);\n    });\n\n    it('should start 4 processes', function(done) {\n      pm2.start({\n        script    : './child.js',\n        instances : 4\n      }, function(err, data) {\n        should(err).be.null();\n\n        pm2.list(function(err, ret) {\n          should(err).be.null();\n          ret.length.should.eql(4);\n          done();\n        });\n      });\n    });\n  });\n\n  describe('Action methods', function() {\n    before(function(done) {\n      pm2.start({\n        script    : '../fixtures/child.js',\n        instances : 4\n      }, done);\n    });\n\n    it('should RESTART all apps', function(done) {\n      pm2.restart('all', function(err, data) {\n        should(err).be.null();\n\n        pm2.list(function(err, procs) {\n          should(err).be.null();\n          procs.length.should.eql(4);\n          procs.forEach(function(proc) {\n            proc.pm2_env.restart_time.should.eql(1);\n          });\n          done();\n        });\n      });\n    });\n\n    it('should RELOAD all apps', function(done) {\n      pm2.reload('all', function(err, data) {\n        should(err).be.null();\n\n        pm2.list(function(err, procs) {\n          should(err).be.null();\n          procs.length.should.eql(4);\n          procs.forEach(function(proc) {\n            proc.pm2_env.restart_time.should.eql(2);\n          });\n          done();\n        });\n      });\n    });\n\n    it('should GRACEFUL RELOAD all apps', function(done) {\n      pm2.reload('all', function(err, data) {\n        should(err).be.null();\n\n        pm2.list(function(err, procs) {\n          should(err).be.null();\n          procs.length.should.eql(4);\n          procs.forEach(function(proc) {\n            proc.pm2_env.restart_time.should.eql(3);\n          });\n          done();\n        });\n      });\n    });\n  });\n\n  describe('Scaling feature', function() {\n    after(function(done) {\n      pm2.delete('all', done);\n    });\n\n    before(function(done) {\n      pm2.delete('all', function() {\n        pm2.start({\n          script    : '../fixtures/child.js',\n          instances : 4,\n          name      : 'child'\n        }, done);\n      });\n    });\n\n    it('should scale up application to 8', function(done) {\n      pm2.scale('child', 8, function(err, procs) {\n        should(err).be.null();\n\n        pm2.list(function(err, procs) {\n          should(err).be.null();\n          procs.length.should.eql(8);\n          done();\n        });\n      });\n    });\n\n    it('should scale down application to 2', function(done) {\n      pm2.scale('child', 2, function(err, procs) {\n        should(err).be.null();\n\n        pm2.list(function(err, procs) {\n          should(err).be.null();\n          procs.length.should.eql(2);\n          done();\n        });\n      });\n    });\n\n    it('should do nothing', function(done) {\n      pm2.scale('child', 2, function(err, procs) {\n        should(err).not.be.null();\n        done();\n      });\n    });\n  });\n\n  // Skip Becoz Bun\n  describe.skip('Listen timeout feature', function() {\n    after(function(done) {\n      pm2.delete('all', done);\n    });\n\n    it('should start script with 1000ms listen timeout', function(done) {\n      pm2.start({\n        script    : './child.js',\n        listen_timeout : 1000,\n        exec_mode: 'cluster',\n        instances : 1,\n        name      : 'child'\n      }, done);\n    });\n\n    it('should have listen timeout updated', function(done) {\n      pm2.list(function(err, list) {\n        should(list[0].pm2_env.listen_timeout).eql(1000);\n        should(list.length).eql(1);\n        done();\n      });\n    });\n\n    it('should take listen_timeout into account', function(done) {\n      var called = false;\n      var plan = new Plan(3, done);\n\n      setTimeout(function() {\n        should(called).be.false();\n        plan.ok(true);\n      }, 800);\n\n      setTimeout(function() {\n        should(called).be.true();\n        plan.ok(true);\n      }, 2500);\n\n      pm2.reload('all', function(err, data) {\n        called = true;\n        plan.ok(true);\n      });\n    });\n\n    it('should restart script with different listen timeout', function(done) {\n      pm2.restart({\n        script    : './child.js',\n        listen_timeout : 100,\n        instances : 1,\n        name      : 'child'\n      }, done);\n    });\n\n    it('should have listen timeout updated', function(done) {\n      pm2.list(function(err, list) {\n        should(list[0].pm2_env.listen_timeout).eql(100);\n        should(list.length).eql(1);\n        done();\n      });\n    });\n\n    it('should be reloaded after 100ms', function(done) {\n      var called = false;\n\n      setTimeout(function() {\n        should(called).be.true();\n        done();\n      }, 500);\n\n      pm2.reload('all', function(err, data) {\n        called = true;\n      });\n    });\n  });\n\n  describe('Kill timeout feature', function() {\n    after(function(done) {\n      pm2.delete('all', done);\n    });\n\n    it('should start script with 1000ms listen timeout', function(done) {\n      pm2.start({\n        script    : './cluster/sigint_catcher.js',\n        kill_timeout : 1000,\n        instances : 1,\n        name      : 'sigint'\n      }, done);\n    });\n\n    it('should have listen timeout updated', function(done) {\n      pm2.list(function(err, list) {\n        should(list[0].pm2_env.kill_timeout).eql(1000);\n        should(list.length).eql(1);\n        done();\n      });\n    });\n\n    it('should script not be killed before kill timeout', function(done) {\n      var called = false;\n\n      setTimeout(function() {\n        should(called).be.false();\n      }, 800);\n\n      pm2.reload('sigint', function() {\n        called = true;\n        done();\n      });\n    });\n\n  });\n\n});\n"
  },
  {
    "path": "test/programmatic/common.mocha.js",
    "content": "var Common = require('../../lib/Common');\nvar should = require('should');\n\nprocess.chdir(__dirname);\n\ndescribe('Common utilities', function () {\n  describe('Config file detection', function () {\n    var tests = [\n      { arg: \"ecosystem.json\", expected: \"json\" },\n      { arg: \"ecosystem.yml\", expected: \"yaml\" },\n      { arg: \"ecosystem.yaml\", expected: \"yaml\" },\n      { arg: \"ecosystem.config.js\", expected: \"js\" },\n      { arg: \"ecosystem.config.cjs\", expected: \"js\" },\n      { arg: \"ecosystem.config.mjs\", expected: \"mjs\" },\n    ]\n\n    tests.forEach(function (test) {\n      it('should accept configuration file ' + test.arg , function () {\n        var result = Common.isConfigFile(test.arg);\n        should(result).eql(test.expected);\n      })\n    });\n\n    it('should not accept unknown filename', function () {\n      should(Common.isConfigFile('lorem-ipsum.js')).be.null();\n    })\n  })\n\n  describe('Config file candidates', function () {\n    it('should return an array with well-known file extensions', function () {\n      var result = Common.getConfigFileCandidates('ecosystem');\n      should(result).eql([\n        'ecosystem.json',\n        'ecosystem.yml',\n        'ecosystem.yaml',\n        'ecosystem.config.js',\n        'ecosystem.config.cjs',\n        'ecosystem.config.mjs'\n      ]);\n    });\n  });\n\n})\n"
  },
  {
    "path": "test/programmatic/conf_update.mocha.js",
    "content": "\nconst PM2 = require('../..');\nconst should = require('should');\n\nprocess.chdir(__dirname);\n\ndescribe('Modules programmatic testing', function() {\n  var pm2;\n\n  after(function(done) {\n    pm2.kill(done);\n  });\n\n  it('should instanciate PM2', function() {\n    pm2 = new PM2.custom({\n      cwd : '../fixtures'\n    });\n  });\n\n  it('should start 4 processes', function(done) {\n    pm2.start({\n      script    : './echo.js',\n      instances : 4,\n      uid : process.env.USER,\n      force : true\n    }, function(err, procs) {\n      should(err).eql(null);\n      should(procs.length).eql(4);\n      should(procs[0].pm2_env.uid).eql(process.env.USER);\n      done();\n    });\n  });\n\n  it('should start 4 processes', function(done) {\n    pm2.restart('echo', {\n      uid : process.env.USER\n    }, function(err, procs) {\n      console.log(JSON.stringify(procs[0].pm2_env, '', 2));\n      should(err).eql(null);\n      should(procs.length).eql(4);\n      should(procs[0].pm2_env.uid).eql(process.env.USER);\n      done();\n    });\n  });\n});\n"
  },
  {
    "path": "test/programmatic/configuration.mocha.js",
    "content": "\nvar should = require('should');\nvar PM2 = require('../..');\n\nvar Configuration = require('../../lib/Configuration.js');\n\ndescribe('Configuration via SET / GET tests', function() {\n  before(function(done) {\n    PM2.list(done);\n  });\n\n  it('should set a value', function(done) {\n    Configuration.set('key1', 'val1', function(err, data) {\n      should.not.exists(err);\n      done();\n    });\n  });\n\n  it('should get all values', function(done) {\n    Configuration.getAll(function(err, data) {\n      should.not.exists(err);\n      data.key1.should.eql('val1');\n      done();\n    });\n  });\n\n  it('should set another value', function(done) {\n    Configuration.set('key2', 'val2', function(err, data) {\n      should.not.exists(err);\n      done();\n    });\n  });\n\n  it('should get all values', function(done) {\n    Configuration.getAll(function(err, data) {\n      should.not.exists(err);\n      data.key1.should.eql('val1');\n      data.key2.should.eql('val2');\n      done();\n    });\n  });\n\n  it('should unset first value', function(done) {\n    Configuration.unset('key1', function(err, data) {\n      should.not.exists(err);\n      done();\n    });\n  });\n\n  it('should get all values', function(done) {\n    Configuration.getAll(function(err, data) {\n      should.not.exists(err);\n      should.not.exists(data.key1);\n      data.key2.should.eql('val2');\n      done();\n    });\n  });\n\n  it('should get all values SYNCHRONOUSLY', function() {\n    var data = Configuration.getAllSync();\n\n    should.not.exists(data.key1);\n    data.key2.should.eql('val2');\n  });\n\n  describe('Sub value system', function() {\n    it('should set a sub key', function(done) {\n      Configuration.set('module-name.var1', 'val1', function(err, data) {\n        should.not.exists(err);\n        done();\n      });\n    });\n\n    it('should set a second sub key', function(done) {\n      Configuration.set('module-name.var2', 'val2', function(err, data) {\n        should.not.exists(err);\n        done();\n      });\n    });\n\n    it('should get the val', function(done) {\n      Configuration.getAll(function(err, data) {\n        should.not.exists(err);\n        data['module-name']['var1'].should.eql('val1');\n        data['module-name']['var2'].should.eql('val2');\n        done();\n      });\n    });\n\n    it('should get the val with .get', function(done) {\n      Configuration.get('module-name.var1', function(err, data) {\n        should.not.exists(err);\n        data.should.eql('val1');\n        done();\n      });\n    });\n\n    it('should get the val with .get', function(done) {\n      Configuration.get('module-name.var2', function(err, data) {\n        should.not.exists(err);\n        data.should.eql('val2');\n        done();\n      });\n    });\n\n    it('should NOT get the val with .get', function(done) {\n      Configuration.get('moduleasd-name.var2', function(err, data) {\n        should.exists(err);\n        should(data).be.null();\n        done();\n      });\n    });\n\n    it('should NOT get the val with .get', function(done) {\n      Configuration.get('module-name.var3', function(err, data) {\n        should.exists(err);\n        should(data).be.null();\n        done();\n      });\n    });\n\n  });\n\n  describe('Sub value system with :', function() {\n    it('should set a sub key', function(done) {\n      Configuration.set('module-name2:var1', 'val1', function(err, data) {\n        should.not.exists(err);\n        done();\n      });\n    });\n\n    it('should set a second sub key', function(done) {\n      Configuration.set('module-name2:var2', 'val2', function(err, data) {\n        should.not.exists(err);\n        done();\n      });\n    });\n\n    it('should get the val', function(done) {\n      Configuration.getAll(function(err, data) {\n        should.not.exists(err);\n        data['module-name2']['var1'].should.eql('val1');\n        data['module-name2']['var2'].should.eql('val2');\n        done();\n      });\n    });\n\n    it('should unset the val', function(done) {\n      Configuration.unset('module-name2:var2', function(err, data) {\n        should.not.exists(err);\n        data['module-name2']['var1'].should.eql('val1');\n        should.not.exists(data['module-name2']['var2']);\n        done();\n      });\n    });\n\n  });\n\n  describe('Sync', function() {\n    before(function() {\n      Configuration.unsetSync('module-name2');\n    });\n\n    it('should have 0 modules listed', function(done) {\n      var data = Configuration.getSync('module-name2');\n\n      should(data).be.null();\n      done();\n    });\n\n    it('should set a sub key', function(done) {\n      var ret = Configuration.setSync('module-name2:var1', 'val1');\n\n      done();\n    });\n\n    it('should have one key', function(done) {\n      var data = Configuration.getSync('module-name2');\n\n      data['var1'].should.eql('val1');\n      done();\n    });\n\n\n    it('should set a second sub key', function(done) {\n      var ret = Configuration.setSync('module-name2:var2', 'val2');\n\n      done();\n    });\n\n    it('should get the val', function() {\n      var data = Configuration.getSync('module-name2:var2');\n      data.should.eql('val2');\n    });\n\n    it('should get null for unknown val', function() {\n      var data = Configuration.getSync('module-name2:var23333');\n      should(data).be.null();\n    });\n\n  });\n\n  describe('Not split what is inside double quotes', function() {\n    it('should do it', function(done) {\n      Configuration.set('module-name2:\"var2:toto\"', 'val2', function(err, data) {\n        should.not.exists(err);\n        done();\n      });\n    });\n\n    it('should get the val', function() {\n      var data = Configuration.getSync('module-name2:\"var2:toto\"');\n      data.should.eql('val2');\n    });\n\n    it('should do it', function(done) {\n      Configuration.set('module-name3.\"var45.toto\"', 'val2', function(err, data) {\n        should.not.exists(err);\n        done();\n      });\n    });\n\n    it('should get the val', function() {\n      var data = Configuration.getSync('module-name3.\"var45.toto\"');\n      data.should.eql('val2');\n    });\n\n  });\n\n  describe('Multiset', function() {\n    it('should mutliset configuration', function(done) {\n      Configuration.multiset('module-name3.\"var45.toto\" val2 k2 v2 k3 v3', function(err, data) {\n        should.not.exists(err);\n        done();\n      });\n    });\n\n    it('should get values', function(done) {\n      var data = Configuration.getSync('module-name3.\"var45.toto\"');\n      data.should.eql('val2');\n      data = Configuration.getSync('k2');\n      data.should.eql('v2');\n      data = Configuration.getSync('k3');\n      data.should.eql('v3');\n      done();\n    });\n\n  });\n\n});\n"
  },
  {
    "path": "test/programmatic/containerizer.mocha.js",
    "content": "\nvar Containerizer = require('../../lib/API/Containerizer.js');\nvar path          = require('path');\nvar fs            = require('fs');\nvar should        = require('should');\nvar Plan          = require('../helpers/plan.js');\n\ndescribe('Containerizer unit tests', function() {\n  var fixture_path = path.join(__dirname, '../fixtures/containerizer');\n  var Dockerfile = path.join(fixture_path, 'Dockerfile');\n\n  var res_lines_dev = ['## DEVELOPMENT MODE',\n                       'ENV NODE_ENV=development',\n                       'CMD [\"pm2-dev\", \"index.js\", \"--env\", \"development\"]'];\n\n  var res_lines_prod = ['## DISTRIBUTION MODE',\n                        'ENV NODE_ENV=production',\n                        'COPY . /var/app',\n                        'CMD [\"pm2-docker\", \"index.js\", \"--env\", \"production\"]'];\n\n  after(function(done) {\n    fs.unlink(Dockerfile, done);\n  });\n\n  it('should generate a dockerfile', function() {\n    var has_meta = false;\n\n    return Containerizer.generateDockerfile(Dockerfile, 'index.js', {\n      mode : 'development'\n    })\n      .then(function(meta) {\n        meta.Dockerfile_path.should.eql(Dockerfile);\n        fs.statSync(Dockerfile);\n\n        var lines = meta.Dockerfile.split('\\n');\n        lines.forEach(function(line, i) {\n          if (line == '## DEVELOPMENT MODE')  {\n            has_meta = true;\n            should(lines[i]).eql(res_lines_dev[0]);\n            should(lines[i + 1]).eql(res_lines_dev[1]);\n            should(lines[i + 2]).eql(res_lines_dev[2]);\n          }\n        });\n\n        should(has_meta).be.true();\n      });\n  });\n\n  it('should switch dockerfile to distribution', function() {\n    return Containerizer.switchDockerFile(Dockerfile, 'index.js', {\n      mode : 'distribution'\n    })\n      .then(function(meta) {\n        meta.Dockerfile_path.should.eql(Dockerfile);\n        fs.statSync(Dockerfile);\n\n        var lines = meta.Dockerfile.split('\\n')\n        lines.forEach(function(line, i) {\n          if (line == '## DISTRIBUTION MODE')  {\n            should(lines[i]).eql(res_lines_prod[0]);\n            should(lines[i + 1]).eql(res_lines_prod[1]);\n            should(lines[i + 2]).eql(res_lines_prod[2]);\n            should(lines[i + 3]).eql(res_lines_prod[3]);\n          }\n        });\n      });\n  });\n\n  it('should switch dockerfile to distribution (no touching it)', function() {\n    return Containerizer.switchDockerFile(Dockerfile, 'index.js', {\n      mode : 'distribution'\n    })\n      .then(function(meta) {\n        meta.Dockerfile_path.should.eql(Dockerfile);\n        fs.statSync(Dockerfile);\n        var lines = meta.Dockerfile.split('\\n');\n        lines.forEach(function(line, i) {\n          if (line == '## DISTRIBUTION MODE')  {\n            should(lines[i]).eql(res_lines_prod[0]);\n            should(lines[i + 1]).eql(res_lines_prod[1]);\n            should(lines[i + 2]).eql(res_lines_prod[2]);\n            should(lines[i + 3]).eql(res_lines_prod[3]);\n          }\n        });\n      });\n  });\n\n  it('should switch dockerfile to development', function() {\n    return Containerizer.switchDockerFile(Dockerfile, 'index.js', {\n      mode : 'development'\n    })\n      .then(function(meta) {\n        meta.Dockerfile_path.should.eql(Dockerfile);\n        fs.statSync(Dockerfile);\n\n        var lines = meta.Dockerfile.split('\\n');\n        lines.forEach(function(line, i) {\n          if (line == '## DEVELOPMENT MODE')  {\n            should(lines[i]).eql(res_lines_dev[0]);\n            should(lines[i + 1]).eql(res_lines_dev[1]);\n            should(lines[i + 2]).eql(res_lines_dev[2]);\n          }\n        });\n      });\n  });\n\n  it('should switch dockerfile to development (no touching it)', function() {\n    return Containerizer.switchDockerFile(Dockerfile, 'index.js', {\n      mode : 'development'\n    })\n      .then(function(meta) {\n        meta.Dockerfile_path.should.eql(Dockerfile);\n        fs.statSync(Dockerfile);\n\n        var lines = meta.Dockerfile.split('\\n');\n        lines.forEach(function(line, i) {\n          if (line == '## DEVELOPMENT MODE')  {\n            should(lines[i]).eql(res_lines_dev[0]);\n            should(lines[i + 1]).eql(res_lines_dev[1]);\n            should(lines[i + 2]).eql(res_lines_dev[2]);\n          }\n        });\n      });\n  });\n\n  it('should switch dockerfile to distribution', function() {\n    return Containerizer.switchDockerFile(Dockerfile, 'index.js', {\n      mode : 'distribution'\n    })\n      .then(function(meta) {\n        meta.Dockerfile_path.should.eql(Dockerfile);\n        fs.statSync(Dockerfile);\n\n        var lines = meta.Dockerfile.split('\\n')\n        lines.forEach(function(line, i) {\n          if (line == '## DISTRIBUTION MODE')  {\n            should(lines[i]).eql(res_lines_prod[0]);\n            should(lines[i + 1]).eql(res_lines_prod[1]);\n            should(lines[i + 2]).eql(res_lines_prod[2]);\n            should(lines[i + 3]).eql(res_lines_prod[3]);\n          }\n        });\n      });\n  });\n\n});\n"
  },
  {
    "path": "test/programmatic/custom_action.mocha.js",
    "content": "\nprocess.chdir(__dirname);\n\nvar pm2 = require('../..');\nvar should = require('should');\n\ndescribe('Custom actions via CLI/API', function() {\n  before(function(done) {\n    pm2.delete('all', function() { done() });\n  });\n\n  after(function(done) {\n    pm2.delete('all', function() { done() });\n  });\n\n  it('should start custom action script', function(done) {\n    pm2.start('./../fixtures/custom_actions/index.js', function() {\n      setTimeout(done, 1200);\n    });\n  });\n\n  it('should trigger message by id', function(done) {\n    pm2.trigger(0, 'ping', function(err, ret) {\n      should(err).be.null();\n      should(ret.length).eql(1);\n      should(ret[0].data.return.pong).eql('hehe');\n      done();\n    });\n  });\n\n  it('should trigger message by name', function(done) {\n    pm2.trigger('index', 'ping', function(err, ret) {\n      should(err).be.null();\n      should(ret.length).eql(1);\n      should(ret[0].data.return.pong).eql('hehe');\n      done();\n    });\n  });\n\n  it('should handle unknown application', function(done) {\n    pm2.trigger('indexxo', 'ping', function(err, ret) {\n      should(err).not.be.null();\n      done();\n    });\n  });\n\n  it('should cannot trigger message if unknow id', function(done) {\n    pm2.trigger(10, 'ping', function(err, ret) {\n      should(err).not.be.null();\n      done();\n    });\n  });\n\n  it('should cannot trigger message if unknow action name', function(done) {\n    pm2.trigger(0, 'XXXXXXXXXx', function(err, ret) {\n      should(err).not.be.null();\n      done();\n    });\n  });\n\n  it('should delete all processes', function(done) {\n    pm2.delete('all', done);\n  });\n\n  it('should start app in cluster mode', function(done) {\n    pm2.start({\n      script: './../fixtures/custom_actions/index.js',\n      instances : '4'\n    }, function() {\n      setTimeout(done, 800);\n    });\n  });\n\n\n  it('should trigger message by id', function(done) {\n    pm2.trigger(0, 'ping', function(err, ret) {\n      should(err).be.null();\n      should(ret.length).eql(1);\n      should(ret[0].data.return.pong).eql('hehe');\n      done();\n    });\n  });\n\n  it('should trigger message by name', function(done) {\n    pm2.trigger('index', 'ping', function(err, ret) {\n      should(err).be.null();\n      should(ret.length).eql(4);\n      should(ret[0].data.return.pong).eql('hehe');\n      done();\n    });\n  });\n\n  it('should trigger message with params by name', function(done) {\n    pm2.trigger('index', 'param', 'shouldret', function(err, ret) {\n      should(err).be.null();\n      should(ret.length).eql(4);\n      should(ret[0].data.return).eql('shouldret');\n      should(ret[3].data.return).eql('shouldret');\n      done();\n    });\n  });\n});\n"
  },
  {
    "path": "test/programmatic/dump.mocha.js",
    "content": "\n//process.env.NODE_ENV ='test';\nvar PM2    = require('../..');\nvar should = require('should');\nvar path   = require('path');\n\ndescribe('PM2 programmatic calls', function() {\n  var proc1 = null;\n  var procs = [];\n  var bus   = null;\n\n  var pm2 = new PM2.custom({\n    cwd : __dirname + '/../fixtures'\n  });\n\n  after(function(done) {\n    pm2.delete('all', function(err, ret) {\n      // clean dump file\n      pm2.clearDump(function(err) {\n        pm2.kill(done);\n      });\n    });\n  });\n\n  before(function(done) {\n    pm2.connect(function() {\n      pm2.launchBus(function(err, _bus) {\n        bus = _bus;\n        pm2.delete('all', function(err, ret) {\n          done();\n        });\n      });\n    });\n  });\n\n  it('should start a script', function(done) {\n    pm2.start({\n      script : './child.js',\n      name : 'child',\n      instances : 1\n    }, function(err, data) {\n      proc1 = data[0];\n      should(err).be.null()\n      done();\n    });\n  });\n\n  it('should save/dump all processes', function(done) {\n    pm2.dump(function(err, ret) {\n      should(err).be.null()\n      done();\n    });\n  });\n\n\n  it('should delete processes', function(done) {\n    pm2.delete('all', function(err, ret) {\n      should(err).be.null()\n      pm2.list(function(err, ret) {\n        should(err).be.null()\n        ret.length.should.eql(0);\n        done();\n      });\n    });\n  });\n\n  it('should not save/dump if 0 processes', function(done) {\n    pm2.dump(function(err, ret) {\n      should(err).not.be.null()\n      done();\n    });\n  });\n\n  it('should save/dump if 0 processes AND --FORCE', function(done) {\n    pm2.dump(true, function(err, ret) {\n      should(err).be.null()\n      done();\n    });\n  });\n\n  it('should resurrect 0 processes', function(done) {\n    pm2.resurrect(function(err, ret) {\n      should(err).be.null()\n      pm2.list(function(err, ret) {\n        should(err).be.null()\n        ret.length.should.eql(0);\n        done();\n      });\n    });\n  });\n\n})\n"
  },
  {
    "path": "test/programmatic/env_switching.js",
    "content": "\n/**\n * PM2 programmatic API tests\n */\n\nvar PM2    = require('../..');\nvar should = require('should');\nvar path   = require('path');\n\n// Change to current folder\nprocess.chdir(__dirname);\n\nvar json_declaration_simple = {\n  script : './../fixtures/env-switching/child.js',\n  name   : 'child',\n  // Default environment\n  env : {\n    NODE_ENV : 'normal'\n  },\n  // Prod\n  env_production : {\n    NODE_ENV : 'production'\n  },\n  // Test\n  env_test : {\n    NODE_ENV : 'test'\n  }\n};\n\nvar json_declaration = {\n  script : './../fixtures/env-switching/child.js',\n  instances: '8',\n  // Default environment\n  env : {\n    NODE_ENV : 'normal'\n  },\n  // Prod\n  env_production : {\n    NODE_ENV : 'production'\n  },\n  // Test\n  env_test : {\n    NODE_ENV : 'test'\n  }\n};\n\ndescribe('PM2 programmatic calls', function() {\n\n  var proc1 = null;\n  var procs = [];\n  var bus   = null;\n\n  var pm2 = new PM2.custom({ });\n\n  after(function(done) {\n    pm2.kill(done);\n  });\n\n  before(function(done) {\n    pm2.connect(function() {\n      pm2.launchBus(function(err, _bus) {\n        bus = _bus;\n        pm2.delete('all', function(err, ret) {\n          done();\n        });\n      });\n    });\n  });\n\n  it('should start a script in production env and NODE_ENV have right value', function(done) {\n    pm2.start(json_declaration, {\n      env : 'production'\n    }, function(err, data) {\n      proc1 = data[0];\n      should(err).be.null();\n      proc1.pm2_env['NODE_ENV'].should.eql('production');\n      done();\n    });\n  });\n\n  it('should start a script in production env and NODE_ENV have right value', function(done) {\n    pm2.restart(json_declaration, {\n      env : 'production'\n    }, function(err, data) {\n      proc1 = data[0];\n      should(err).be.null();\n      proc1.pm2_env.env['NODE_ENV'].should.eql('production');\n      done();\n    });\n  });\n\n  it('should restarted process stay stable', function(done) {\n    setTimeout(function() {\n      pm2.list(function(err, ret) {\n        should(ret[0].pm2_env.restart_time).eql(1)\n        done();\n      });\n    }, 1000)\n  });\n\n  it('should delete all processes', function(done) {\n    pm2.delete('all', function(err, ret) {\n      done();\n    });\n  });\n\n  it('should start a script and NODE_ENV have right value', function(done) {\n    pm2.start(json_declaration, function(err, data) {\n      proc1 = data[0];\n      should(err).be.null();\n      proc1.pm2_env['NODE_ENV'].should.eql(json_declaration.env.NODE_ENV);\n      done();\n    });\n  });\n\n  it('should on restart keep previous NODE_ENV value', function(done) {\n    pm2.restart(json_declaration, {\n      env : 'test'\n    }, function(err, data) {\n      should(err).be.null();\n\n      data[0].pm2_env.env['NODE_ENV'].should.eql(json_declaration.env_test.NODE_ENV);\n      done();\n    });\n  });\n\n  it('should delete all processes', function(done) {\n    pm2.delete('all', function(err, ret) {\n      done();\n    });\n  });\n\n  // it('should start a script and NODE_ENV have right value', function(done) {\n  //   pm2.start(json_declaration_simple, function(err, data) {\n  //     proc1 = data[0];\n  //     should(err).be.null;\n  //     proc1.pm2_env['NODE_ENV'].should.eql(json_declaration.env.NODE_ENV);\n  //     done();\n  //   });\n  // });\n\n\n});\n"
  },
  {
    "path": "test/programmatic/exp_backoff_restart_delay.mocha.js",
    "content": "\nprocess.env.EXP_BACKOFF_RESET_TIMER = 500\nprocess.env.PM2_WORKER_INTERVAL = 100\n\nconst PM2 = require('../..');\nconst should = require('should');\nconst exec = require('child_process').exec\nconst path = require('path')\n\ndescribe('Exponential backoff feature', function() {\n  var pm2\n  var test_path = path.join(__dirname, 'fixtures', 'exp-backoff')\n\n  after(function(done) {\n    pm2.delete('all', function() {\n      pm2.kill(done);\n    })\n  });\n\n  before(function(done) {\n    pm2 = new PM2.custom({\n      cwd : test_path\n    });\n\n    pm2.delete('all', () => done())\n  })\n\n  it('should set exponential backoff restart', (done) => {\n    pm2.start({\n      script: path.join(test_path, 'throw-stable.js'),\n      exp_backoff_restart_delay: 100\n    }, (err, apps) => {\n      should(err).be.null()\n      should(apps[0].pm2_env.exp_backoff_restart_delay).eql(100)\n      done()\n    })\n  })\n\n  it('should have set the prev_restart delay', (done) => {\n    setTimeout(() => {\n      pm2.list((err, procs) => {\n        should(procs[0].pm2_env.prev_restart_delay).be.aboveOrEqual(100)\n        done()\n      })\n    }, 800)\n  })\n\n  it('should have incremented the prev_restart delay', (done) => {\n    setTimeout(() => {\n      pm2.list((err, procs) => {\n        should(procs[0].pm2_env.prev_restart_delay).be.above(100)\n        done()\n      })\n    }, 500)\n  })\n\n  it('should reset prev_restart_delay if application has reach stable uptime', (done) => {\n    setTimeout(() => {\n      pm2.list((err, procs) => {\n        should(procs[0].pm2_env.prev_restart_delay).be.eql(0)\n        done()\n      })\n    }, 3000)\n  })\n})\n"
  },
  {
    "path": "test/programmatic/filter_env.mocha.js",
    "content": "//#4596\n\nprocess.chdir(__dirname)\n\nprocess.env.SHOULD_NOT_BE_THERE = 'true'\n\nvar PM2 = require('../..')\nvar should = require('should')\n\ndescribe('API checks', function() {\n  before(function(done) {\n    PM2.delete('all', function() { done() })\n  })\n\n  after(function(done) {\n    PM2.kill(done)\n  })\n\n  afterEach(function(done) {\n    PM2.delete('all', done)\n  })\n\n  it('should start app and validate presence of env var', function(done) {\n    PM2.start({\n      script: './../fixtures/echo.js'\n    }, (err) => {\n      should(err).be.null()\n      PM2.list(function(err, list) {\n        should(err).be.null()\n        should(list.length).eql(1)\n        should.exists(list[0].pm2_env.SHOULD_NOT_BE_THERE)\n        done()\n      })\n    })\n  })\n\n  it('should start app with filtered env wth array of env to be ignored', function(done) {\n    PM2.start({\n      script: './../fixtures/echo.js',\n      filter_env: ['SHOULD_NOT_BE_THERE']\n    }, (err) => {\n      should(err).be.null()\n      PM2.list(function(err, list) {\n        should(err).be.null()\n        should(list.length).eql(1)\n        should.not.exists(list[0].pm2_env.SHOULD_NOT_BE_THERE)\n        done()\n      })\n    })\n  })\n\n  it('should start app with filtered env with string env name to be ignored', function(done) {\n    PM2.start({\n      script: './../fixtures/echo.js',\n      filter_env: 'SHOULD_NOT_BE_THERE'\n    }, (err) => {\n      should(err).be.null()\n      PM2.list(function(err, list) {\n        should(err).be.null()\n        should(list.length).eql(1)\n        should.not.exists(list[0].pm2_env.SHOULD_NOT_BE_THERE)\n        done()\n      })\n    })\n  })\n\n  it('should start app with filtered env at true to drop all local env', function(done) {\n    PM2.start({\n      script: './../fixtures/echo.js',\n      filter_env: true\n    }, (err) => {\n      should(err).be.null()\n      PM2.list(function(err, list) {\n        should(err).be.null()\n        should(list.length).eql(1)\n        should.not.exists(list[0].pm2_env.SHOULD_NOT_BE_THERE)\n        done()\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/programmatic/fixtures/auto-restart/throw.js",
    "content": "setTimeout(() => {\n  throw new Error('err')\n}, 50)\n"
  },
  {
    "path": "test/programmatic/fixtures/exp-backoff/throw-stable.js",
    "content": "\nif (parseInt(process.env.restart_time) === 5) {\n  setInterval(function() {\n    console.log('Im stable mamen')\n  }, 1000)\n}\nelse {\n  throw new Error('Ugly error')\n}\n"
  },
  {
    "path": "test/programmatic/fixtures/exp-backoff/throw.js",
    "content": "throw new Error('Ugly error')\n"
  },
  {
    "path": "test/programmatic/fixtures/instances/echo.js",
    "content": "\nsetInterval(function() {\n  console.log('OK')\n}, 1000)\n"
  },
  {
    "path": "test/programmatic/fixtures/instances/http.js",
    "content": "\nvar http = require('http');\n\nvar server = http.createServer(function(req, res) {\n  res.writeHead(200);\n  res.end('hey');\n}).listen(8008, function() {\n  console.log('App listening on port %d', server.address().port);\n});\n"
  },
  {
    "path": "test/programmatic/fixtures/json-env-passing/echo.js",
    "content": "\nsetInterval(function() {\n  console.log(process.env.JSONTEST);\n}, 50);\n"
  },
  {
    "path": "test/programmatic/fixtures/json-env-passing/ecosystem.config.js",
    "content": "module.exports = {\n  apps : [{\n    script: 'echo.js',\n    env: {\n      JSONTEST: { si: 'si' }\n    },\n    env_production: {\n      NODE_ENV: 'production'\n    }\n  }],\n\n  deploy : {\n    production : {\n      user : 'node',\n      host : '212.83.163.1',\n      ref  : 'origin/master',\n      repo : 'git@github.com:repo.git',\n      path : '/var/www/production',\n      'post-deploy' : 'npm install && pm2 reload ecosystem.config.js --env production'\n    }\n  }\n};\n"
  },
  {
    "path": "test/programmatic/fixtures/tar-module/mono-app-module/README.md",
    "content": "\nTo start http application in cluster mode:\n\n```bash\n$ pm2 start ecosystem.config.js\n# OR\n$ pm2 start http.js -i max\n```\n"
  },
  {
    "path": "test/programmatic/fixtures/tar-module/mono-app-module/ecosystem.config.js",
    "content": "module.exports = {\n  name: 'envision',\n  apps : [{\n    name   : 'clustered_http',\n    script : './http.js',\n    instances : 'max',\n    exec_mode : 'cluster',\n    env : {\n      PORT : 8002\n    }\n  }, {\n    name : 'forked_app',\n    script : './http.js',\n    env : {\n      PORT : 8001\n    }\n  }]\n}\n"
  },
  {
    "path": "test/programmatic/fixtures/tar-module/mono-app-module/http.js",
    "content": "\nvar http = require('http');\n\nvar server = http.createServer(function(req, res) {\n  res.writeHead(200);\n  res.end('hey');\n}).listen(process.env.PORT || 8000, function() {\n  console.log('App listening on port %d', server.address().port);\n});\n"
  },
  {
    "path": "test/programmatic/fixtures/tar-module/mono-app-module/package.json",
    "content": "{\n  \"name\": \"mono-app-module\",\n  \"version\": \"0.23.0\",\n  \"pm2\": [{\n    \"name\" : \"mono_app\",\n    \"script\": \"http.js\",\n    \"env\" : {\n      \"PORT\" : \"8008\"\n    }\n  }]\n}\n"
  },
  {
    "path": "test/programmatic/fixtures/tar-module/multi-app-module/README.md",
    "content": "\nTo start http application in cluster mode:\n\n```bash\n$ pm2 start ecosystem.config.js\n# OR\n$ pm2 start http.js -i max\n```\n"
  },
  {
    "path": "test/programmatic/fixtures/tar-module/multi-app-module/ecosystem.config.js",
    "content": "module.exports = {\n  name: 'envision',\n  apps : [{\n    name   : 'clustered_http',\n    script : './http.js',\n    instances : 'max',\n    exec_mode : 'cluster',\n    env : {\n      PORT : 8002\n    }\n  }, {\n    name : 'forked_app',\n    script : './http.js',\n    env : {\n      PORT : 8001\n    }\n  }]\n}\n"
  },
  {
    "path": "test/programmatic/fixtures/tar-module/multi-app-module/http.js",
    "content": "\nvar http = require('http');\n\nvar server = http.createServer(function(req, res) {\n  res.writeHead(200);\n  res.end('hey');\n}).listen(process.env.PORT || 8000, function() {\n  console.log('App listening on port %d', server.address().port);\n});\n"
  },
  {
    "path": "test/programmatic/fixtures/tar-module/multi-app-module/package.json",
    "content": "{\n  \"name\": \"multi-app-module\",\n  \"version\": \"0.1\",\n  \"pm2\": [{\n    \"name\" : \"first_app\",\n    \"script\": \"http.js\",\n    \"env\" : {\n      \"PORT\" : \"8005\"\n    }\n  }, {\n    \"name\" : \"second_app\",\n    \"script\" : \"./http.js\",\n    \"env\" : {\n      \"PORT\" : \"8002\"\n    }\n  }]\n}\n"
  },
  {
    "path": "test/programmatic/fixtures/version-test/index.js",
    "content": "\nsetInterval(function() {\n  console.log('HEY')\n}, 1000)\n"
  },
  {
    "path": "test/programmatic/fixtures/version-test/package.json",
    "content": "{\"name\":\"version-module\",\"version\":\"1.0.0\",\"description\":\"\",\"main\":\"index.js\",\"scripts\":{\"test\":\"echo \\\"Error: no test specified\\\" && exit 1\"},\"author\":\"\",\"license\":\"ISC\"}"
  },
  {
    "path": "test/programmatic/flagExt.mocha.js",
    "content": "\nvar should = require('should');\nvar f_e = require('../../lib/API/Modules/flagExt.js');\nvar fs = require('fs');\n\ndescribe('Flag -ext', function() {\n\n    var opts = {};\n    var res = [];\n    opts.ext = 'js,json';\n\n    it('should return not empty result', function() {\n        f_e.make_available_extension(opts, res);\n        should(res).be.not.empty();\n    });\n    it('should not crash', function() {\n        f_e.make_available_extension();\n        f_e.make_available_extension(res);\n        f_e.make_available_extension(opts);\n    });\n    it('should give different results', function() {\n        var tmp_res = [];\n        f_e.make_available_extension(opts, res);\n        opts.ext = 'sh,py';\n        f_e.make_available_extension(opts, tmp_res);\n        should(res).not.equal(tmp_res);\n    });\n    it('should not crash in case, when no access for file or directory by permissions', function() {\n        var dir = fs.mkdirSync(\"noAccessDir\", 0777);\n        opts.ext = 'txt'\n        var fileStream = fs.createWriteStream(\"noAccessDir/checkPermissions.txt\");\n        fileStream.write(\"It's a temporary file for testing flag --ext in PM2\");\n        fileStream.end();\n        fs.chmodSync('noAccessDir/checkPermissions.txt', 0000);\n        fs.chmodSync('noAccessDir', 0000);\n        f_e.make_available_extension(opts, []);\n        f_e.make_available_extension(opts, []);\n        fs.chmodSync('noAccessDir', 0777);\n        fs.chmodSync('noAccessDir/checkPermissions.txt', 0777);\n        fs.unlinkSync('noAccessDir/checkPermissions.txt');\n        fs.rmdirSync('noAccessDir/');\n    });\n});\n"
  },
  {
    "path": "test/programmatic/flush.mocha.js",
    "content": "process.env.NODE_ENV = 'test';\n\nvar PM2    = require('../..');\nvar should = require('should');\nvar fs     = require('fs');\nvar path   = require('path');\n\ndescribe('Programmatic flush feature test', function() {\n  var proc1 = null;\n  var procs = [];\n\n  var pm2 = new PM2.custom({\n    cwd : __dirname + '/../fixtures'\n  });\n\n  before(function(done) {\n    pm2.delete('all', function() {\n      done();\n    });\n  });\n\n  after(function(done) {\n    pm2.disconnect(done);\n  });\n\n  afterEach(function(done) {\n    pm2.delete('all', done);\n  });\n\n  describe('Flush test', function() {\n    it('flush all logs', function(done) {\n      pm2.start({\n        script: './echo.js',\n        error_file : 'error-echo.log',\n        out_file   : 'out-echo.log',\n        merge_logs: false\n      }, function(err, procs) {\n        should(err).be.null();\n        \n        var out_file = procs[0].pm2_env.pm_out_log_path;\n        var err_file = procs[0].pm2_env.pm_err_log_path;\n        out_file.should.containEql('out-echo-0.log');\n        err_file.should.containEql('error-echo-0.log');\n        pm2.flush(undefined, function(){\n          fs.readFileSync(out_file, \"utf8\").should.be.empty();\n          fs.readFileSync(err_file, \"utf8\").should.be.empty();\n          done();\n        });\n      });\n    });\n    it('flush only echo logs', function(done) {\n      pm2.start({\n        script: './echo.js',\n        error_file : 'error-echo.log',\n        out_file   : 'out-echo.log',\n        merge_logs: false\n      }, function(err, procs) {\n        should(err).be.null();\n        var out_file = procs[0].pm2_env.pm_out_log_path;\n        var err_file = procs[0].pm2_env.pm_err_log_path;\n        pm2.start({\n            script: './001-test.js',\n            error_file : 'error-001-test.log',\n            out_file   : 'out-001-test.log',\n            merge_logs: false\n          }, function(err, procs, $out_file, $err_file) {\n            should(err).be.null();\n            var out_file1 = procs[0].pm2_env.pm_out_log_path;\n            var err_file1 = procs[0].pm2_env.pm_err_log_path;\n            pm2.flush('echo', function(){\n              fs.readFileSync(out_file, \"utf8\").toString().should.be.empty();\n              fs.readFileSync(err_file, \"utf8\").toString().should.be.empty();\n              fs.readFileSync(out_file1, \"utf8\").toString().should.not.be.empty();\n              fs.readFileSync(err_file1, \"utf8\").toString().should.not.be.empty();\n              done();\n        });\n        });\n      });\n    });\n  });\n});"
  },
  {
    "path": "test/programmatic/god.mocha.js",
    "content": "\nvar PM2 = require('../..');\nvar God = require('../../lib/God');\nvar numCPUs = require('os').cpus().length;\nvar fs = require('fs');\nvar path = require('path');\nvar should = require('should');\nvar Common = require('../../lib/Common');\nvar eachLimit = require('async/eachLimit');\n\nvar cst = require('../../constants.js');\n\n// Change to current folder\nprocess.chdir(__dirname);\n\nvar pm2= new PM2.custom();\n\n/**\n * Description\n * @method getConf\n * @return AssignmentExpression\n */\nfunction getConf() {\n  var a = Common.prepareAppConf({ cwd : process.cwd() }, {\n    script : '../fixtures/echo.js',\n    name : 'echo',\n    instances : 2\n  });\n  return a;\n}\n\nfunction getConf2() {\n  return Common.prepareAppConf({ cwd : process.cwd() }, {\n    script : '../fixtures/echo2.js',\n    instances       : 4,\n    exec_mode       : 'cluster_mode',\n    name : 'child'\n  });\n}\n\nfunction getConf3() {\n  return Common.prepareAppConf({ cwd : process.cwd() }, {\n    script : '../fixtures/echo3.js',\n    instances       : 10,\n    exec_mode       : 'cluster_mode',\n    name : 'child'\n  });\n}\n\nfunction getConf4() {\n  return Common.prepareAppConf({ cwd : process.cwd() }, {\n    script : '../fixtures/args.js',\n    args            : ['-d', '-a'],\n    instances       : '1',\n    name : 'child'\n  });\n}\n\nfunction deleteAll(data, cb) {\n  var processes = God.getFormatedProcesses();\n\n  eachLimit(processes, cst.CONCURRENT_ACTIONS, function(proc, next) {\n    console.log('Deleting process %s', proc.pm2_env.pm_id);\n    God.deleteProcessId(proc.pm2_env.pm_id, function() {\n      return next();\n    });\n    return false;\n  }, function(err) {\n    if (err) return cb(God.logAndGenerateError(err), {});\n\n    God.clusters_db = null;\n    God.clusters_db = {};\n    return cb(null, []);\n  });\n}\n\n\ndescribe('God', function() {\n  before(function(done) {\n    pm2.connect(function() {\n      deleteAll({}, function(err, dt) {\n        done()\n      });\n    });\n  });\n\n  it('should have right properties', function() {\n    God.should.have.property('configuration');\n    God.should.have.property('prepare');\n    God.should.have.property('ping');\n    God.should.have.property('dumpProcessList');\n    God.should.have.property('getProcesses');\n    God.should.have.property('getMonitorData');\n    God.should.have.property('getFormatedProcesses');\n    God.should.have.property('checkProcess');\n    God.should.have.property('reloadLogs');\n    God.should.have.property('stopProcessId');\n    God.should.have.property('sendSignalToProcessId');\n    God.should.have.property('sendSignalToProcessName');\n  });\n\n  describe('One process', function() {\n    var proc, pid;\n\n    it('should fork one process', function(done) {\n      God.prepare(getConf(), function(err, procs) {\n        should(err).be.null();\n\t      procs[0].pm2_env.status.should.be.equal('online');\n        var a = God.getFormatedProcesses()\n\t      God.getFormatedProcesses().length.should.equal(2);\n\t      done();\n      });\n    });\n  });\n\n  describe('Process State Machine', function() {\n    var clu, pid;\n\n    before(function(done) {\n      deleteAll({}, function(err, dt) {\n        done();\n      });\n    });\n    it('should start a process', function(done) {\n      God.prepare(getConf(), function(err, procs) {\n        clu = procs[0];\n\n        pid = clu.pid;\n\t      procs[0].pm2_env.status.should.be.equal('online');\n\t      done();\n      });\n    });\n\n    it('should stop a process and keep in database on state stopped', function(done) {\n      God.stopProcessId(clu.pm2_env.pm_id, function(err, proc) {\n        proc.pm2_env.status.should.be.equal('stopped');\n        God.checkProcess(proc.pid).should.be.equal(false);\n        done();\n      });\n    });\n\n    it('should restart the same process and set it as state online and be up', function(done) {\n      God.restartProcessId({id:clu.pm2_env.pm_id}, function(err, dt) {\n        var proc = God.findProcessById(clu.pm2_env.pm_id);\n        proc.pm2_env.status.should.be.equal('online');\n        God.checkProcess(proc.process.pid).should.be.equal(true);\n        done();\n      });\n    });\n\n    it('should stop and delete a process id', function(done) {\n      var old_pid = clu.pid;\n      God.deleteProcessId(clu.pm2_env.pm_id, function(err, dt) {\n        var proc = God.findProcessById(clu.pm2_env.pm_id);\n        God.checkProcess(old_pid).should.be.equal(false);\n        God.getFormatedProcesses().length.should.be.equal(1);\n        done();\n      });\n    });\n\n  });\n\n\n  describe('Reload - cluster', function() {\n\n    before(function(done) {\n      deleteAll({}, function(err, dt) {\n        done();\n      });\n    });\n\n    it('should launch app', function(done) {\n      God.prepare(getConf2(), function(err, procs) {\n\t      var processes = God.getFormatedProcesses();\n\n        setTimeout(function() {\n          processes.length.should.equal(4);\n          processes.forEach(function(proc) {\n            proc.pm2_env.restart_time.should.eql(0);\n          });\n          done();\n        }, 100);\n      });\n    });\n\n  });\n\n  describe('Multi launching', function() {\n    before(function(done) {\n      deleteAll({}, function(err, dt) {\n        done();\n      });\n    });\n\n    afterEach(function(done) {\n      deleteAll({}, function(err, dt) {\n        done();\n      });\n    });\n\n    it('should launch multiple processes depending on CPUs available', function(done) {\n      God.prepare(Common.prepareAppConf({cwd : process.cwd() }, {\n        script : '../fixtures/echo.js',\n        name : 'child',\n        instances:3\n      }), function(err, procs) {\n\t      God.getFormatedProcesses().length.should.equal(3);\n        procs.length.should.equal(3);\n        done();\n      });\n    });\n\n    it('should start maximum processes depending on CPU numbers', function(done) {\n      God.prepare(getConf3(), function(err, procs) {\n\t      God.getFormatedProcesses().length.should.equal(10);\n        procs.length.should.equal(10);\n        done();\n      });\n    });\n\n    it('should dump process list', function(done) {\n      God.prepare(Common.prepareAppConf({cwd : process.cwd() }, {\n        script    : '../fixtures/echo.js',\n        name      : 'child',\n        instances : 3\n      }), function(err, procs) {\n        God.getFormatedProcesses().length.should.equal(3);\n        procs.length.should.equal(3);\n\n        God.dumpProcessList(function(err) {\n          should(err).be.null();\n          var apps = fs.readFileSync(cst.DUMP_FILE_PATH);\n          apps = JSON.parse(apps);\n          apps.length.should.equal(3);\n          done();\n        });\n      });\n    });\n\n    it('should handle arguments', function(done) {\n      God.prepare(getConf4(), function(err, procs) {\n        setTimeout(function() {\n          God.getFormatedProcesses()[0].pm2_env.restart_time.should.eql(0);\n          done();\n        }, 500);\n      });\n    });\n\n  });\n\n  it('should report pm2 version', function(done) {\n    God.getVersion({}, function(err, version) {\n      version.should.not.be.null();\n      done();\n    });\n  });\n\n  it('should get monitor data', function(done) {\n    var f = require('child_process').fork('../fixtures/echo.js')\n\n    var processes = [\n      // stopped status\n      {\n        pm2_env: {status: cst.STOPPED_STATUS}\n      },\n      // axm pid\n      {\n        pm2_env: {\n          status: cst.ONLINE_STATUS, axm_options: {pid: process.pid}\n        }\n      },\n      // axm pid is NaN\n      {\n        pm2_env: {\n          status: cst.ONLINE_STATUS, axm_options: {pid: 'notanumber'}\n        }\n      },\n      {\n        pm2_env: {\n          status: cst.ONLINE_STATUS\n        },\n        pid: f.pid\n      }\n    ]\n\n    // mock\n    var g = {\n      getFormatedProcesses: function() {\n        return processes\n      }\n    }\n\n    require('../../lib/God/ActionMethods.js')(g)\n\n    g.getMonitorData({}, function(err, procs) {\n      should(err).be.null();\n      procs.length.should.be.equal(processes.length);\n      procs[0].monit.should.be.deepEqual({memory: 0, cpu: 0});\n      procs[1].monit.memory.should.be.greaterThan(0);\n      procs[2].monit.should.be.deepEqual({memory: 0, cpu: 0});\n      procs[3].monit.memory.should.be.greaterThan(0);\n      f.kill()\n      done()\n    })\n  });\n});\n"
  },
  {
    "path": "test/programmatic/graceful.mocha.js",
    "content": "\nprocess.env.NODE_ENV = 'test';\n\nvar PM2    = require('../..');\nvar should = require('should');\nvar path   = require('path');\nvar Plan   = require('../helpers/plan.js');\nvar sexec = require('../../lib/tools/sexec.js')\n\nprocess.chdir(__dirname);\n\ndescribe('Wait ready / Graceful start / restart', function() {\n  this.retries(2)\n\n  var pm2 = new PM2.custom({\n    cwd : '../fixtures/listen-timeout/',\n  });\n\n  before(function(done) {\n    pm2.delete('all', function() {\n      done()\n    });\n  });\n\n  describe('(FORK) Listen timeout feature', function() {\n    this.timeout(10000);\n\n    after(function(done) {\n      pm2.delete('all', done);\n    });\n\n    it('should force script to set as ready after forced listen_timeout', function(done) {\n      pm2.start({\n        script         : './wait-ready.js',\n        listen_timeout : 1000,\n        wait_ready     : true,\n        name           : 'echo'\n      });\n\n      setTimeout(function() {\n        pm2.list(function(err, apps) {\n          should(apps[0].pm2_env.status).eql('launching');\n        });\n      }, 800);\n\n      setTimeout(function() {\n        pm2.list(function(err, apps) {\n          should(apps[0].pm2_env.status).eql('online');\n          done();\n        })\n      }, 1500);\n    });\n\n    it('should have listen timeout updated', function(done) {\n      pm2.list(function(err, list) {\n        should(list[0].pm2_env.wait_ready).eql(true);\n        done();\n      });\n    });\n\n    it('should take listen timeout into account', function(done) {\n      var called = false;\n      var plan = new Plan(4, done);\n\n      setTimeout(function() {\n        should(called).be.false();\n        plan.ok(true);\n      }, 300);\n\n      setTimeout(function() {\n        should(called).be.true();\n        plan.ok(true);\n\n        pm2.list((err, apps) => {\n          should(apps[0].pm2_env.wait_ready).eql(true)\n          plan.ok(true)\n        })\n      }, 1500);\n\n      pm2.reload('all', function(err, data) {\n        called = true;\n        plan.ok(true);\n      });\n    });\n\n    it('should restart script with different listen timeout', function(done) {\n      pm2.restart({\n        script    : './echo.js',\n        listen_timeout : 100,\n        instances : 1,\n        name      : 'echo'\n      }, done);\n    });\n\n    it('should have listen timeout updated', function(done) {\n      pm2.list(function(err, list) {\n        should(list[0].pm2_env.listen_timeout).eql(100);\n        should(list.length).eql(1);\n        done();\n      });\n    });\n\n    it('should be reloaded after 100ms', function(done) {\n      var called = false;\n\n      setTimeout(function() {\n        should(called).be.true();\n        done();\n      }, 500);\n\n      pm2.reload('all', function(err, data) {\n        called = true;\n      });\n    });\n  });\n\n\n  describe('(CLUSTER) Listen timeout feature', function() {\n    this.timeout(10000);\n\n    after(function(done) {\n      pm2.delete('all', done);\n    });\n\n    it('should force script to set as ready after forced listen_timeout', function(done) {\n      pm2.start({\n        script         : './wait-ready.js',\n        listen_timeout : 1000,\n        wait_ready     : true,\n        instances      : 1,\n        exec_mode: 'cluster',\n        name           : 'http'\n      });\n\n      setTimeout(function() {\n        pm2.list(function(err, apps) {\n          should(apps[0].pm2_env.status).eql('launching');\n        });\n      }, 500);\n\n      setTimeout(function() {\n        pm2.list(function(err, apps) {\n          should(apps[0].pm2_env.status).eql('online');\n          done();\n        })\n      }, 1500);\n    });\n\n    it('should take listen timeout into account', function(done) {\n      var called = false;\n      var plan = new Plan(4, done);\n\n      setTimeout(function() {\n        should(called).be.false();\n        plan.ok(true);\n      }, 500);\n\n      setTimeout(function() {\n        should(called).be.true();\n        plan.ok(true);\n\n        pm2.list((err, apps) => {\n          should(apps[0].pm2_env.wait_ready).eql(true)\n          plan.ok(true)\n        })\n      }, 1500);\n\n      pm2.reload('all', function(err, data) {\n        called = true;\n        plan.ok(true);\n      });\n    });\n\n  });\n\n  describe('(Cluster): Wait ready feature', function () {\n    this.timeout(10000);\n\n    after(function(done) {\n      pm2.delete('all', done);\n    });\n\n    it('Should send SIGINT right after ready and not wait for listen timeout', function(done) {\n      const plan = new Plan(2, done);\n\n      pm2.start({\n        script         : './wait-ready.js',\n        listen_timeout : 5000,\n        wait_ready     : true,\n        instances      : 1,\n        exec_mode      : 'cluster',\n        name           : 'echo'\n      }, (error, result) => {\n        if (error) {\n          return done(error);\n        }\n        const oldPid = result[0].process.pid;\n        plan.ok(typeof oldPid !== 'undefined');\n\n        pm2.reload('echo', {}, done);\n        setTimeout(function() {\n          sexec(`ps -eo pid | grep -w ${oldPid}`, (err, res) => {\n            plan.ok(err === 1);\n          })\n        }, 2000);\n      });\n    });\n  });\n\n});\n"
  },
  {
    "path": "test/programmatic/id.mocha.js",
    "content": "\nprocess.chdir(__dirname);\n\nvar PM2 = require('../..');\nvar should = require('should');\nvar assert = require('assert')\n\ndescribe('Unique ID verification', function() {\n  describe('when starting', function() {\n    var _id = null\n\n    before(function(done) {\n      PM2.delete('all', function() { done() });\n    });\n\n    after(function(done) {\n      PM2.delete('all', function() { done() });\n    });\n\n    it('should start a script', function(done) {\n      PM2.start('../fixtures/child.js', function(err) {\n        should(err).be.null();\n        PM2.list(function(err, list) {\n          should(err).be.null();\n          assert(list.length > 0)\n          assert(typeof list[0].pm2_env.unique_id === 'string')\n          _id = list[0].pm2_env.unique_id\n          done();\n        });\n      });\n    });\n\n    it('should stop app by id', function(done) {\n      PM2.stop(0, done);\n    });\n\n    it('should restart and not changed unique id', function(done) {\n      PM2.restart(0, (err) => {\n        should(err).be.null();\n        PM2.list(function(err, list) {\n          should(err).be.null();\n          assert(list.length > 0)\n          assert(typeof list[0].pm2_env.unique_id === 'string')\n          assert( list[0].pm2_env.unique_id === _id)\n          done();\n        });\n      });\n    });\n\n\n    it('should generate another unique id for new process', function(done) {\n      PM2.start('./../fixtures/child.js', { name: 'toto' }, function(err) {\n        assert(!err);\n        PM2.list(function(err, list) {\n          should(err).be.null();\n          assert(list.length === 2)\n          assert(typeof list[0].pm2_env.unique_id === 'string')\n          assert(typeof list[1].pm2_env.unique_id === 'string')\n          assert(list[0].pm2_env.unique_id !== typeof list[1].pm2_env.unique_id)\n          done();\n        });\n      });\n    });\n\n    it('should duplicate a process and have a new id', function(done) {\n      PM2.scale('child', 2, function(err) {\n        assert(!err);\n        PM2.list(function(err, list) {\n          should(err).be.null();\n          should(list.length).eql(3);\n          assert(typeof list[0].pm2_env.unique_id === 'string')\n          assert(typeof list[1].pm2_env.unique_id === 'string')\n          assert(typeof list[2].pm2_env.unique_id === 'string')\n          assert(list[0].pm2_env.unique_id !== typeof list[1].pm2_env.unique_id)\n          assert(list[1].pm2_env.unique_id !== typeof list[2].pm2_env.unique_id)\n          assert(list[0].pm2_env.unique_id !== typeof list[2].pm2_env.unique_id)\n          done();\n        });\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/programmatic/inside.mocha.js",
    "content": "\nvar PM2 = require('../..');\nvar should = require('should');\n\ndescribe('Call PM2 inside PM2', function() {\n  var pm2 = new PM2.custom({\n    cwd : __dirname + '/../fixtures/inside'\n  });\n\n  after(function(done) {\n    pm2.kill(function(){\n      done();\n    });\n  });\n\n  it('should start script that starts a script', function(done) {\n    pm2.start('start_inside.js', function(err) {\n      should(err).be.null();\n      setTimeout(done, 1500);\n    });\n  });\n\n  it('should list 2 processes and apps stabilized', function(done) {\n    pm2.list(function(err, list) {\n      should(err).be.null();\n      should(list.length).eql(2);\n      list.forEach(function(proc) {\n        should(proc.pm2_env.restart_time).eql(0);\n        should(proc.pm2_env.status).eql('online');\n      });\n      done();\n    });\n  });\n\n  it('should start script that restart script', function(done) {\n    pm2.start('restart_inside.js', function(err) {\n      should(err).be.null();\n      setTimeout(done, 1500);\n    });\n  });\n\n  it('should list 3 processes and apps stabilized', function(done) {\n    pm2.list(function(err, list) {\n      should(err).be.null();\n      should(list.length).eql(3);\n      list.forEach(function(proc) {\n        if (proc.name == 'echo') {\n          should(proc.pm2_env.restart_time).eql(1);\n          should(proc.pm2_env.status).eql('online');\n        }\n        else {\n          should(proc.pm2_env.restart_time).eql(0);\n          should(proc.pm2_env.status).eql('online');\n        }\n      });\n      done();\n    });\n  });\n\n  it('should start script that reload script', function(done) {\n    pm2.start('reload_inside.js', function(err) {\n      should(err).be.null();\n      setTimeout(done, 1500);\n    });\n  });\n\n  it('should list 4 processes and apps stabilized', function(done) {\n    pm2.list(function(err, list) {\n      should(err).be.null();\n      should(list.length).eql(4);\n      list.forEach(function(proc) {\n        if (proc.name == 'echo') {\n          should(proc.pm2_env.restart_time).eql(2);\n          should(proc.pm2_env.status).eql('online');\n        }\n        else {\n          should(proc.pm2_env.restart_time).eql(0);\n          should(proc.pm2_env.status).eql('online');\n        }\n      });\n      done();\n    });\n  });\n\n});\n"
  },
  {
    "path": "test/programmatic/instances.mocha.js",
    "content": "\nconst pm2 = require('../..');\nconst should = require('should');\nconst path = require('path')\nconst os = require('os')\n\ndescribe('PM2 instances max bound test', function() {\n  var test_path = path.join(__dirname, 'fixtures', 'instances')\n\n  after((done) => {\n    pm2.delete('all', () => { done() })\n  })\n\n  before((done) => {\n    pm2.uninstall('all', () => {\n      pm2.delete('all', () => { done() })\n    })\n  })\n\n  it('should start maximum number of instances in cluster mode', (done) => {\n    pm2.start({\n      script: path.join(test_path, 'http.js'),\n      instances: 'max'\n    }, function(err, apps) {\n      should(apps.length).eql(os.cpus().length)\n      should(apps[0].pm2_env.exec_mode).eql('cluster_mode')\n      if (apps.length > 1)\n        should(apps[1].pm2_env.exec_mode).eql('cluster_mode')\n      done()\n    })\n  })\n\n  it('should app be in stable mode', (done) => {\n    setTimeout(function() {\n      pm2.list(function(err, apps) {\n        should(apps[0].pm2_env.restart_time).eql(0)\n        if (apps.length > 1)\n          should(apps[1].pm2_env.restart_time).eql(0)\n        done()\n      })\n    }, 1000)\n  })\n\n  it('should delete all', (done) => {\n    pm2.delete('all', function() {\n      done()\n    })\n  })\n\n  it('should start maximum number of instances in cluster mode', (done) => {\n    pm2.start({\n      script: path.join(test_path, 'http.js'),\n      instances: 0\n    }, function(err, apps) {\n      should(apps.length).eql(os.cpus().length)\n      should(apps[0].pm2_env.exec_mode).eql('cluster_mode')\n      if (apps.length > 1)\n        should(apps[1].pm2_env.exec_mode).eql('cluster_mode')\n      done()\n    })\n  })\n\n  it('should delete all', (done) => {\n    pm2.delete('all', function() {\n      done()\n    })\n  })\n\n  it('should start 4 instances in cluster mode', (done) => {\n    pm2.start({\n      script: path.join(test_path, 'http.js'),\n      instances: 4\n    }, function(err, apps) {\n      should(apps.length).eql(4)\n      should(apps[0].pm2_env.exec_mode).eql('cluster_mode')\n      should(apps[1].pm2_env.exec_mode).eql('cluster_mode')\n      done()\n    })\n  })\n\n  it('should start maximum number of instances in fork mode', (done) => {\n    pm2.start({\n      script: path.join(test_path, 'echo.js'),\n      exec_mode: 'fork',\n      instances: 'max'\n    }, function(err, apps) {\n      should(apps.length).eql(os.cpus().length)\n      should(apps[0].pm2_env.exec_mode).eql('fork_mode')\n      if (apps.length > 1)\n        should(apps[1].pm2_env.exec_mode).eql('fork_mode')\n      done()\n    })\n  })\n\n  it('should app be in stable mode', (done) => {\n    setTimeout(function() {\n      pm2.list(function(err, apps) {\n        should(apps[0].pm2_env.restart_time).eql(0)\n        if (apps.length > 1)\n          should(apps[1].pm2_env.restart_time).eql(0)\n        done()\n      })\n    }, 1000)\n  })\n\n})\n"
  },
  {
    "path": "test/programmatic/internal_config.mocha.js",
    "content": "\nconst PM2 = require('../..');\nconst should = require('should');\n\ndescribe('Internal PM2 configuration', function() {\n  var pm2\n\n  before(function() {\n    pm2 = new PM2.custom();\n  })\n\n  it('should set pm2:registry', function(done) {\n    pm2.set('pm2:registry', 'http://thing.com', done)\n  })\n\n  it('should new instance have the configuration', function() {\n    var pm3 = new PM2.custom();\n\n    pm3.connect(() => {\n      should(pm2.user_conf.registry).eql('http://thing.com')\n    })\n  })\n})\n"
  },
  {
    "path": "test/programmatic/issues/json_env_passing_4080.mocha.js",
    "content": "var PM2    = require('../../..');\nvar should = require('should');\n\ndescribe('Programmatic log feature test', function() {\n  var proc1 = null;\n  var procs = [];\n\n  var pm2 = new PM2.custom({\n    cwd : __dirname + '/../fixtures/json-env-passing'\n  });\n\n  before(function(done) {\n    pm2.delete('all', function() {\n      done();\n    });\n  });\n\n  after(function(done) {\n    pm2.delete('all', function() {\n      pm2.disconnect(done);\n    });\n  });\n\n  it('should start a process with object as environment variable', function(done) {\n    pm2.start({\n      script: 'echo.js',\n      env: {\n        NORMAL: 'STR',\n        JSONTEST: { si: 'si' }\n      },\n      env_production: {\n        NODE_ENV: 'production'\n      }\n    }, function(err, procs) {\n      should(err).be.null()\n      should(procs.length).eql(1)\n      done()\n    })\n  })\n\n  it('should retrieve environment variable stringified', function(done) {\n    pm2.list((err, procs) => {\n      should(procs[0].pm2_env.JSONTEST).eql('{\"si\":\"si\"}')\n      should(procs[0].pm2_env.NORMAL).eql('STR')\n      done()\n    })\n  })\n})\n"
  },
  {
    "path": "test/programmatic/json_validation.mocha.js",
    "content": "\nvar should = require('should');\nvar Config = require('../../lib/tools/Config');\nvar Schema = require('../../lib/API/schema.json');\n\n// Change to current folder\nprocess.chdir(__dirname);\n\ndescribe('JSON validation tests', function() {\n  it('should fail when passing wrong json', function() {\n    var ret = Config.validateJSON({\n      \"exec_interpreter\"   : \"node\",\n      \"exec_mode\"          : \"clusasdter_mode\",\n      \"instances\"          : \"max\",\n      \"log_date_format\"    : \"YYYY-MM-DD HH:mm Z\",\n      \"max_memory_restart\" : \"160\",\n      \"merge_logs\"         : true,\n      \"name\"               : \"hapi_playground\",\n      \"script\"             : \"chidld.js\",\n      \"cwd\"                : \"examadsples\",\n      \"node_args\"          : \"--harmoasdny\",\n      \"ignore_watch\"        : [\"[\\\\/\\\\\\\\]\\\\./\", \"log\"],\n      \"watch\"              : \"true\"\n    });\n\n    /**\n     * Error about instances to not be an integer\n     * Error about watch to not be a boolean\n     */\n    ret.errors.length.should.eql(2);\n  });\n\n  it('should succeed while passing right json', function() {\n    var ret = Config.validateJSON({\n      \"exec_interpreter\"   : \"node\",\n      \"exec_mode\"          : \"cluster_mode\",\n      \"instances\"          : 0,\n      \"log_date_format\"    : \"YYYY-MM-DD HH:mm Z\",\n      \"max_memory_restart\" : \"160\",\n      \"merge_logs\"         : true,\n      \"error_file\"         : \"err.file\",\n      \"out_file\"           : \"out.file\",\n      \"pid_file\"           : \"pid.file\",\n      \"log_file\"           : \"my-merged-log-file.log\",\n      \"name\"               : \"hapi_playground\",\n      \"script\"             : \"child.js\",\n      \"cwd\"                : \"examples\",\n      \"node_args\"          : \"--harmony\",\n      \"max_memory_restart\" : \"10M\",\n      \"ignore_watch\"        : [\"[\\\\/\\\\\\\\]\\\\./\", \"log\"],\n      \"watch\"              : true,\n      \"node_args\"          : [\"hey\",\"hay\"],\n      \"env\"                : {}\n    });\n\n    ret.errors.length.should.eql(0);\n  });\n\n  it('should set default values if some attributes not defined', function(done) {\n    var default_values = Object.keys(Schema).filter(function(attr) {\n      if (Schema[attr].default) return Schema[attr].default;\n      return false;\n    });\n\n    var ret = Config.validateJSON({\n      script : 'test.js',\n      name   : 'toto'\n    });\n\n    // Returned array should contain also default values\n    Object.keys(ret.config).should.containDeep(default_values);\n    done();\n  });\n\n});\n"
  },
  {
    "path": "test/programmatic/lazy_api.mocha.js",
    "content": "\nprocess.chdir(__dirname);\n\nprocess.env.PM2_RELOAD_LOCK_TIMEOUT = 1000;\n\nvar PM2 = require('../..');\n\nvar should = require('should');\n\ndescribe('Lazy API usage', function() {\n  it('should start a script without passing any args', function(done) {\n    PM2.start('./../fixtures/child.js', done);\n  });\n\n  it('should list one process', function(done) {\n    PM2.list(function(err, procs) {\n      procs.length.should.eql(1);\n      done();\n    });\n  });\n\n  it('should fail to start script', function(done) {\n    PM2.start('./../fixtures/child.js', function(err) {\n      should.exists(err);\n      done();\n    });\n  });\n\n  it('should list one process', function(done) {\n    PM2.list(function(err, procs) {\n      procs.length.should.eql(1);\n      done();\n    });\n  });\n\n\n  it('should reload', function(done) {\n    PM2.reload('child', function(err) {\n      should.not.exists(err);\n      done();\n    });\n  });\n\n  it('should process been restarted', function(done) {\n    PM2.list(function(err, procs) {\n      procs.length.should.eql(1);\n      procs[0].pm2_env.restart_time.should.eql(1);\n      done();\n    });\n  });\n\n  it('should restart', function(done) {\n    PM2.restart('./../fixtures/child.js');\n    setTimeout(function() {\n      done();\n    }, 300);\n  });\n\n  it('should process been restarted', function(done) {\n    PM2.list(function(err, procs) {\n      procs.length.should.eql(1);\n      procs[0].pm2_env.restart_time.should.eql(2);\n      done();\n    });\n  });\n\n  it('should stop', function(done) {\n    PM2.stop('./../fixtures/child.js');\n    setTimeout(function() {\n      done();\n    }, 300);\n  });\n\n  it('should process been stopped', function(done) {\n    PM2.list(function(err, procs) {\n      procs.length.should.eql(1);\n      procs[0].pm2_env.status.should.eql('stopped');\n      done();\n    });\n  });\n\n  it('should delete', function(done) {\n    PM2.delete('./../fixtures/child.js');\n    setTimeout(function() {\n      done();\n    }, 300);\n  });\n\n  it('should list 0 procs', function(done) {\n    PM2.list(function(err, procs) {\n      procs.length.should.eql(0);\n      done();\n    });\n  });\n});\n"
  },
  {
    "path": "test/programmatic/logs.js",
    "content": "\nprocess.env.NODE_ENV = 'test';\n\nvar PM2    = require('../..');\nvar should = require('should');\nvar fs     = require('fs');\nvar path   = require('path');\n\ndescribe('Programmatic log feature test', function() {\n  var proc1 = null;\n  var procs = [];\n\n  var pm2 = new PM2.custom({\n    cwd : __dirname + '/../fixtures'\n  });\n\n  before(function(done) {\n    pm2.delete('all', function() {\n      done();\n    });\n  });\n\n  after(function(done) {\n    pm2.disconnect(done);\n  });\n\n  afterEach(function(done) {\n    pm2.delete('all', done);\n  });\n\n  describe('Log merging', function() {\n    it('should process HAS post fixed logs with id (merge_logs: false)', function(done) {\n      pm2.start({\n        script: './echo.js',\n        error_file : 'error-echo.log',\n        out_file   : 'out-echo.log',\n        merge_logs: false,\n        name: 'toto'\n      }, function(err, procs) {\n        should(err).be.null();\n        var out_file = procs[0].pm2_env.pm_out_log_path;\n        var err_file = procs[0].pm2_env.pm_err_log_path;\n\n        out_file.should.containEql('out-echo-0.log');\n        err_file.should.containEql('error-echo-0.log');\n\n        setTimeout(function() {\n          fs.readFileSync(out_file).toString().should.containEql('echo.js');\n          fs.readFileSync(err_file).toString().should.containEql('echo.js-error');\n          done();\n        }, 500);\n      });\n    });\n\n    it('should process HAS NOT post fixed logs with id (merge_logs: true)', function(done) {\n      pm2.start({\n        script: './echo.js',\n        error_file : 'error-echo.log',\n        out_file   : 'out-echo.log',\n        merge_logs : true\n      }, function(err, procs) {\n        should(err).be.null();\n\n        var out_file = procs[0].pm2_env.pm_out_log_path;\n        var err_file = procs[0].pm2_env.pm_err_log_path;\n\n        out_file.should.containEql('out-echo.log');\n        err_file.should.containEql('error-echo.log');\n\n        setTimeout(function() {\n          fs.readFileSync(out_file).toString().should.containEql('echo.js');\n          fs.readFileSync(err_file).toString().should.containEql('echo.js-error');\n          done();\n        }, 500);\n      });\n    });\n\n    it('should process HAS NOT post fixed logs with id and MERGED FILE (merge_logs: true)', function(done) {\n      pm2.start({\n        script: './echo.js',\n        error_file : 'error-echo.log',\n        out_file   : 'out-echo.log',\n        log_file   : 'merged.log',\n        merge_logs : true\n      }, function(err, procs) {\n        should(err).be.null();\n\n        var out_file = procs[0].pm2_env.pm_out_log_path;\n        var err_file = procs[0].pm2_env.pm_err_log_path;\n        var log_file = procs[0].pm2_env.pm_log_path;\n\n        out_file.should.containEql('out-echo.log');\n        err_file.should.containEql('error-echo.log');\n        log_file.should.containEql('merged.log');\n\n        setTimeout(function() {\n          fs.readFileSync(out_file).toString().should.containEql('echo.js');\n          fs.readFileSync(err_file).toString().should.containEql('echo.js-error');\n          fs.readFileSync(log_file).toString().should.containEql('echo.js-error');\n          fs.readFileSync(log_file).toString().should.containEql('echo.js');\n          done();\n        }, 500);\n      });\n    });\n  });\n\n  describe('Log timestamp', function() {\n    it('should every file be timestamped', function(done) {\n      pm2.start({\n        script          : './echo.js',\n        error_file      : 'error-echo.log',\n        out_file        : 'out-echo.log',\n        log_file        : 'merged.log',\n        merge_logs      : true,\n        log_date_format : 'YYYY-MM-DD HH:mm Z'\n      }, function(err, procs) {\n        should(err).be.null();\n\n        var out_file = procs[0].pm2_env.pm_out_log_path;\n        var err_file = procs[0].pm2_env.pm_err_log_path;\n        var log_file = procs[0].pm2_env.pm_log_path;\n\n        out_file.should.containEql('out-echo.log');\n        err_file.should.containEql('error-echo.log');\n        log_file.should.containEql('merged.log');\n\n        setTimeout(function() {\n          fs.readFileSync(out_file).toString().should.containEql('20');\n          fs.readFileSync(err_file).toString().should.containEql('20');\n          fs.readFileSync(log_file).toString().should.containEql('20');\n          done();\n        }, 500);\n      });\n    });\n  });\n\n  describe('Disable logs', function() {\n    it('should start app with log disabled', function(done) {\n      try {\n        fs.writeFileSync(path.resolve('./test/fixtures/error-echo-disabled.log'), '');\n        fs.writeFileSync(path.resolve('./test/fixtures/out-echo-disabled.log'), '');\n        fs.writeFileSync(path.resolve('./test/fixtures/merged.log'), '');\n      } catch(e) {}\n\n      pm2.start({\n        script          : './echo.js',\n        error_file      : 'error-echo-disabled.log',\n        out_file        : 'out-echo-disabled.log',\n        log_file        : 'merged.log',\n        disable_logs    : true,\n        merge_logs      : true\n      }, function(err, procs) {\n        should(err).be.null();\n\n        var out_file = procs[0].pm2_env.pm_out_log_path;\n        var err_file = procs[0].pm2_env.pm_err_log_path;\n        var log_file = procs[0].pm2_env.pm_log_path;\n\n        setTimeout(function() {\n          fs.readFileSync(err_file).toString().should.eql('');\n          fs.readFileSync(out_file).toString().should.eql('');\n          done();\n        }, 500);\n      });\n    });\n\n    it('should start app with log disabled', function(done) {\n      try {\n        fs.writeFileSync(path.resolve('./test/fixtures/error-echo-disabled.log'), '');\n        fs.writeFileSync(path.resolve('./test/fixtures/out-echo-disabled.log'), '');\n        fs.writeFileSync(path.resolve('./test/fixtures/merged.log'), '');\n      } catch(e) {\n      }\n\n      pm2.start({\n        script          : './echo.js',\n        error_file      : '/dev/null',\n        out_file        : '/dev/null',\n        log_file        : '/dev/null',\n        disable_logs    : true,\n        merge_logs      : true\n      }, function(err, procs) {\n        should(err).be.null();\n\n        var out_file = procs[0].pm2_env.pm_out_log_path;\n        var err_file = procs[0].pm2_env.pm_err_log_path;\n\n        setTimeout(function() {\n          fs.readFileSync(err_file).toString().should.eql('');\n          fs.readFileSync(out_file).toString().should.eql('');\n          done();\n        }, 500);\n      });\n    });\n  });\n\n  describe('Logs set to /dev/null', function() {\n    it('should not write to logs', function(done) {\n      pm2.start({\n        script: './echo.js',\n        error_file : '/dev/null',\n        out_file   : '/dev/null'\n      }, function(err, procs) {\n        should(err).be.null();\n\n        var out_file = procs[0].pm2_env.pm_out_log_path;\n        var err_file = procs[0].pm2_env.pm_err_log_path;\n\n        out_file.should.containEql('/dev/null');\n        err_file.should.containEql('/dev/null');\n\n        setTimeout(function() {\n          fs.readFileSync(out_file).toString().should.containEql('');\n          fs.readFileSync(err_file).toString().should.containEql('');\n          done();\n        }, 500);\n      });\n    });\n\n    it('should write to log_file and not error_file or out_file', function(done) {\n      pm2.start({\n        script: './echo.js',\n        error_file : '/dev/null',\n        out_file   : '/dev/null',\n        log_file   : 'merged.log',\n        merge_logs : true\n      }, function(err, procs) {\n        should(err).be.null();\n\n        var out_file = procs[0].pm2_env.pm_out_log_path;\n        var err_file = procs[0].pm2_env.pm_err_log_path;\n        var log_file = procs[0].pm2_env.pm_log_path;\n\n        out_file.should.containEql('/dev/null');\n        err_file.should.containEql('/dev/null');\n        log_file.should.containEql('merged.log');\n\n        setTimeout(function() {\n          fs.readFileSync(out_file).toString().should.containEql('');\n          fs.readFileSync(err_file).toString().should.containEql('');\n          fs.readFileSync(log_file).toString().should.containEql('echo.js-error');\n          fs.readFileSync(log_file).toString().should.containEql('echo.js');\n          done();\n        }, 500);\n      });\n    });\n\n    it('should write to log_file and error_file but not out_file', function(done) {\n      pm2.start({\n        script: './echo.js',\n        error_file : 'error-echo.log',\n        out_file   : '/dev/null',\n        log_file   : 'merged.log',\n        merge_logs : true\n      }, function(err, procs) {\n        should(err).be.null();\n\n        var out_file = procs[0].pm2_env.pm_out_log_path;\n        var err_file = procs[0].pm2_env.pm_err_log_path;\n        var log_file = procs[0].pm2_env.pm_log_path;\n\n        out_file.should.containEql('/dev/null');\n        err_file.should.containEql('error-echo.log');\n        log_file.should.containEql('merged.log');\n\n        setTimeout(function() {\n          fs.readFileSync(out_file).toString().should.containEql('');\n          fs.readFileSync(err_file).toString().should.containEql('echo.js-error');\n          fs.readFileSync(log_file).toString().should.containEql('echo.js-error');\n          fs.readFileSync(log_file).toString().should.containEql('echo.js');\n          done();\n        }, 500);\n      });\n    });\n  });\n\n});\n"
  },
  {
    "path": "test/programmatic/max_memory_limit.js",
    "content": "\nprocess.env.NODE_ENV = 'test';\nprocess.env.PM2_WORKER_INTERVAL = 1000;\n\nvar PM2    = require('../..');\nvar should = require('should');\nvar path   = require('path');\n\n// Change to current folder\n\ndescribe('Max memory restart programmatic', function() {\n  var proc1 = null;\n  var procs = [];\n  var pm2 = new PM2.custom({\n    cwd : __dirname + '/../fixtures/json-reload/'\n  });\n\n  after(function(done) {\n    pm2.kill(done)\n  });\n\n  afterEach(function(done) {\n    pm2.delete('all', function() {\n      setTimeout(done, 300);\n    });\n  });\n\n  before(function(done) {\n    pm2.connect(function() {\n      done();\n    });\n  });\n\n  describe('Max memory limit', function() {\n    it('should restart process based on memory limit (UGLY WAY)', function(done) {\n      pm2.start('./big-array.js', {\n        maxMemoryRestart : '10M'\n      }, function(err, data) {\n        should(err).be.null();\n\n        setTimeout(function() {\n          pm2.list(function(err, ret) {\n            should(err).be.null();\n            ret[0].pm2_env.restart_time.should.not.eql(0);\n            done();\n          });\n        }, 3000);\n      });\n    });\n\n    it('should restart process based on memory limit (JSON WAY)', function(done) {\n      pm2.start({\n        script : './big-array.js',\n        max_memory_restart : '10M'\n      }, function(err, data) {\n        should(err).be.null();\n\n        setTimeout(function() {\n          pm2.list(function(err, ret) {\n            should(err).be.null();\n            ret[0].pm2_env.restart_time.should.not.eql(0);\n            done();\n          });\n        }, 3000);\n      });\n    });\n\n    it('should restart CLUSTER process based on memory limit (JSON WAY)', function(done) {\n      pm2.start({\n        script : './../big-array-listen.js',\n        max_memory_restart : '10M',\n        exec_mode : 'cluster'\n      }, function(err, data) {\n        should(err).be.null();\n\n        setTimeout(function() {\n          pm2.list(function(err, ret) {\n            should(err).be.null();\n            ret[0].pm2_env.restart_time.should.not.eql(0);\n            done();\n          });\n        }, 3000);\n      });\n    });\n\n\n  });\n\n});\n"
  },
  {
    "path": "test/programmatic/misc_commands.js",
    "content": "\n\nvar PM2    = require('../..');\nvar should = require('should');\nvar path   = require('path');\nvar fs     = require('fs');\n\nvar cst = require('../../constants.js');\n\ndescribe('Misc commands', function() {\n  var pm2 = new PM2.custom({\n    cwd : __dirname + '/../fixtures'\n  });\n\n  after(function(done) {\n    pm2.kill(done);\n  });\n\n  before(function(done) {\n    pm2.connect(function() {\n      pm2.delete('all', function() {\n        done();\n      });\n    });\n  });\n\n  it('should start 4 processes', function(done) {\n    pm2.start({\n      script    : './echo.js',\n      instances : 4,\n      name      : 'echo'\n    }, function(err, data) {\n      should(err).be.null();\n      done();\n    });\n  });\n\n  it('should restart them', function(done) {\n    pm2.restart('all', function(err, data) {\n      should(err).be.null();\n\n      pm2.list(function(err, procs) {\n        should(err).be.null();\n        procs.length.should.eql(4);\n        procs.forEach(function(proc) {\n          proc.pm2_env.restart_time.should.eql(1);\n        });\n        done();\n      });\n    });\n  });\n\n  it('should fail when trying to reset metadatas of unknown process', function(done) {\n    pm2.reset('allasd', function(err, data) {\n      should(err).not.be.null();\n      done();\n    });\n  });\n\n  it('should reset their metadatas', function(done) {\n    pm2.reset('all', function(err, data) {\n      should(err).be.null();\n\n      pm2.list(function(err, procs) {\n        should(err).be.null();\n        procs.length.should.eql(4);\n        procs.forEach(function(proc) {\n          proc.pm2_env.restart_time.should.eql(0);\n        });\n        done();\n      });\n    });\n  });\n\n  it('should save process list to dump', function(done) {\n    if (fs.existsSync(cst.DUMP_FILE_PATH)) {\n      fs.unlinkSync(cst.DUMP_FILE_PATH);\n    }\n\n    if (fs.existsSync(cst.DUMP_BACKUP_FILE_PATH)) {\n      fs.unlinkSync(cst.DUMP_BACKUP_FILE_PATH);\n    }\n\n    pm2.dump(function(err, data) {\n      should(fs.existsSync(cst.DUMP_FILE_PATH)).be.true();\n      should(fs.existsSync(cst.DUMP_BACKUP_FILE_PATH)).be.false();\n      should(err).be.null();\n      done();\n    });\n  });\n\n  it('should back up dump and re-save process list', function(done) {\n    var origDump = fs.readFileSync(cst.DUMP_FILE_PATH).toString();\n\n    pm2.dump(function(err, data) {\n      should(fs.existsSync(cst.DUMP_FILE_PATH)).be.true();\n      should(fs.existsSync(cst.DUMP_BACKUP_FILE_PATH)).be.true();\n      should(err).be.null();\n\n      var dumpBackup = fs.readFileSync(cst.DUMP_BACKUP_FILE_PATH).toString();\n\n      should(origDump).be.equal(dumpBackup);\n      done();\n    });\n  });\n\n  it('should delete child processes', function(done) {\n    pm2.delete('echo', function(err, data) {\n      should(err).be.null();\n\n      pm2.list(function(err, procs) {\n        should(err).be.null();\n        procs.length.should.eql(0);\n        done();\n      });\n    });\n  });\n\n  it('should resurrect previous processes from dump', function(done) {\n    pm2.resurrect(function(err, data) {\n      should(err).be.null();\n\n      pm2.list(function(err, procs) {\n        should(err).be.null();\n        procs.length.should.eql(4);\n        done();\n      });\n    });\n  });\n\n  it('should resurrect previous processes from backup if dump is broken', function(done) {\n    fs.writeFileSync(cst.DUMP_FILE_PATH, '[{');\n\n    pm2.resurrect(function(err, data) {\n      should(err).be.null();\n\n      pm2.list(function(err, procs) {\n        should(err).be.null();\n        procs.length.should.eql(4);\n        done();\n      });\n    });\n  });\n\n  it('should delete broken dump', function() {\n    should(fs.existsSync(cst.DUMP_FILE_PATH)).be.false();\n  });\n\n  it('should resurrect previous processes from backup if dump is missing', function(done) {\n    if (fs.existsSync(cst.DUMP_FILE_PATH)) {\n      fs.unlinkSync(cst.DUMP_FILE_PATH);\n    }\n\n    pm2.resurrect(function(err, data) {\n      should(err).be.null();\n\n      pm2.list(function(err, procs) {\n        should(err).be.null();\n        procs.length.should.eql(4);\n        done();\n      });\n    });\n  });\n\n  it('should resurrect no processes if dump and backup are broken', function() {\n    fs.writeFileSync(cst.DUMP_FILE_PATH, '[{');\n    fs.writeFileSync(cst.DUMP_BACKUP_FILE_PATH, '[{');\n\n    should(pm2.resurrect()).be.false();\n  });\n\n  it('should delete broken dump and backup', function() {\n    should(fs.existsSync(cst.DUMP_FILE_PATH)).be.false();\n    should(fs.existsSync(cst.DUMP_BACKUP_FILE_PATH)).be.false();\n  });\n\n  it('should resurrect no processes if dump and backup are missing', function() {\n    if (fs.existsSync(cst.DUMP_FILE_PATH)) {\n      fs.unlinkSync(cst.DUMP_FILE_PATH);\n    }\n\n    if (fs.existsSync(cst.DUMP_BACKUP_FILE_PATH)) {\n      fs.unlinkSync(cst.DUMP_BACKUP_FILE_PATH);\n    }\n\n    should(pm2.resurrect()).be.false();\n  });\n\n});\n"
  },
  {
    "path": "test/programmatic/module_configuration.mocha.js",
    "content": "\nprocess.env.NODE_ENV = 'test';\n\nvar PM2    = require('../..');\nvar should = require('should');\nvar path   = require('path');\nvar Plan   = require('../helpers/plan.js');\nvar fs = require('fs');\n\nprocess.chdir(__dirname);\n\ndescribe('Module default flush configuration', function() {\n  this.timeout(30000);\n\n  before(function(done) {\n    PM2.unset('pm2-logrotate', done);\n  });\n\n  it('should install a module', function(done) {\n    PM2.install('pm2-logrotate', function() {\n      setTimeout(done, 1000);\n    });\n  });\n\n  it('should module configuration have module options', function(done) {\n    var conf = require(process.env.HOME + '/.pm2/module_conf.json');\n    should(conf['pm2-logrotate'].max_size).eql('10M');\n    should(conf['pm2-logrotate'].retain).eql('all');\n    should(conf['pm2-logrotate'].rotateModule).eql(true);\n    done();\n  });\n\n  it('should change configuration', function(done) {\n    PM2.set('pm2-logrotate.max_size', '20M', done);\n  });\n\n  it('should have right value', function() {\n    var conf = JSON.parse(fs.readFileSync(process.env.HOME + '/.pm2/module_conf.json'));\n    should(conf['pm2-logrotate'].max_size).eql('20M');\n  });\n\n  it('should re install a module and not override previous set value', function() {\n    var conf = JSON.parse(fs.readFileSync(process.env.HOME + '/.pm2/module_conf.json'));\n    should(conf['pm2-logrotate'].max_size).eql('20M');\n  });\n\n  it('should uninstall module', function(done) {\n    PM2.uninstall('pm2-logrotate', done);\n  });\n\n\n});\n"
  },
  {
    "path": "test/programmatic/module_tar.mocha.js",
    "content": "\nconst PM2 = require('../..');\nconst should = require('should');\nconst exec = require('child_process').exec\nconst path = require('path')\nconst fs = require('fs')\n\ndescribe('Modules programmatic testing', function() {\n  var pm2;\n\n  var MODULE_FOLDER_MONO = path.join(__dirname, './fixtures/tar-module/mono-app-module')\n  var MODULE_FOLDER_MULTI = path.join(__dirname, './fixtures/tar-module/multi-app-module')\n\n  var PACKAGE_MONO = path.join(process.cwd(), 'mono-app-module-v0-23-0.tar.gz')\n  var PACKAGE_MULTI = path.join(process.cwd(), 'multi-app-module-v0-1.tar.gz')\n\n  after(function(done) {\n    pm2.kill(done);\n  });\n\n  before(function(done) {\n    pm2 = new PM2.custom({\n      cwd : './fixtures'\n    });\n\n    pm2.uninstall('all', () => done())\n  })\n\n  describe('Package', function() {\n    before((done) => {\n      fs.unlink(PACKAGE_MONO, () => {\n        fs.unlink(PACKAGE_MULTI, () => {\n          done()\n        })\n      })\n    })\n\n    it('should package tarball for mono app', function(done) {\n      pm2.package(MODULE_FOLDER_MONO, (err) => {\n        should(err).be.null()\n        should(fs.existsSync(PACKAGE_MONO)).eql(true)\n        done()\n      })\n    })\n\n    it('should package tarball for multi app', function(done) {\n      pm2.package(MODULE_FOLDER_MULTI, (err) => {\n        should(err).be.null()\n        should(fs.existsSync(PACKAGE_MULTI)).eql(true)\n        done()\n      })\n    })\n  })\n\n  describe('MULTI Install', function() {\n    it('should install module', function(done) {\n      pm2.install(PACKAGE_MULTI, {\n        tarball: true\n      }, function(err, apps) {\n        should(err).eql(null);\n        done();\n      });\n    });\n\n    it('should have file decompressed in the right folder', function() {\n      var target_path = path.join(PM2._conf.DEFAULT_MODULE_PATH, 'multi-app-module')\n      fs.readFileSync(path.join(target_path, 'package.json'))\n    })\n\n    it('should have boot key present', function(done) {\n      var conf = JSON.parse(fs.readFileSync(process.env.HOME + '/.pm2/module_conf.json'))\n      should.exist(conf['tar-modules']['multi-app-module']);\n      done()\n    })\n\n    it('should have started 2 apps', function(done) {\n      pm2.list(function(err, list) {\n        should(err).be.null();\n        should(list.length).eql(2)\n        should(list[0].pm2_env.version).eql('0.1')\n        should(list[0].name).eql('multi-app-module:first_app')\n        should(list[1].name).eql('multi-app-module:second_app')\n        should(list[1].pm2_env.version).eql('0.1')\n        should(list[0].pm2_env.status).eql('online')\n        should(list[1].pm2_env.status).eql('online')\n        done()\n      })\n    })\n  })\n\n  describe('Reinstall', () => {\n    it('should install module', function(done) {\n      pm2.install(PACKAGE_MULTI, {\n        tarball: true\n      }, function(err, apps) {\n        should(err).eql(null);\n        done();\n      });\n    });\n\n    it('should have only 2 apps', function(done) {\n      pm2.list(function(err, list) {\n        should(err).be.null();\n        should(list.length).eql(2)\n        should(list[0].pm2_env.status).eql('online')\n        should(list[1].pm2_env.status).eql('online')\n        done()\n      })\n    })\n  })\n\n  describe('Re spawn PM2', () => {\n    it('should kill/resurect pm2', (done) => {\n      pm2.update(function(err) {\n        should(err).be.null();\n        done()\n      })\n    })\n\n    it('should have boot key present', function(done) {\n      var conf = JSON.parse(fs.readFileSync(process.env.HOME + '/.pm2/module_conf.json'))\n      should.exist(conf['tar-modules']['multi-app-module']);\n      done()\n    })\n\n    it('should have started 2 apps', function(done) {\n      pm2.list(function(err, list) {\n        should(err).be.null();\n        should(list.length).eql(2)\n        should(list[0].pm2_env.status).eql('online')\n        should(list[0].pm2_env.version).eql('0.1')\n        should(list[1].pm2_env.version).eql('0.1')\n        should(list[1].pm2_env.status).eql('online')\n        done()\n      })\n    })\n  })\n\n  describe('CLI UX', () => {\n    it('should not delete modules when calling pm2 delete all', (done) => {\n      pm2.delete('all', (err, apps) => {\n        should(apps.length).eql(2)\n        done()\n      })\n    })\n  })\n\n  describe('Uninstall', () => {\n    it('should uninstall multi app module', (done) => {\n      pm2.uninstall('multi-app-module', (err, data) => {\n        should(err).be.null();\n        done()\n      })\n    })\n\n    it('should have boot key deleted', function(done) {\n      var conf = JSON.parse(fs.readFileSync(process.env.HOME + '/.pm2/module_conf.json'))\n      should.not.exist(conf['tar-modules']['multi-app-module']);\n      done()\n    })\n\n    it('should have no running apps', function(done) {\n      pm2.list(function(err, list) {\n        should(err).be.null();\n        should(list.length).eql(0)\n        done()\n      })\n    })\n  })\n\n  describe('MONO APP', () => {\n    it('should install module', function(done) {\n      pm2.install(PACKAGE_MONO, {\n        tarball: true\n      }, function(err, apps) {\n        should(err).eql(null);\n        done();\n      });\n    });\n\n    it('should have file decompressed in the right folder', function() {\n      var target_path = path.join(PM2._conf.DEFAULT_MODULE_PATH, 'mono-app-module')\n      var pkg_path = path.join(target_path, 'package.json')\n      fs.readFileSync(pkg_path)\n    })\n\n    it('should have boot key present', function(done) {\n      var conf = JSON.parse(fs.readFileSync(process.env.HOME + '/.pm2/module_conf.json'))\n      should.exist(conf['tar-modules']['mono-app-module']);\n      done()\n    })\n\n    it('should have started 1 app', function(done) {\n      pm2.list(function(err, list) {\n        should(err).be.null();\n        should(list.length).eql(1)\n        should(list[0].name).eql('mono_app')\n        should(list[0].pm2_env.version).eql('0.23.0')\n        should(list[0].pm2_env.status).eql('online')\n        done()\n      })\n    })\n\n    it('should uninstall multi app module', (done) => {\n      pm2.uninstall('mono-app-module', (err, data) => {\n        should(err).be.null();\n        done()\n      })\n    })\n\n    it('should have boot key deleted', function(done) {\n      var conf = JSON.parse(fs.readFileSync(process.env.HOME + '/.pm2/module_conf.json'))\n      should.not.exist(conf['tar-modules']['mono-app-module']);\n      done()\n    })\n\n    it('should have no running apps', function(done) {\n      pm2.list(function(err, list) {\n        should(err).be.null();\n        should(list.length).eql(0)\n        done()\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/programmatic/modules.mocha.js",
    "content": "\nconst PM2 = require('../..');\nconst should = require('should');\n\ndescribe('Modules programmatic testing', function() {\n  var pm2;\n\n  after(function(done) {\n    pm2.kill(done);\n  });\n\n  it('should instanciate PM2', function() {\n    pm2 = new PM2.custom({\n      daemon_mode : true\n    });\n  });\n\n  it('should install a module', function(done) {\n    pm2.install('pm2-server-monit', function(err, apps) {\n      should(err).eql(null);\n      should(apps.length).eql(1);\n      var pm2_env = apps[0].pm2_env;\n      should.exist(pm2_env);\n      done();\n    });\n  });\n\n  it.skip('should run post install command', function(done) {\n    var fs = require('fs');\n    var ec = {};\n    ec.dependencies = new Array();\n    ec.dependencies.push('pm2-server-monit');\n    ec.post_install = {};\n    ec.post_install['pm2-server-monit'] = 'echo \"test passed!\"';\n    fs.appendFileSync('test.json', JSON.stringify(ec));\n    pm2.install('test.json', function() {\n      fs.unlinkSync('test.json');\n      done();\n    });\n  });\n\n  it('should list one module', function(done) {\n    pm2.list(function(err, apps) {\n      should(err).eql(null);\n      should(apps.length).eql(1);\n      var pm2_env = apps[0].pm2_env;\n      should(pm2_env.status).eql('online');\n      done();\n    });\n  });\n\n  it('should uninstall all modules', function(done) {\n    pm2.uninstall('all', function(err, apps) {\n      done();\n    });\n  });\n});\n"
  },
  {
    "path": "test/programmatic/namespace.mocha.js",
    "content": "\n\nprocess.chdir(__dirname);\n\nvar PM2 = require('../..');\nvar should = require('should');\n\ndescribe('NAMESPACE app management', function() {\n  var pm2 = new PM2.custom({\n    cwd : __dirname + '/../fixtures'\n  });\n\n  before(function(done) {\n    pm2.delete('all', function() { done() });\n  });\n\n  after(function(done) {\n    pm2.kill(done);\n  });\n\n  it('should start 2 app in NS1', (done) => {\n    pm2.start({\n      script: './echo.js',\n      name: 'echo1-ns1',\n      namespace: 'NS1'\n    }, (err, procs) => {\n      should(err).be.null()\n      procs[0].pm2_env.namespace.should.eql('NS1')\n      pm2.start({\n        script: './echo.js',\n        namespace: 'NS1',\n        name: 'echo2-ns1'\n      }, (err, procs) => {\n        should(err).be.null()\n        procs[0].pm2_env.namespace.should.eql('NS1')\n        done()\n      })\n    })\n  })\n\n  it('should start 2 app in NS2', (done) => {\n    pm2.start({\n      script: './echo.js',\n      name: 'echo1-ns2',\n      namespace: 'NS2'\n    }, (err, procs) => {\n      should(err).be.null()\n      procs[0].pm2_env.namespace.should.eql('NS2')\n      pm2.start({\n        script: './echo.js',\n        name: 'echo2-ns2',\n        namespace: 'NS2'\n      }, (err, procs) => {\n        should(err).be.null()\n        procs[0].pm2_env.namespace.should.eql('NS2')\n        done()\n      })\n    })\n  })\n\n  it('should restart only app in NS1', function(done) {\n    pm2.restart('NS1', () => {\n      PM2.list(function(err, list) {\n        should(err).be.null();\n        should(list.length).eql(4);\n        list.forEach(l => {\n          if (l.name == 'echo1-ns1')\n            should(l.pm2_env.restart_time).eql(1)\n          if (l.name == 'echo2-ns1')\n            should(l.pm2_env.restart_time).eql(1)\n          if (l.name == 'echo1-ns2')\n            should(l.pm2_env.restart_time).eql(0)\n          if (l.name == 'echo2-ns2')\n            should(l.pm2_env.restart_time).eql(0)\n        })\n        done();\n      });\n    })\n  })\n\n  it('should restart all', function(done) {\n    pm2.restart('all', () => {\n      PM2.list(function(err, list) {\n        should(err).be.null();\n        should(list.length).eql(4);\n        list.forEach(l => {\n          if (l.name == 'echo1-ns1')\n            should(l.pm2_env.restart_time).eql(2)\n          if (l.name == 'echo2-ns1')\n            should(l.pm2_env.restart_time).eql(2)\n          if (l.name == 'echo1-ns2')\n            should(l.pm2_env.restart_time).eql(1)\n          if (l.name == 'echo2-ns2')\n            should(l.pm2_env.restart_time).eql(1)\n        })\n        done();\n      });\n    })\n  })\n\n  it('should restart NS2', function(done) {\n    pm2.restart('NS2', () => {\n      PM2.list(function(err, list) {\n        should(err).be.null();\n        should(list.length).eql(4);\n        list.forEach(l => {\n          if (l.name == 'echo1-ns1')\n            should(l.pm2_env.restart_time).eql(2)\n          if (l.name == 'echo2-ns1')\n            should(l.pm2_env.restart_time).eql(2)\n          if (l.name == 'echo1-ns2')\n            should(l.pm2_env.restart_time).eql(2)\n          if (l.name == 'echo2-ns2')\n            should(l.pm2_env.restart_time).eql(2)\n        })\n        done();\n      });\n    })\n  })\n\n  it('should stop NS2', function(done) {\n    pm2.stop('NS2', () => {\n      PM2.list(function(err, list) {\n        should(err).be.null();\n        should(list.length).eql(4);\n        list.forEach(l => {\n          if (l.name == 'echo1-ns1')\n            should(l.pm2_env.restart_time).eql(2)\n          if (l.name == 'echo2-ns1')\n            should(l.pm2_env.restart_time).eql(2)\n          if (l.name == 'echo1-ns2')\n            should(l.pm2_env.status).eql('stopped')\n          if (l.name == 'echo2-ns2')\n            should(l.pm2_env.status).eql('stopped')\n        })\n        done();\n      });\n    })\n  })\n\n  it('should delete NS2', function(done) {\n    pm2.delete('NS2', () => {\n      PM2.list(function(err, list) {\n        should(err).be.null();\n        should(list.length).eql(2);\n        done();\n      });\n    })\n  })\n\n})\n"
  },
  {
    "path": "test/programmatic/path_resolution.mocha.js",
    "content": "\nvar path = require('path');\nprocess.chdir(path.join(__dirname, '../fixtures'));\n\nvar PM2 = require('../..');\nvar should = require('should');\n\ndescribe('Path resolution in configuration file', function() {\n  this.timeout(4000)\n  before(function(done) {\n    PM2.delete('all', function() { done() } );\n  });\n\n  afterEach(function(done) {\n    PM2.delete('all', function() { done() } );\n  });\n\n  it('should resolve paths (home)', function(done) {\n    PM2.start('./path-resolution/ecosystem.config.js', function(err, proc) {\n      should(proc[0].pm2_env.pm_err_log_path).eql(path.join(process.env.HOME, 'echo-err.log'));\n      should(proc[0].pm2_env.pm_out_log_path).eql(path.join(process.env.HOME, 'echo-out.log'));\n      should(proc[0].pm2_env.pm_pid_path).eql(path.join(process.env.HOME, 'echo-pid.log'));\n      done();\n    });\n  });\n\n  it('should resolve paths (local)', function(done) {\n    PM2.start('./path-resolution/ecosystem2.config.js', function(err, proc) {\n      should(proc[0].pm2_env.pm_err_log_path).eql(path.join(process.cwd(), 'echo-err.log'));\n      should(proc[0].pm2_env.pm_out_log_path).eql(path.join(process.cwd(), 'echo-out.log'));\n      should(proc[0].pm2_env.pm_pid_path).eql(path.join(process.cwd(), 'echo-pid.log'));\n      done();\n    });\n  });\n\n  it('should auto prefix log path on cluster mode', function(done) {\n    PM2.start('./path-resolution/ecosystem3.config.js', function(err, proc) {\n      should(proc[0].pm2_env.pm_err_log_path).eql(path.join(process.cwd(), 'echo-err-0.log'));\n      should(proc[0].pm2_env.pm_out_log_path).eql(path.join(process.cwd(), 'echo-out-0.log'));\n\n      should(proc[1].pm2_env.pm_err_log_path).eql(path.join(process.cwd(), 'echo-err-1.log'));\n      should(proc[1].pm2_env.pm_out_log_path).eql(path.join(process.cwd(), 'echo-out-1.log'));\n      done();\n    });\n  });\n\n});\n"
  },
  {
    "path": "test/programmatic/programmatic.js",
    "content": "\n/**\n * PM2 programmatic API tests\n */\n\n//process.env.NODE_ENV ='test';\nvar PM2    = require('../..');\nvar should = require('should');\nvar path   = require('path');\n\ndescribe('PM2 programmatic calls', function() {\n  var proc1 = null;\n  var procs = [];\n  var bus   = null;\n\n  var pm2 = new PM2.custom({\n    cwd : __dirname + '/../fixtures'\n  });\n\n  after(function(done) {\n    pm2.delete('all', function(err, ret) {\n      // clean dump file\n      pm2.clearDump(function(err) {\n        pm2.kill(done);\n      });\n    });\n  });\n\n  before(function(done) {\n    pm2.connect(function() {\n      pm2.launchBus(function(err, _bus) {\n        bus = _bus;\n        pm2.delete('all', function(err, ret) {\n          done();\n        });\n      });\n    });\n  });\n\n  describe('General methods', function() {\n    it('should start a script', function(done) {\n      pm2.start({\n        script : './child.js',\n        name : 'child',\n        instances : 1\n      }, function(err, data) {\n        proc1 = data[0];\n        should(err).be.null()\n        done();\n      });\n    });\n\n    it('should get id', function(done) {\n      pm2.getProcessIdByName('child', function(err, data) {\n        data[0].should.eql(0);\n        should(err).be.null()\n        setTimeout(done, 300);\n      });\n    });\n\n    it('should get node.js version application running', function(done) {\n      pm2.describe('child', function(err, data) {\n        should(err).be.null()\n        data[0].pm2_env.node_version.should.eql(process.versions.node);\n        done();\n      });\n    });\n\n    it('should start a script and force to launch it', function(done) {\n      pm2.start({\n        script : './child.js',\n        force : true,\n        name : 'toto',\n        instances : 1\n      }, function(err, data) {\n        should(err).be.null()\n        data.length.should.eql(1);\n        done();\n      });\n    });\n\n    it('should start a script in a specified cwd', function(done) {\n      var target_cwd = path.join(__dirname, '/../fixtures/');\n\n      pm2.start({\n        script : './cron.js',\n        cwd: target_cwd,\n        instances : 1\n      }, function(err, data) {\n        should(err).be.null();\n        proc1 = data[0];\n        proc1.pm2_env.cwd.should.eql(target_cwd);\n        should(err).be.null()\n        done();\n      });\n    });\n\n    it('should notice error if wrong file passed', function(done) {\n      pm2.start('./UNKNOWN_SCRIPT.js', {\n        force : true,\n        name : 'tota',\n        instances : 3\n      }, function(err, data) {\n        should.exists(err);\n        done();\n      });\n    });\n\n    it('should start a script and force to launch it', function(done) {\n      pm2.start('./child.js', {\n        force : true,\n        name : 'tota',\n        instances : 6\n      }, function(err, data) {\n        should(err).be.null()\n        data.length.should.eql(6);\n        done();\n      });\n    });\n\n    it('should get pm2 version', function(done) {\n      pm2.getVersion(function(err, data) {\n        should(err).be.null()\n        should.exists(data);\n        done();\n      });\n    });\n\n    it('should list processes', function(done) {\n      pm2.list(function(err, ret) {\n        should(err).be.null()\n        ret.length.should.eql(9);\n        done();\n      });\n    });\n\n    it('should delete one process', function(done) {\n      pm2.delete(proc1.pm2_env.pm_id, function(err, ret) {\n        should(err).be.null()\n        pm2.list(function(err, ret) {\n          should(err).be.null()\n          ret.length.should.eql(8);\n          done();\n        });\n      });\n    });\n\n    it('should save/dump all processes', function(done) {\n      pm2.dump(function(err, ret) {\n        should(err).be.null()\n        done();\n      });\n    });\n\n    it('should delete processes', function(done) {\n      pm2.delete('all', function(err, ret) {\n        should(err).be.null()\n        pm2.list(function(err, ret) {\n          should(err).be.null()\n          ret.length.should.eql(0);\n          done();\n        });\n      });\n    });\n\n    it('should resurrect processes', function(done) {\n      pm2.resurrect(function(err, ret) {\n        should(err).be.null()\n        pm2.list(function(err, ret) {\n          should(err).be.null()\n          ret.length.should.eql(8);\n          done();\n        });\n      });\n    });\n\n    it('should ping pm2', function(done) {\n      pm2.ping(function(err, ret) {\n        should(err).be.null()\n        done();\n      });\n    });\n\n    it('should reload all', function(done) {\n      pm2.reload('all', function(err, ret) {\n        should(err).be.null()\n        done();\n      });\n    });\n\n    it('should reload only one application', function(done) {\n      pm2.reload('tota', function(err, ret) {\n        should(err).be.null()\n        pm2.describe('tota', function(err, proc) {\n          should(err).be.null()\n          procs = proc;\n          proc[0].pm2_env.restart_time.should.eql(2);\n          done();\n        });\n      });\n    });\n\n    it('should describe all process with name', function(done) {\n      pm2.describe('tota', function(err, proc) {\n        should(err).be.null()\n        proc.length.should.eql(6);\n        done();\n      });\n    });\n  });\n\n  describe('Restart methods', function() {\n    it('should restart all', function(done) {\n      pm2.restart('all', function(err, ret) {\n        should(err).be.null()\n        pm2.describe('tota', function(err, proc) {\n          should(err).be.null()\n          proc.length.should.eql(6);\n          proc[0].pm2_env.restart_time.should.eql(3);\n          done();\n        });\n      });\n    });\n\n    it('should restart process by name', function(done) {\n      pm2.restart('tota', function(err, ret) {\n        should(err).be.null()\n        pm2.describe('tota', function(err, proc) {\n          should(err).be.null()\n          proc.length.should.eql(6);\n          proc[0].pm2_env.restart_time.should.eql(4);\n          done();\n        });\n      });\n    });\n\n    it('should restart process by id', function(done) {\n      pm2.restart(procs[0].pm2_env.pm_id, function(err, ret) {\n        should(err).be.null()\n        pm2.describe(procs[0].pm2_env.pm_id, function(err, proc) {\n          should(err).be.null()\n          proc.length.should.eql(1);\n          proc[0].pm2_env.restart_time.should.eql(5);\n          done();\n        });\n      });\n    });\n  });\n\n  describe('Stop methods', function() {\n    it('should stop process name', function(done) {\n      pm2.stop(procs[0].pm2_env.name, function(err, ret) {\n        should(err).be.null()\n        pm2.describe(procs[0].pm2_env.name, function(err, procs) {\n          should(err).be.null()\n          procs[0].pm2_env.status.should.eql('stopped');\n          done();\n        });\n      });\n    });\n\n    it('should stop process id', function(done) {\n      pm2.stop(procs[1].pm2_env.pm_id, function(err, ret) {\n        should(err).be.null()\n        pm2.describe(procs[1].pm2_env.pm_id, function(err, procs) {\n          should(err).be.null()\n          procs[0].pm2_env.status.should.eql('stopped');\n          done();\n        });\n      });\n    });\n\n    it('should stop process all', function(done) {\n      pm2.stop('all', function(err, ret) {\n        should(err).be.null()\n        pm2.describe(procs[0].pm2_env.pm_id, function(err, procs) {\n          should(err).be.null()\n          procs[0].pm2_env.status.should.eql('stopped');\n          done();\n        });\n      });\n    });\n  });\n\n  describe('start OR restart', function() {\n    beforeEach(function(done) {\n      pm2.delete('all', function(err, ret) {\n        done();\n      });\n    });\n\n    it('should start a JSON object in cluster mode', function(done) {\n      pm2.start({\n        script : './echo.js',\n        instances : 4,\n        exec_mode : 'cluster'\n      }, function(err, dt) {\n        should(err).be.null()\n\n        pm2.list(function(err, ret) {\n          should(err).be.null()\n          ret.length.should.eql(4);\n          ret[0].pm2_env.exec_mode.should.eql('cluster_mode');\n          done();\n        });\n      });\n    });\n\n    it('should start a JSON object in fork mode', function(done) {\n      pm2.start({\n        script : './echo.js',\n        instances : 4,\n        exec_mode : 'fork'\n      }, function(err, dt) {\n        should(err).be.null()\n\n        pm2.list(function(err, ret) {\n          should(err).be.null()\n          ret.length.should.eql(4);\n          ret[0].pm2_env.exec_mode.should.eql('fork_mode');\n          done();\n        });\n      });\n    });\n\n    it('should start a JSON file', function(done) {\n      pm2.start('./all2.json', function(err, dt) {\n        should(err).be.null()\n\n        pm2.list(function(err, ret) {\n          should(err).be.null()\n          ret.length.should.eql(4);\n          done();\n        });\n      });\n    });\n  });\n\n\n  describe('start OR restart', function() {\n    before(function(done) {\n      pm2.delete('all', function(err, ret) {\n        pm2.list(function(err, ret) {\n          should(err).be.null()\n          ret.length.should.eql(0);\n          done();\n        });\n      });\n    });\n\n    it('should start', function(done) {\n      pm2._startJson('./all2.json', {}, 'restartProcessId', function(err, data) {\n        should(err).be.null()\n        pm2.list(function(err, ret) {\n          should(err).be.null()\n          ret.length.should.eql(4);\n          done();\n        });\n      });\n    });\n\n    it('should NOW restart action', function(done) {\n      pm2._startJson('./all2.json', {}, 'restartProcessId', function(err, data) {\n        should(err).be.null()\n        pm2.list(function(err, ret) {\n          should(err).be.null()\n\n          ret.forEach(function(app) {\n            app.pm2_env.restart_time.should.eql(1);\n          });\n          done();\n        });\n      });\n    });\n\n    it('should reset status', function(done) {\n      pm2.delete('all', function(err, ret) {\n        done();\n      });\n    });\n\n    it('should start with specific environment variables depending on the env type', function(done) {\n      pm2._startJson('../fixtures/all2.json', {\n        env : 'production'\n      }, 'restartProcessId', function(err, data) {\n        should(err).be.null()\n        pm2.list(function(err, ret) {\n          should(err).be.null();\n          ret[0].pm2_env['NODE_ENV'].should.eql('production');\n          ret[0].pm2_env['TOTO'].should.eql('heymoto');\n          done();\n        });\n      });\n    });\n\n  });\n\n  describe('Connect / Disconnect', function() {\n    it('should disconnect', function() {\n      pm2.disconnect();\n    });\n\n    it('should connect', function(done) {\n      pm2.connect(function() {\n        done();\n      });\n    });\n\n    it('should disconnect with callback', function(done) {\n      pm2.disconnect(function() {\n        done();\n      });\n    });\n\n    it('should connect', function(done) {\n      pm2.connect(function() {\n        done();\n      });\n    });\n  });\n\n});\n"
  },
  {
    "path": "test/programmatic/reload-locker.mocha.js",
    "content": "\n\nprocess.env.NODE_ENV = 'test';\nprocess.env.PM2_RELOAD_LOCK_TIMEOUT = 2000;\n\nvar PM2    = require('../..');\nvar should = require('should');\nvar path   = require('path');\nvar Plan   = require('../helpers/plan.js');\nvar fs = require('fs');\nvar cst = require('../../constants.js');\n\nprocess.chdir(__dirname);\n\ndescribe('Reload locker system', function() {\n  var pm2 = new PM2.custom({\n    cwd : '../fixtures'\n  });\n\n  before(function(done) {\n    pm2.list(done);\n  });\n\n  after(function(done) {\n    pm2.kill(done)\n  });\n\n  it('should start app', function(done) {\n    pm2.start({\n      script    : './http.js',\n      instances : 2\n    }, function(err, data) {\n      should(err).be.null();\n\n      pm2.list(function(err, ret) {\n        should(err).be.null();\n        ret.length.should.eql(2);\n        done();\n      });\n    });\n  });\n\n  it('should trigger one reload and forbid the second', function(done) {\n    pm2.reload('all');\n\n    setTimeout(function() {\n      fs.statSync(cst.PM2_RELOAD_LOCKFILE);\n      var dt = parseInt(fs.readFileSync(cst.PM2_RELOAD_LOCKFILE).toString());\n\n      should(dt).above(0);\n\n      pm2.reload('all', function(err) {\n        should.exists(err);\n        if (err)\n          done()\n        else\n          done(new Error('should trigger error'));\n      });\n    }, 100);\n  });\n\n  it('should re allow reload when reload finished', function(done) {\n    setTimeout(function() {\n      pm2.reload('all', done);\n    }, 2000);\n  });\n\n  it('should lock file be empty', function() {\n    var dt = fs.readFileSync(cst.PM2_RELOAD_LOCKFILE).toString();\n    should(dt).eql('');\n  });\n\n});\n"
  },
  {
    "path": "test/programmatic/resurect_state.mocha.js",
    "content": "\nvar PM2    = require('../..');\nvar should = require('should');\nvar path   = require('path');\nvar fs     = require('fs');\n\nvar cst = require('../../constants.js');\nvar Configuration = require('../../lib/Configuration.js');\n\ndescribe.skip('Keep state on pm2 update', function() {\n  var pm2\n\n  before((done) => {\n    Configuration.set('pm2:autodump', 'true', function(err, data) {\n        pm2 = new PM2.custom({\n          cwd : __dirname + '/../fixtures'\n        });\n\n      should.not.exists(err);\n      done();\n    });\n  })\n\n  after((done) => {\n    Configuration.set('pm2:autodump', 'false', function(err, data) {\n      should.not.exists(err);\n      done();\n    });\n  })\n\n  describe('Should autosave edits on stop/start/delete', function() {\n\n    after(function(done) {\n      pm2.kill(done);\n    });\n\n    before(function(done) {\n      pm2.connect(function() {\n        pm2.delete('all', function() {\n          done();\n        });\n      });\n    });\n\n    it('should set autodump to true', function(done) {\n      pm2.set('pm2:autodump', 'true', function(err, data) {\n        should.not.exists(err);\n        done();\n      });\n    })\n\n    it('should start 4 processes', function(done) {\n      pm2.start({\n        script    : './echo.js',\n        instances : 4,\n        name      : 'echo'\n      }, function(err, data) {\n        should(err).be.null();\n        done();\n      });\n    });\n\n    it('should kill pm2', function(done) {\n      pm2.kill(done)\n    })\n\n    it('should resurect with one process stopped', function(done) {\n      pm2.resurrect(() => {\n        pm2.list((err, dt) => {\n          if (dt.length == 4)\n            return done()\n          return done(new Error('Did not kept process status'))\n        })\n      })\n    })\n\n    it('should stop 1 process', function(done) {\n      pm2.stop(0, done)\n    })\n\n    it('should kill pm2', function(done) {\n      pm2.kill(done)\n    })\n\n    it('should resurect with one process stopped', function(done) {\n      pm2.resurrect(() => {\n        pm2.list((err, dt) => {\n          if (dt.length == 4 && dt.filter(proc => proc.pm2_env.status == 'stopped').length == 1)\n            return done()\n          return done(new Error('Did not kept process status'))\n        })\n      })\n    })\n\n    it('should delete and save', function(done) {\n      pm2.delete(0, done)\n    })\n\n    it('should kill pm2', function(done) {\n      pm2.kill(done)\n    })\n\n    it('should resurect with one process stopped', function(done) {\n      pm2.resurrect(() => {\n        pm2.list((err, dt) => {\n          if (dt.length == 3)\n            return done()\n          return done(new Error('Did not kept process status'))\n        })\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/programmatic/send_data_process.mocha.js",
    "content": "\n\n/**\n * PM2 programmatic API tests\n */\n\nvar PM2    = require('../..');\nvar should = require('should');\nvar path   = require('path');\n\ndescribe('PM2 programmatic calls', function() {\n\n  var pm2 = new PM2.custom({\n    cwd : __dirname + '/../fixtures'\n  });\n\n  var pm2_bus = null;\n  var proc1   = null;\n  var procs   = [];\n\n  after(function(done) {\n    pm2.delete('all', function(err, ret) {\n      pm2.kill(done);\n    });\n  });\n\n  before(function(done) {\n    pm2.connect(function() {\n      pm2.launchBus(function(err, bus) {\n        pm2_bus = bus;\n\n        pm2.delete('all', function(err, ret) {\n          done();\n        });\n      });\n    });\n  });\n\n  /**\n   * process.on('message', function(packet) {\n   *   process.send({\n   *     topic : 'process:msg',\n   *     data  : {\n   *       success : true\n   *     }\n   *   });\n   * });\n   */\n  it('should start a script', function(done) {\n    pm2.start({\n      script : './send-data-process/return-data.js'\n    }, function(err, data) {\n      proc1 = data[0];\n      should(err).be.null();\n      done();\n    });\n  });\n\n  it('should receive data packet', function(done) {\n    pm2_bus.on('process:msg', function(packet) {\n      pm2_bus.off('process:msg');\n      packet.raw.data.success.should.eql(true);\n      packet.raw.topic.should.eql('process:msg');\n      packet.process.pm_id.should.eql(proc1.pm2_env.pm_id);\n      packet.process.name.should.eql(proc1.pm2_env.name);\n      done();\n    });\n\n    pm2.sendDataToProcessId(proc1.pm2_env.pm_id, {\n      topic : 'process:msg',\n      data : {\n        some : 'data',\n        hello : true\n      }\n    }, function(err, res) {\n      should(err).be.null();\n    });\n  });\n\n  it('should receive data packet (other input)', function(done) {\n    pm2_bus.on('process:msg', function(packet) {\n      pm2_bus.off('process:msg');\n      packet.raw.data.success.should.eql(true);\n      packet.raw.topic.should.eql('process:msg');\n      packet.process.pm_id.should.eql(proc1.pm2_env.pm_id);\n      packet.process.name.should.eql(proc1.pm2_env.name);\n      done();\n    });\n\n    pm2.sendDataToProcessId({\n      id: proc1.pm2_env.pm_id,\n      topic : 'process:msg',\n      data : {\n        some : 'data',\n        hello : true\n      }\n    }, function(err, res) {\n      should(err).be.null();\n    });\n  });\n\n});\n"
  },
  {
    "path": "test/programmatic/signals.js",
    "content": "\nvar PM2    = require('../..');\nvar should = require('should');\nvar path   = require('path');\nvar fs     = require('fs');\n\ndescribe('Signal kill (+delayed)', function() {\n  var proc1 = null;\n\n  var pm2 = new PM2.custom({\n    cwd : __dirname + '/../fixtures'\n  });\n\n  after(function(done) {\n    pm2.delete('all', function(err, ret) {\n      pm2.kill(done);\n    });\n  });\n\n  before(function(done) {\n    pm2.connect(function() {\n      pm2.delete('all', function(err, ret) {\n        done();\n      });\n    });\n  });\n\n  describe('with 3000ms PM2_KILL_TIMEOUT (environment variable)', function() {\n    it('should set 3000ms to PM2_KILL_TIMEOUT', function(done) {\n      process.env.PM2_KILL_TIMEOUT = 3000;\n\n      pm2.update(function() {\n        done();\n      });\n    });\n\n    it('should start a script', function(done) {\n      pm2.start({\n        script : './signals/delayed_sigint.js',\n        name : 'delayed-sigint'\n      }, function(err, data) {\n        proc1 = data[0];\n        should(err).be.null();\n        setTimeout(done, 1000);\n      });\n    });\n\n    it('should stop script after 3000ms', function(done) {\n      setTimeout(function() {\n        pm2.list(function(err, list) {\n          list[0].pm2_env.status.should.eql('stopping');\n        });\n      }, 2500);\n\n      setTimeout(function() {\n        pm2.list(function(err, list) {\n          list[0].pm2_env.status.should.eql('stopped');\n          done();\n        });\n      }, 3500);\n\n      pm2.stop('delayed-sigint', function(err, app) {\n        //done(err);\n      });\n\n    });\n  });\n\n  describe('with 1000ms PM2_KILL_TIMEOUT (environment variable)', function() {\n    it('should set 1000ms to PM2_KILL_TIMEOUT', function(done) {\n      process.env.PM2_KILL_TIMEOUT = 1000;\n\n      pm2.update(function() {\n        done();\n      });\n    });\n\n    it('should start a script', function(done) {\n      pm2.start({\n        script : './signals/delayed_sigint.js',\n        name : 'delayed-sigint'\n      }, function(err, data) {\n        proc1 = data[0];\n        should(err).be.null();\n        setTimeout(done, 1000);\n      });\n    });\n\n    it('should stop script after 1000ms', function(done) {\n      setTimeout(function() {\n        pm2.list(function(err, list) {\n          list[0].pm2_env.status.should.eql('stopping');\n        });\n      }, 500);\n\n      setTimeout(function() {\n        pm2.list(function(err, list) {\n          list[0].pm2_env.status.should.eql('stopped');\n          done();\n        });\n      }, 1500);\n\n      pm2.stop('delayed-sigint', function(err, app) {\n        //done(err);\n      });\n\n    });\n  });\n\n  describe('[CLUSTER MODE] with 1000ms PM2_KILL_TIMEOUT (environment variable)', function() {\n    it('should set 1000ms to PM2_KILL_TIMEOUT', function(done) {\n      process.env.PM2_KILL_TIMEOUT = 1000;\n\n      pm2.update(function() {\n        done();\n      });\n    });\n\n    it('should start a script', function(done) {\n      pm2.start({\n        script : './signals/delayed_sigint.js',\n        name : 'delayed-sigint',\n        exec_mode : 'cluster'\n      }, function(err, data) {\n        proc1 = data[0];\n        should(err).be.null();\n        setTimeout(done, 1000);\n      });\n    });\n\n    it('should stop script after 1000ms', function(done) {\n      setTimeout(function() {\n        pm2.list(function(err, list) {\n          list[0].pm2_env.status.should.eql('stopping');\n        });\n      }, 500);\n\n      setTimeout(function() {\n        pm2.list(function(err, list) {\n          list[0].pm2_env.status.should.eql('stopped');\n          done();\n        });\n      }, 1500);\n\n      pm2.stop('delayed-sigint', function(err, app) {\n        //done(err);\n      });\n\n    });\n\n    it('should reload script', function(done) {\n      setTimeout(function() {\n        pm2.list(function(err, list) {\n          list[0].pm2_env.status.should.eql('online');\n          done();\n        });\n      }, 1500);\n\n      pm2.reload('delayed-sigint', function(err, app) {\n        //done(err);\n      });\n\n    });\n  });\n\n  describe('with 4000ms via kill_timeout (json/cli option)', function() {\n    it('should set 1000ms to PM2_KILL_TIMEOUT', function(done) {\n      process.env.PM2_KILL_TIMEOUT = 1000;\n\n      pm2.update(function() {\n        done();\n      });\n    });\n\n    it('should start a script with flag kill timeout to 4000ms', function(done) {\n      pm2.start({\n        script : './signals/delayed_sigint.js',\n        name : 'delayed-sigint',\n        exec_mode : 'cluster',\n        kill_timeout : 4000\n      }, function(err, data) {\n        proc1 = data[0];\n        should(err).be.null();\n        setTimeout(done, 1000);\n      });\n    });\n\n    it('should stop script after 4000ms (and not 1000ms)', function(done) {\n      setTimeout(function() {\n        pm2.list(function(err, list) {\n          list[0].pm2_env.status.should.eql('stopping');\n        });\n      }, 1500);\n\n      setTimeout(function() {\n        pm2.list(function(err, list) {\n          list[0].pm2_env.status.should.eql('stopped');\n          done();\n        });\n      }, 4500);\n\n      pm2.stop('delayed-sigint', function(err, app) {\n        //done(err);\n      });\n\n    });\n\n    it('should delete all', function(done) {\n      pm2.delete('all', done)\n    })\n\n  });\n\n});\n\ndescribe('Message kill (signal behavior override via PM2_KILL_USE_MESSAGE, +delayed)', function() {\n  var proc1 = null;\n  var appName = 'delayedsend';\n\n  process.env.PM2_KILL_USE_MESSAGE = true;\n\n  var pm2 = new PM2.custom({\n    cwd : __dirname + '/../fixtures',\n  });\n\n  after(function(done) {\n    pm2.delete('all', function(err, ret) {\n      pm2.kill(done);\n    });\n  });\n\n  before(function(done) {\n    pm2.connect(function() {\n      pm2.delete('all', function(err, ret) {\n        done();\n      });\n    });\n  });\n\n  describe.only('with 1000ms PM2_KILL_TIMEOUT (environment variable)', function() {\n    it('should set 1000ms to PM2_KILL_TIMEOUT', function(done) {\n      process.env.PM2_KILL_TIMEOUT = 1000;\n\n      pm2.update(function() {\n        done();\n      });\n    });\n\n    it('should start a script', function(done) {\n      pm2.start({\n        script : './signals/delayed_send.js',\n        error_file : 'error-echo.log',\n        out_file   : 'out-echo.log',\n        name : appName,\n      }, function(err, data) {\n        proc1 = data[0];\n        should(err).be.null();\n        setTimeout(done, 1000);\n      });\n    });\n\n    it('should stop script after 1000ms', function(done) {\n      setTimeout(function() {\n        console.log('CALLED1')\n        pm2.describe(appName, function(err, list) {\n          console.log('CALLED1FINI')\n          should(err).be.null();\n          list[0].pm2_env.status.should.eql('stopping');\n        });\n      }, 500);\n\n      setTimeout(function() {\n        console.log('CALLED2')\n        pm2.describe(appName, function(err, list) {\n          console.log('CALLED2FINI')\n          should(err).be.null();\n          list[0].pm2_env.status.should.eql('stopped');\n          done();\n        });\n      }, 1500);\n\n      pm2.stop(appName, function(err, app) {\n        //done(err);\n      });\n\n    });\n\n    it('should read shutdown message', function(done) {\n      var out_file = proc1.pm2_env.pm_out_log_path;\n      fs.readFileSync(out_file).toString().should.containEql('shutdown message');\n      done();\n    })\n\n    it('should delete all', function(done) {\n      pm2.delete('all', done)\n    })\n\n  });\n\n  describe('[CLUSTER MODE] with 1000ms PM2_KILL_TIMEOUT (environment variable)', function() {\n    it('should set 1000ms to PM2_KILL_TIMEOUT', function(done) {\n      process.env.PM2_KILL_TIMEOUT = 1000;\n\n      pm2.update(function() {\n        done();\n      });\n    });\n\n    it('should start a script', function(done) {\n      pm2.start({\n        script : './signals/delayed_send.js',\n        name : appName,\n        exec_mode : 'cluster'\n      }, function(err, data) {\n        proc1 = data[0];\n        should(err).be.null();\n        setTimeout(done, 1000);\n      });\n    });\n\n    it('should stop script after 1000ms', function(done) {\n      setTimeout(function() {\n        pm2.describe(appName, function(err, list) {\n          should(err).be.null();\n          list[0].pm2_env.status.should.eql('stopping');\n        });\n      }, 500);\n\n      setTimeout(function() {\n        pm2.describe(appName, function(err, list) {\n          should(err).be.null();\n          list[0].pm2_env.status.should.eql('stopped');\n          done();\n        });\n      }, 1500);\n\n      pm2.stop(appName, function(err, app) {\n        //done(err);\n      });\n\n    });\n\n    it('should reload script', function(done) {\n      setTimeout(function() {\n        pm2.describe(appName, function(err, list) {\n          should(err).be.null();\n          list[0].pm2_env.status.should.eql('online');\n          done();\n        });\n      }, 1500);\n\n      pm2.reload(appName, function(err, app) {\n        //done(err);\n      });\n\n    });\n\n    it('should read shutdown message', function(done) {\n      var out_file = proc1.pm2_env.pm_out_log_path;\n      fs.readFileSync(out_file).toString().should.containEql('shutdown message');\n      done();\n    })\n\n  });\n\n});\n"
  },
  {
    "path": "test/programmatic/sys_infos.mocha.js",
    "content": "\nconst Sysinfos = require('../../lib/Sysinfo/SystemInfo.js')\nconst should = require('should');\nconst path = require('path')\n\ndescribe('Sysinfos', function() {\n  var sysinfo\n\n  after(() => {\n    sysinfo.kill()\n  })\n\n  it('should start a failing app in fork mode', function(done) {\n    sysinfo = new Sysinfos()\n    sysinfo.fork()\n    done()\n  })\n\n  it('should query procs', function(done) {\n\n    setTimeout(() => {\n      sysinfo.query((err, data) => {\n        console.log(data)\n        done()\n      })\n\n    }, 500)\n  })\n\n})\n"
  },
  {
    "path": "test/programmatic/user_management.mocha.js",
    "content": "\nprocess.env.NODE_ENV = 'test'\nprocess.chdir(__dirname);\n\nvar PM2 = require('../..');\nvar should = require('should');\n\ndescribe('User management', function() {\n  before(function(done) {\n    PM2.delete('all', function() { done() });\n  });\n\n  after(function(done) {\n    PM2.kill(done);\n  });\n\n  it('should fail with unknown user', function(done) {\n    PM2.start('./../fixtures/child.js', {\n      user: 'toto'\n    },function(err) {\n      should(err.message).match(/cannot be found/)\n\n      PM2.list(function(err, list) {\n        should(err).be.null();\n        should(list.length).eql(0);\n        done();\n      });\n    });\n  })\n\n  it('should succeed with known user', function(done) {\n    PM2.start('./../fixtures/child.js', {\n      user: process.env.USER\n    },function(err) {\n      should(err).be.null();\n      PM2.list(function(err, list) {\n        should(err).be.null();\n        should(list.length).eql(1);\n        should.exist(list[0].pm2_env.uid)\n        should.exist(list[0].pm2_env.gid)\n        PM2.delete('all', done)\n      });\n    });\n  })\n\n  it('should succeed with known user via uid field', function(done) {\n    PM2.start('./../fixtures/child.js', {\n      uid: process.env.USER\n    },function(err) {\n      should(err).be.null();\n      PM2.list(function(err, list) {\n        should(err).be.null();\n        should.exist(list[0].pm2_env.uid)\n        should.exist(list[0].pm2_env.gid)\n        should(list.length).eql(1);\n        PM2.delete('all', done)\n      });\n    });\n  })\n})\n"
  },
  {
    "path": "test/programmatic/version.mocha.js",
    "content": "\nconst PM2 = require('../..');\nconst should = require('should');\nconst exec = require('child_process').exec\nconst path = require('path')\nconst fs = require('fs')\n\ndescribe('Modules programmatic testing', function() {\n  var pm2\n  var pkg_path = path.join(__dirname, 'fixtures/version-test/package.json')\n\n  after(function(done) {\n    pm2.delete('all', function() {\n      pm2.kill(done);\n    })\n  });\n\n  before(function(done) {\n    pm2 = new PM2.custom({\n      cwd : path.join(__dirname, 'fixtures')\n    });\n\n    var pkg = JSON.parse(fs.readFileSync(pkg_path))\n    pkg.version = '1.0.0'\n    fs.writeFileSync(pkg_path, JSON.stringify(pkg))\n\n    pm2.delete('all', () => done())\n  })\n\n  it('should start app and find version', function(done) {\n    pm2.start('./version-test/index.js', (err) => {\n      pm2.list(function(err, apps) {\n        should(err).be.null()\n        var real_version = JSON.parse(fs.readFileSync(path.join(__dirname, 'fixtures/version-test/package.json'))).version\n        should(apps[0].pm2_env.version).equal(real_version)\n        done()\n      })\n    })\n  })\n\n  var origin_version\n  it('should update version', function(done) {\n    var old = JSON.parse(fs.readFileSync(pkg_path))\n    origin_version = old.version\n    old.version = '2.0.0'\n    fs.writeFileSync(pkg_path, JSON.stringify(old))\n    pm2.restart('all', function() {\n      setTimeout(() => {\n        pm2.list((err, list) => {\n          should(list[0].pm2_env.version).equal('2.0.0')\n          done()\n        })\n      }, 400)\n    })\n  })\n\n  it('should restore version', function(done) {\n    var old = JSON.parse(fs.readFileSync(pkg_path))\n    old.version = origin_version\n    fs.writeFileSync(pkg_path, JSON.stringify(old))\n\n    pm2.restart('all', function() {\n      pm2.list((err, list) => {\n        should(list[0].pm2_env.version).equal(origin_version)\n        done()\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "test/programmatic/watcher.js",
    "content": "var should = require('should');\nvar p = require('path');\nvar fs = require('fs')\nvar EventEmitter = require('events').EventEmitter\nvar PM2  = require('../..');\nvar extend = require('util')._extend\n\nvar cwd = __dirname + '/../fixtures/watcher';\n\nvar paths = {\n  server : p.join(cwd, 'server-watch.js'),\n  bak    : p.join(cwd, 'server-watch.bak.js'),\n  json   : p.join(cwd, 'server-watch.json')\n};\n\nvar ee = new EventEmitter()\n\nvar json = {\n  name  : 'server-watch',\n  script: './server-watch.js',\n  cwd   : cwd\n}\n\nfunction testPM2Env(event) {\n  return function(obj, cb) {\n    ee.once(event, function(e) {\n      if(typeof obj == 'function') {\n        return obj(e)\n      }\n\n      var value\n\n      for(var key in obj) {\n        value = obj[key]\n        console.log('Testing %s for value %s', key, value)\n        should(e[key]).eql(value)\n      }\n\n      return cb()\n    })\n  }\n}\n\nfunction errShouldBeNull(err) {\n  should(err).be.null();\n}\n\ndescribe('Watcher', function() {\n  var pm2 = new PM2.custom({\n    cwd : __dirname + '/../fixtures/watcher'\n  });\n\n  after(function(cb) {\n    pm2.destroy(function() {\n      fs.unlink(paths.server, cb)\n    });\n  });\n\n  before(function(cb) {\n    //copy server-watch.bak, we'll add some lines in it\n    fs.readFile(paths.bak, function(err, data) {\n      if(err) {\n        return cb(err)\n      }\n\n      return fs.writeFile(paths.server, data, cb)\n    })\n  })\n\n  before(function(done) {\n    pm2.connect(function() {\n      done();\n    });\n  });\n\n  before(function(done) {\n    pm2.launchBus(function(err, bus) {\n      should(err).be.null\n\n      bus.on('process:event', function(e) {\n        var name = e.process.name + ':' + e.event\n        console.log('Bus receiving: ' + name)\n        delete e.process.ENV\n        ee.emit(name, e.process)\n      })\n\n      return done()\n    })\n  })\n\n  it('should be watching', function(cb) {\n    testPM2Env('server-watch:online')({watch: true}, cb)\n\n    var json_app = extend(json, {watch: true});\n    pm2.start(json_app, errShouldBeNull)\n  })\n\n  it('should be watching after restart', function(cb) {\n    testPM2Env('server-watch:online')({watch: true}, cb)\n    pm2.restart('server-watch', errShouldBeNull)\n  })\n\n  it('should restart because of file edit', function(cb) {\n    testPM2Env('server-watch:online')({restart_time: 2}, cb)\n    fs.appendFileSync(paths.server, 'console.log(\"edit\")')\n  })\n\n  it('should stop watching', function(cb) {\n    process.argv.push('--watch')\n    testPM2Env('server-watch:stop')({watch: false}, function() {\n      process.argv.splice(process.argv.indexOf('--watch'), 1)\n      cb()\n    })\n    pm2.stop('server-watch', errShouldBeNull)\n\n    // this would be better:\n    // pm2.actionFromJson('stopProcessId', extend(json, {watch: false}), errShouldBeNull)\n    // or :\n    // pm2.stop('server-watch', {watch: false}, errShouldBeNull)\n  })\n\n  it('should not watch', function(cb) {\n    testPM2Env('server-watch:online')({watch: false}, cb)\n    pm2.restart(extend(json, {watch: false}), errShouldBeNull)\n  })\n\n  it('should watch', function(cb) {\n    testPM2Env('server-watch:online')({restart_time: 3, watch: true}, cb)\n    pm2.restart(extend(json, {watch: true}), errShouldBeNull)\n  })\n\n  it('should delete process', function(cb) {\n    pm2.delete('server-watch', cb)\n  })\n\n  it('should watch json', function(cb) {\n    testPM2Env('server-watch:online')(function() {\n      cb()\n    })\n\n    var json_app = paths.json;\n    pm2.start(json_app, errShouldBeNull)\n  })\n\n  it('should restart json from file touch', function(cb) {\n    testPM2Env('server-watch:online')({restart_time: 1}, cb)\n\n    var path = p.join(cwd, 'donotwatchme.dir', 'test')\n\n    fs.writeFile(path, 'Test', {flag: 'a+'}, function(err) {\n      errShouldBeNull(err)\n    })\n  })\n\n  it('should restart json from file deletion', function(cb) {\n    testPM2Env('server-watch:online')({restart_time: 2}, cb)\n\n    var path = p.join(cwd, 'donotwatchme.dir', 'test')\n\n    fs.unlink(path, function(err) {\n      errShouldBeNull(err)\n    })\n  })\n\n  it('should not restart from ignore_watch', function(cb) {\n\n    var path = p.join(cwd, 'pm2.log')\n\n    fs.writeFile(path, 'Log', {flag: 'a+'}, function(err) {\n      errShouldBeNull(err)\n\n      pm2.describe('server-watch', function(err, d) {\n        should(d[0].pm2_env.restart_time).eql(2)\n        fs.unlinkSync(path)\n        return cb()\n      })\n    })\n  })\n\n  it('should work with watch_delay', function(cb) {\n    testPM2Env('server-watch:online')({watch: true, watch_delay: 4000}, cb);\n    pm2.start(extend(json, {watch: true, watch_delay: 4000}), errShouldBeNull);\n  })\n\n  it('should not crash with watch_delay without watch', function(cb) {\n    testPM2Env('server-watch:online')({watch_delay: 4000}, cb);\n    pm2.start(extend(json, {watch_delay: 4000}), errShouldBeNull);\n  })\n\n  /**\n   * Test #1668\n   */\n  it('should delete from json', function(cb) {\n    testPM2Env('server-watch:exit')(function() {\n      cb()\n    })\n\n    pm2.delete(paths.json, errShouldBeNull)\n  })\n})\n"
  },
  {
    "path": "test/unit.sh",
    "content": "#!/usr/bin/env bash\n\nif command -v bun >/dev/null 2>&1\nthen\n    mocha=\"bunx mocha\"\nelse\n    mocha=\"npx mocha\"\nfi\n\npm2=\"`pwd`/bin/pm2\"\n\nfunction reset {\n    $pm2 uninstall all -s\n    $pm2 link delete -s\n    $pm2 kill -s\n}\n\nfunction runUnitTest {\n    echo \"[~] Starting test $1\"\n    START=$(date +%s)\n    $mocha --exit --bail $1\n    RET=$?\n\n    if [ $RET -ne 0 ];\n    then\n        STR=\"[RETRY] $1 failed and NOW is getting retried\"\n        echo $STR\n        echo $STR >> unit_time\n\n        reset\n        $mocha --bail --exit $1\n        RET=$?\n\n        if [ $RET -ne 0 ];\n        then\n            echo -e \"######## TEST ✘ $1 FAILED TWICE!!\"\n            exit 1\n        fi\n    fi\n\n    reset\n\n    END=$(date +%s)\n    DIFF=$(echo \"$END - $START\" | bc)\n    STR=\"[V] $1 succeeded and took $DIFF seconds\"\n    echo $STR\n    echo $STR >> unit_time\n}\n\nreset\n\ntouch unit_time\n> unit_time\n\nD=test/programmatic\n\n# Abort script at first error\n#set -e\n\nrunUnitTest $D/path_resolution.mocha.js\nrunUnitTest $D/modules.mocha.js\nrunUnitTest $D/instances.mocha.js\nrunUnitTest $D/reload-locker.mocha.js\nrunUnitTest $D/filter_env.mocha.js\nrunUnitTest $D/resurect_state.mocha.js\nrunUnitTest $D/programmatic.js\nrunUnitTest $D/namespace.mocha.js\nrunUnitTest $D/auto_restart.mocha.js\nrunUnitTest $D/containerizer.mocha.js\nrunUnitTest $D/api.mocha.js\nrunUnitTest $D/lazy_api.mocha.js\n#runUnitTest $D/version.mocha.js\nrunUnitTest $D/exp_backoff_restart_delay.mocha.js\nrunUnitTest $D/api.backward.compatibility.mocha.js\nrunUnitTest $D/custom_action.mocha.js\nrunUnitTest $D/logs.js\nrunUnitTest $D/watcher.js\nrunUnitTest $D/max_memory_limit.js\nrunUnitTest $D/cluster.mocha.js\nrunUnitTest $D/graceful.mocha.js\nrunUnitTest $D/inside.mocha.js\nrunUnitTest $D/misc_commands.js\nrunUnitTest $D/signals.js\nrunUnitTest $D/send_data_process.mocha.js\n\nrunUnitTest $D/json_validation.mocha.js\nrunUnitTest $D/env_switching.js\nrunUnitTest $D/configuration.mocha.js\nrunUnitTest $D/id.mocha.js\n\nrunUnitTest $D/god.mocha.js\nrunUnitTest $D/dump.mocha.js\nrunUnitTest $D/common.mocha.js\n\nrunUnitTest $D/issues/json_env_passing_4080.mocha.js\n\nD=test/interface\n\nrunUnitTest $D/bus.spec.mocha.js\nrunUnitTest $D/bus.fork.spec.mocha.js\nrunUnitTest $D/utility.mocha.js\n\necho \"============== unit test finished ==============\"\ncat unit_time\n"
  },
  {
    "path": "types/index.d.ts",
    "content": "// Type definitions for pm2 6.0.8\n// Definitions by: João Portela https://www.github.com/jportela\n\n// Exported Methods\n\n/**\n * Either connects to a running pm2 daemon (“God”) or launches and daemonizes one.\n * Once launched, the pm2 process will keep running after the script exits.\n * @param errback - Called when finished connecting to or launching the pm2 daemon process.\n */\nexport function connect(errback: ErrCallback): void;\n/**\n * Either connects to a running pm2 daemon (“God”) or launches and daemonizes one.\n * Once launched, the pm2 process will keep running after the script exits.\n * @param noDaemonMode - (Default: false) If true is passed for the first argument\n * pm2 will not be run as a daemon and will die when the related script exits.\n * By default, pm2 stays alive after your script exits.\n * If pm2 is already running, your script will link to the existing daemon but will die once your process exits.\n * @param errback - Called when finished connecting to or launching the pm2 daemon process.\n */\nexport function connect(noDaemonMode:boolean, errback: ErrCallback): void;\n\n/**\n * Starts a script that will be managed by pm2.\n * @param options - Options\n * @param errback - An errback called when the script has been started.\n * The proc parameter will be a pm2 process object.\n */\nexport function start(options: StartOptions, errback: ErrProcCallback): void;\n/**\n * Starts a script that will be managed by pm2.\n * @param jsonConfigFile - The path to a JSON file that can contain the same options as the options parameter.\n * @param errback - An errback called when the script has been started.\n * The proc parameter will be a pm2 process object.\n */\nexport function start(jsonConfigFile: string, errback: ErrProcCallback): void;\n/**\n * Starts a script that will be managed by pm2.\n * @param script - The path of the script to run.\n * @param errback - An errback called when the script has been started.\n * The proc parameter will be a pm2 process object.\n */\nexport function start(script: string , errback: ErrProcCallback): void;\n/**\n * Starts a script that will be managed by pm2.\n * @param script - The path of the script to run.\n * @param options - Options\n * @param errback - An errback called when the script has been started.\n * The proc parameter will be a pm2 process object.\n */\nexport function start(script: string, options: StartOptions, errback: ErrProcCallback): void;\n/**\n * Starts a script that will be managed by pm2.\n * @param script - The path of the script to run.\n * @param jsonConfigFile - The path to a JSON file that can contain the same options as the options parameter.\n * @param errback - An errback called when the script has been started.\n * The proc parameter will be a pm2 process object.\n */\nexport function start(script: string, jsonConfigFile: string, errback: ErrProcCallback): void;\n\n/**\n * Disconnects from the pm2 daemon.\n */\nexport function disconnect(): void;\n\n/**\n * Stops a process but leaves the process meta-data in pm2’s list\n * @param process - Can either be the name as given in the pm2.start options,\n * a process id, or the string “all” to indicate that all scripts should be restarted.\n * @param errback - called when the process is stopped\n */\nexport function stop(process: string|number, errback: ErrProcCallback): void;\n\n/**\n * Stops and restarts the process.\n * @param process - Can either be the name as given in the pm2.start options,\n * a process id, or the string “all” to indicate that all scripts should be restarted.\n * @param errback - called when the process is restarted\n */\nexport function restart(process: string|number, errback: ErrProcCallback): void;\n\n/**\n * Stops the process and removes it from pm2’s list.\n * The process will no longer be accessible by its name\n * @param process - Can either be the name as given in the pm2.start options,\n * a process id, or the string “all” to indicate that all scripts should be restarted.\n * @param errback - called when the process is deleted\n */\ndeclare function del(process: string|number, errback: ErrProcCallback): void;\n// have to use this construct because `delete` is a reserved word\nexport {del as delete};\n\n/**\n * Zero-downtime rolling restart. At least one process will be kept running at\n * all times as each instance is restarted individually.\n * Only works for scripts started in cluster mode.\n * @param process - Can either be the name as given in the pm2.start options,\n * a process id, or the string “all” to indicate that all scripts should be restarted.\n * @param errback - called when the process is reloaded\n */\nexport function reload(process: string|number, errback: ErrProcCallback): void;\n\n/**\n * Zero-downtime rolling restart. At least one process will be kept running at\n * all times as each instance is restarted individually.\n * Only works for scripts started in cluster mode.\n * @param process - Can either be the name as given in the pm2.start options,\n * a process id, or the string “all” to indicate that all scripts should be restarted.\n * @param options - An object containing configuration\n * @param options.updateEnv - (Default: false) If true is passed in, pm2 will reload it’s\n * environment from process.env before reloading your process.\n * @param errback - called when the process is reloaded\n */\nexport function reload(process: string|number, options: ReloadOptions, errback: ErrProcCallback): void;\n\n/**\n * Kills the pm2 daemon (same as pm2 kill). Note that when the daemon is killed, all its\n * processes are also killed. Also note that you still have to explicitly disconnect\n * from the daemon even after you kill it.\n * @param errback\n */\nexport function killDaemon(errback: ErrProcDescCallback): void;\n\n/**\n * Returns various information about a process: eg what stdout/stderr and pid files are used.\n * @param process - Can either be the name as given in the pm2.start options,\n * a process id, or the string “all” to indicate that all scripts should be restarted.\n * @param errback\n */\nexport function describe(process: string|number, errback: ErrProcDescsCallback): void;\n\n/**\n * Gets the list of running processes being managed by pm2.\n * @param errback\n */\nexport function list(errback: ErrProcDescsCallback): void;\n\n/**\n * Writes the process list to a json file at the path in the DUMP_FILE_PATH environment variable\n * (“~/.pm2/dump.pm2” by default).\n * @param errback\n */\nexport function dump(errback: ErrResultCallback): void;\n\n/**\n * Flushes the logs.\n * @param process - Can either be the name as given in the pm2.start options,\n * a process id, or the string “all” to indicate that all scripts should be restarted.\n * @param errback\n */\nexport function flush(process: number|string, errback: ErrResultCallback): void;\n\n/**\n * @param errback\n */\nexport function dump(errback: ErrResultCallback): void;\n\n/**\n * Rotates the log files. The new log file will have a higher number\n * in it (the default format being ${process.name}-${out|err}-${number}.log).\n * @param errback\n */\nexport function reloadLogs(errback: ErrResultCallback): void;\n\n/**\n * Opens a message bus.\n * @param errback The bus will be an Axon Sub Emitter object used to listen to and send events.\n */\nexport function launchBus(errback: ErrBusCallback): void;\n\n/**\n * @param signal\n * @param process - Can either be the name as given in the pm2.start options,\n * a process id, or the string “all” to indicate that all scripts should be restarted.\n * @param errback\n */\nexport function sendSignalToProcessName(signal:string|number, process: number|string, errback: ErrResultCallback): void;\n\n/**\n * - Registers the script as a process that will start on machine boot. The current process list will be dumped and saved for resurrection on reboot.\n * @param platform\n * @param errback\n */\nexport function startup(platform: Platform, errback: ErrResultCallback): void;\n\n/**\n * - Send an set of data as object to a specific process\n * @param proc_id\n * @param packet\n * @param cb\n */\nexport function sendDataToProcessId(proc_id: number, packet: object, cb: ErrResultCallback): void;\n\n/**\n * - Send an set of data as object to a specific process\n * @param packet {id: number, type: 'process:msg', topic: true, data: object}\n */\nexport function sendDataToProcessId(packet: {id: number, type: 'process:msg', topic: true, data: object}): void;\n\n/**\n * Launch system monitoring (CPU, Memory usage)\n * @param errback - Called when monitoring is launched\n */\nexport function launchSysMonitoring(errback?: ErrCallback): void;\n\n/**\n * Profile CPU or Memory usage\n * @param type - 'cpu' for CPU profiling, 'mem' for memory profiling\n * @param time - Duration in seconds (default: 10)\n * @param errback - Called when profiling is complete\n */\nexport function profile(type: 'cpu' | 'mem', time?: number, errback?: ErrCallback): void;\n\n/**\n * Get process environment variables\n * @param app_id - Process name or id\n * @param errback - Called with environment variables\n */\nexport function env(app_id: string | number, errback?: ErrCallback): void;\n\n/**\n * Get process PID\n * @param app_name - Process name (optional, returns all PIDs if not provided)\n * @param errback - Called with PID information\n */\nexport function getPID(app_name?: string, errback?: ErrProcCallback): void;\n\n/**\n * Trigger a custom action on a process\n * @param pm_id - Process id\n * @param action_name - Name of the action to trigger\n * @param params - Parameters to pass to the action\n * @param errback - Called when action completes\n */\nexport function trigger(pm_id: string | number, action_name: string, params?: any, errback?: ErrCallback): void;\n\n/**\n * Inspect a process (debugging)\n * @param app_name - Process name\n * @param errback - Called with inspect information\n */\nexport function inspect(app_name: string, errback?: ErrCallback): void;\n\n/**\n * Serve static files\n * @param path - Path to serve files from\n * @param port - Port number (default: 8080)\n * @param options - Serve options\n * @param errback - Called when server starts\n */\nexport function serve(path?: string, port?: number, options?: ServeOptions, errback?: ErrCallback): void;\n\n/**\n * Install a PM2 module\n * @param module_name - Name of the module to install\n * @param options - Installation options\n * @param errback - Called when installation completes\n */\nexport function install(module_name: string, options?: InstallOptions, errback?: ErrCallback): void;\n\n/**\n * Uninstall a PM2 module\n * @param module_name - Name of the module to uninstall\n * @param errback - Called when uninstallation completes\n */\nexport function uninstall(module_name: string, errback?: ErrCallback): void;\n\n/**\n * Send line to process stdin\n * @param pm_id - Process id\n * @param line - Line to send\n * @param separator - Line separator (default: '\\n')\n * @param errback - Called when line is sent\n */\nexport function sendLineToStdin(pm_id: string | number, line: string, separator?: string, errback?: ErrCallback): void;\n\n/**\n * Attach to process logs\n * @param pm_id - Process id\n * @param separator - Log separator\n * @param errback - Called when attached\n */\nexport function attach(pm_id: string | number, separator?: string, errback?: ErrCallback): void;\n\n/**\n * Get PM2 configuration value\n * @param key - Configuration key (optional, returns all config if not provided)\n * @param errback - Called with configuration value\n */\nexport function get(key?: string, errback?: ErrCallback): void;\n\n/**\n * Set PM2 configuration value\n * @param key - Configuration key\n * @param value - Configuration value\n * @param errback - Called when value is set\n */\nexport function set(key: string, value: any, errback?: ErrCallback): void;\n\n/**\n * Set multiple PM2 configuration values\n * @param values - Configuration values as string\n * @param errback - Called when values are set\n */\nexport function multiset(values: string, errback?: ErrCallback): void;\n\n/**\n * Unset PM2 configuration value\n * @param key - Configuration key to unset\n * @param errback - Called when value is unset\n */\nexport function unset(key: string, errback?: ErrCallback): void;\n\n// Interfaces\n\nexport interface Proc {\n  name?: string;\n  vizion?: boolean;\n  autorestart?: boolean;\n  exec_mode?: string;\n  exec_interpreter?: string;\n  pm_exec_path?: string;\n  pm_cwd?: string;\n  instances?: number;\n  node_args?: string[];\n  pm_out_log_path?: string;\n  pm_err_log_path?: string;\n  pm_pid_path?: string;\n  status?: string;\n  pm_uptime?: number;\n  axm_actions?: any[];\n  axm_monitor?: any;\n  axm_dynamic?: any;\n  vizion_running?: boolean;\n  created_at?: number;\n  pm_id?: number;\n  restart_time?: number;\n  unstable_restarts?: number;\n  started_inside?: boolean;\n  command?: Command;\n  versioning?: any;\n  exit_code?: number;\n}\n\nexport interface Command {\n  locked?: boolean;\n  metadata?: any;\n  started_at?: any;\n  finished_at?: any;\n  error?: any;\n}\n\n/**\n * An object with information about the process.\n */\nexport interface ProcessDescription {\n  /**\n   * The name given in the original start command.\n   */\n  name?: string;\n  /**\n   * The pid of the process.\n   */\n  pid?: number;\n  /**\n   * The pid for the pm2 God daemon process.\n   */\n  pm_id?: number;\n  monit?: Monit;\n  /**\n   * The list of path variables in the process’s environment\n   */\n  pm2_env?: Pm2Env;\n}\n\ninterface Monit {\n  /**\n   * The number of bytes the process is using.\n   */\n  memory?: number;\n  /**\n   * The percent of CPU being used by the process at the moment.\n   */\n  cpu?: number;\n}\n\n/**\n * The list of path variables in the process’s environment\n */\ninterface Pm2Env {\n  /**\n   * The working directory of the process.\n   */\n  pm_cwd?: string;\n  /**\n   * The stdout log file path.\n   */\n  pm_out_log_path?: string;\n  /**\n   * The stderr log file path.\n   */\n  pm_err_log_path?: string;\n  /**\n   * The interpreter used.\n   */\n  exec_interpreter?: string;\n  /**\n   * The uptime of the process.\n   */\n  pm_uptime?: number;\n  /**\n   * The number of unstable restarts the process has been through.\n   */\n  unstable_restarts?: number;\n  restart_time?: number;\n  status?: ProcessStatus;\n  /**\n   * The number of running instances.\n   */\n  instances?: number | 'max';\n  /**\n   * The path of the script being run in this process.\n   */\n  pm_exec_path?: string;\n}\n\nexport interface StartOptions {\n  /**\n   * Enable or disable auto start after process added (default: true).\n   */\n  autostart?: boolean;\n  /**\n   * Enable or disable auto restart after process failure (default: true).\n   */\n  autorestart?: boolean;\n  /**\n   * List of exit codes that should allow the process to stop (skip autorestart).\n   */\n  stop_exit_codes?: number[];\n  /**\n   * An arbitrary name that can be used to interact with (e.g. restart) the process\n   * later in other commands. Defaults to the script name without its extension\n   * (eg “testScript” for “testScript.js”)\n   */\n  name?: string;\n  /**\n   * The path of the script to run\n   */\n  script?: string;\n  /**\n   * A string or array of strings composed of arguments to pass to the script.\n   */\n  args?: string | string[];\n  /**\n   * A string or array of strings composed of arguments to call the interpreter process with.\n   * Eg “–harmony” or [”–harmony”,”–debug”]. Only applies if interpreter is something other\n   * than “none” (its “node” by default).\n   */\n  interpreter_args?: string | string[];\n  /**\n   * The working directory to start the process with.\n   */\n  cwd?: string;\n  /**\n   * (Default: “~/.pm2/logs/app_name-out.log”) The path to a file to append stdout output to.\n   * Can be the same file as error.\n   */\n  output?: string;\n  /**\n   * (Default: “~/.pm2/logs/app_name-error.err”) The path to a file to append stderr output to. Can be the same file as output.\n   */\n  error?: string;\n  /**\n   * The display format for log timestamps (eg “YYYY-MM-DD HH:mm Z”). The format is a moment display format.\n   */\n  log_date_format?: string;\n  /**\n   * Default: “~/.pm2/logs/~/.pm2/pids/app_name-id.pid”)\n   * The path to a file to write the pid of the started process. The file will be overwritten.\n   * Note that the file is not used in any way by pm2 and so the user is free to manipulate or\n   * remove that file at any time. The file will be deleted when the process is stopped or the daemon killed.\n   */\n  pid?: string;\n  /**\n   * The minimum uptime of the script before it’s considered successfully started.\n   */\n  min_uptime?: number;\n  /**\n   * The maximum number of times in a row a script will be restarted if it exits in less than min_uptime.\n   */\n  max_restarts?: number;\n  /**\n   * If sets and script’s memory usage goes about the configured number, pm2 restarts the script.\n   * Uses human-friendly suffixes: ‘K’ for kilobytes, ‘M’ for megabytes, ‘G’ for gigabytes’, etc. Eg “150M”.\n   */\n  max_memory_restart?: number | string;\n  /**\n   * Arguments to pass to the interpreter\n   */\n  node_args?: string | string[];\n  /**\n   * Prefix logs with time\n   */\n  time?: boolean;\n  /**\n   * This will make PM2 listen for that event. In your application you will need to add process.send('ready');\n   * when you want your application to be considered as ready.\n   */\n  wait_ready?: boolean;\n  /**\n   * (Default: 1600)\n   * The number of milliseconds to wait after a stop or restart command issues a SIGINT signal to kill the\n   * script forceably with a SIGKILL signal.\n   */\n  kill_timeout?: number;\n  /**\n   * (Default: 0) Number of millseconds to wait before restarting a script that has exited.\n   */\n  restart_delay?: number;\n  /**\n   * (Default: “node”) The interpreter for your script (eg “python”, “ruby”, “bash”, etc).\n   * The value “none” will execute the ‘script’ as a binary executable.\n   */\n  interpreter?: string;\n  /**\n   * (Default: ‘fork’) If sets to ‘cluster’, will enable clustering\n   * (running multiple instances of the script).\n   */\n  exec_mode?: string;\n  /**\n   * (Default: 1) How many instances of script to create. Only relevant in exec_mode ‘cluster’.\n   */\n  instances?: number;\n  /**\n   * (Default: false) If true, merges the log files for all instances of script into one stderr log\n   * and one stdout log. Only applies in ‘cluster’ mode. For example, if you have 4 instances of\n   * ‘test.js’ started via pm2, normally you would have 4 stdout log files and 4 stderr log files,\n   * but with this option set to true you would only have one stdout file and one stderr file.\n   */\n  merge_logs?: boolean;\n  /**\n   * If set to true, the application will be restarted on change of the script file.\n   */\n  watch?: boolean|string[];\n  /**\n   * (Default: false) By default, pm2 will only start a script if that script isn’t\n   * already running (a script is a path to an application, not the name of an application\n   * already running). If force is set to true, pm2 will start a new instance of that script.\n   */\n  force?: boolean;\n  ignore_watch?: string[];\n  cron?: any;\n  execute_command?: any;\n  write?: any;\n  source_map_support?: any;\n  disable_source_map_support?: any;\n  /**\n   * The environment variables to pass on to the process.\n   */\n  env?: { [key: string]: string; };\n  /**\n   * NameSpace for the process\n   * @default 'default'\n   * @example 'production'\n   * @example 'development'\n   * @example 'staging'\n   */\n  namespace?: string;\n  /**\n   * (Default: false) Exponential backoff restart delay in milliseconds.\n   * When enabled, PM2 will progressively increase restart delays after failures.\n   */\n  exp_backoff_restart_delay?: number;\n  /**\n   * Timeout for application to be ready after reload (in milliseconds).\n   */\n  listen_timeout?: number;\n  /**\n   * (Default: false) If true, shutdown the process using process.send('shutdown') instead of process.kill().\n   */\n  shutdown_with_message?: boolean;\n  /**\n   * Environment variable name that gets incremented for each cluster instance.\n   */\n  increment_var?: string;\n  /**\n   * Name of the environment variable holding the instance ID.\n   * @default 'NODE_APP_INSTANCE'\n   */\n  instance_var?: string;\n  /**\n   * Filter out specific environment variables from the process.\n   * Can be true to filter all, or array/string of specific variables.\n   */\n  filter_env?: boolean | string | string[];\n  /**\n   * (Default: false) Disable logs output.\n   */\n  disable_logs?: boolean;\n  /**\n   * Log output type.\n   */\n  log_type?: string;\n  /**\n   * (Default: false) Enable container mode.\n   */\n  container?: boolean;\n  /**\n   * (Default: false) Distribution mode for Docker.\n   */\n  dist?: boolean;\n  /**\n   * Docker image name.\n   */\n  image_name?: string;\n  /**\n   * Node.js version for Docker container.\n   */\n  node_version?: string;\n  /**\n   * (Default: false) Fresh install for Docker.\n   */\n  fresh?: boolean;\n  /**\n   * (Default: false) Docker daemon mode.\n   */\n  dockerdaemon?: boolean;\n}\n\ninterface ReloadOptions {\n  /**\n   * (Default: false) If true is passed in, pm2 will reload it's environment from process.env\n   * before reloading your process.\n   */\n  updateEnv?: boolean;\n}\n\n/**\n * Options for serving static files\n */\nexport interface ServeOptions {\n  /**\n   * (Default: false) Single Page Application mode\n   */\n  spa?: boolean;\n  /**\n   * Basic authentication username\n   */\n  basic_auth_username?: string;\n  /**\n   * Basic authentication password\n   */\n  basic_auth_password?: string;\n  /**\n   * Monitor URL path\n   */\n  monitor?: string;\n}\n\n/**\n * Options for Docker operations\n */\nexport interface DockerOptions {\n  /**\n   * Docker image name\n   */\n  imageName?: string;\n  /**\n   * Node.js version to use\n   */\n  nodeVersion?: string;\n  /**\n   * (Default: false) Fresh installation\n   */\n  fresh?: boolean;\n  /**\n   * (Default: false) Force operation\n   */\n  force?: boolean;\n  /**\n   * (Default: false) Docker daemon mode\n   */\n  dockerdaemon?: boolean;\n}\n\n/**\n * Options for module installation\n */\nexport interface InstallOptions {\n  /**\n   * (Default: false) Install from tarball\n   */\n  tarball?: boolean;\n  /**\n   * (Default: true) Perform installation\n   */\n  install?: boolean;\n  /**\n   * (Default: false) Docker mode\n   */\n  docker?: boolean;\n  /**\n   * (Default: false) Use v1 API\n   */\n  v1?: boolean;\n  /**\n   * (Default: false) Safe mode installation\n   */\n  safe?: boolean | number;\n}\n\n// Types\n\ntype ProcessStatus = 'online' | 'stopping' | 'stopped' | 'launching' | 'errored' | 'one-launch-status' | 'waiting_restart';\ntype Platform = 'ubuntu' | 'centos' | 'redhat' | 'gentoo' | 'systemd' | 'darwin' | 'amazon';\n\ntype ErrCallback = (err: Error) => void;\ntype ErrProcCallback = (err: Error, proc: Proc) => void;\ntype ErrProcDescCallback = (err: Error, processDescription: ProcessDescription) => void;\ntype ErrProcDescsCallback = (err: Error, processDescriptionList: ProcessDescription[]) => void;\ntype ErrResultCallback = (err: Error, result: any) => void;\ntype ErrBusCallback = (err: Error, bus: any) => void;\n"
  },
  {
    "path": "types/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n      \"module\": \"commonjs\",\n      \"lib\": [\"es6\"],\n      \"noImplicitAny\": true,\n      \"noImplicitThis\": true,\n      \"strictNullChecks\": true,\n\n      // If the library is an external module (uses `export`), this allows your test file to import \"mylib\" instead of \"./index\".\n      // If the library is global (cannot be imported via `import` or `require`), leave this out.\n      \"baseUrl\": \".\",\n      \"paths\": { \"mylib\": [\".\"] }\n  }\n}\n"
  }
]