PNG  IHDRQgAMA a cHRMz&u0`:pQ<bKGDgmIDATxwUﹻ& ^CX(J I@ "% (** BX +*i"]j(IH{~R)[~>h{}gy)I$Ij .I$I$ʊy@}x.: $I$Ii}VZPC)I$IF ^0ʐJ$I$Q^}{"r=OzI$gRZeC.IOvH eKX $IMpxsk.쒷/&r[޳<v| .I~)@$updYRa$I |M.e JaֶpSYR6j>h%IRز if&uJ)M$I vLi=H;7UJ,],X$I1AҒJ$ XY XzI@GNҥRT)E@;]K*Mw;#5_wOn~\ DC&$(A5 RRFkvIR}l!RytRl;~^ǷJj اy뷦BZJr&ӥ8Pjw~vnv X^(I;4R=P[3]J,]ȏ~:3?[ a&e)`e*P[4]T=Cq6R[ ~ޤrXR Հg(t_HZ-Hg M$ãmL5R uk*`%C-E6/%[t X.{8P9Z.vkXŐKjgKZHg(aK9ڦmKjѺm_ \#$5,)-  61eJ,5m| r'= &ڡd%-]J on Xm|{ RҞe $eڧY XYrԮ-a7RK6h>n$5AVڴi*ֆK)mѦtmr1p| q:흺,)Oi*ֺK)ܬ֦K-5r3>0ԔHjJئEZj,%re~/z%jVMڸmrt)3]J,T K֦OvԒgii*bKiNO~%PW0=dii2tJ9Jݕ{7"I P9JKTbu,%r"6RKU}Ij2HKZXJ,妝 XYrP ެ24c%i^IK|.H,%rb:XRl1X4Pe/`x&P8Pj28Mzsx2r\zRPz4J}yP[g=L) .Q[6RjWgp FIH*-`IMRaK9TXcq*I y[jE>cw%gLRԕiFCj-ďa`#e~I j,%r,)?[gp FI˨mnWX#>mʔ XA DZf9,nKҲzIZXJ,L#kiPz4JZF,I,`61%2s $,VOϚ2/UFJfy7K> X+6 STXIeJILzMfKm LRaK9%|4p9LwJI!`NsiazĔ)%- XMq>pk$-$Q2x#N ؎-QR}ᶦHZډ)J,l#i@yn3LN`;nڔ XuX5pF)m|^0(>BHF9(cզEerJI rg7 4I@z0\JIi䵙RR0s;$s6eJ,`n 䂦0a)S)A 1eJ,堌#635RIgpNHuTH_SԕqVe ` &S)>p;S$魁eKIuX`I4춒o}`m$1":PI<[v9^\pTJjriRŭ P{#{R2,`)e-`mgj~1ϣLKam7&U\j/3mJ,`F;M'䱀 .KR#)yhTq;pcK9(q!w?uRR,n.yw*UXj#\]ɱ(qv2=RqfB#iJmmL<]Y͙#$5 uTU7ӦXR+q,`I}qL'`6Kͷ6r,]0S$- [RKR3oiRE|nӦXR.(i:LDLTJjY%o:)6rxzҒqTJjh㞦I.$YR.ʼnGZ\ֿf:%55 I˼!6dKxm4E"mG_ s? .e*?LRfK9%q#uh$)i3ULRfK9yxm܌bj84$i1U^@Wbm4uJ,ҪA>_Ij?1v32[gLRD96oTaR׿N7%L2 NT,`)7&ƝL*꽙yp_$M2#AS,`)7$rkTA29_Iye"|/0t)$n XT2`YJ;6Jx".e<`$) PI$5V4]29SRI>~=@j]lp2`K9Jaai^" Ԋ29ORI%:XV5]JmN9]H;1UC39NI%Xe78t)a;Oi Ҙ>Xt"~G>_mn:%|~ޅ_+]$o)@ǀ{hgN;IK6G&rp)T2i୦KJuv*T=TOSV>(~D>dm,I*Ɛ:R#ۙNI%D>G.n$o;+#RR!.eU˽TRI28t)1LWϚ>IJa3oFbu&:tJ*(F7y0ZR ^p'Ii L24x| XRI%ۄ>S1]Jy[zL$adB7.eh4%%누>WETf+3IR:I3Xה)3אOۦSRO'ٺ)S}"qOr[B7ϙ.edG)^ETR"RtRݜh0}LFVӦDB^k_JDj\=LS(Iv─aTeZ%eUAM-0;~˃@i|l @S4y72>sX-vA}ϛBI!ݎߨWl*)3{'Y|iSlEڻ(5KtSI$Uv02,~ԩ~x;P4ցCrO%tyn425:KMlD ^4JRxSهF_}شJTS6uj+ﷸk$eZO%G*^V2u3EMj3k%)okI]dT)URKDS 7~m@TJR~荪fT"֛L \sM -0T KfJz+nإKr L&j()[E&I ߴ>e FW_kJR|!O:5/2跌3T-'|zX ryp0JS ~^F>-2< `*%ZFP)bSn"L :)+pʷf(pO3TMW$~>@~ū:TAIsV1}S2<%ޟM?@iT ,Eūoz%i~g|`wS(]oȤ8)$ ntu`өe`6yPl IzMI{ʣzʨ )IZ2= ld:5+請M$-ї;U>_gsY$ÁN5WzWfIZ)-yuXIfp~S*IZdt;t>KūKR|$#LcԀ+2\;kJ`]YǔM1B)UbG"IRߊ<xܾӔJ0Z='Y嵤 Leveg)$znV-º^3Ւof#0Tfk^Zs[*I꯳3{)ˬW4Ւ4 OdpbZRS|*I 55#"&-IvT&/윚Ye:i$ 9{LkuRe[I~_\ؠ%>GL$iY8 9ܕ"S`kS.IlC;Ҏ4x&>u_0JLr<J2(^$5L s=MgV ~,Iju> 7r2)^=G$1:3G< `J3~&IR% 6Tx/rIj3O< ʔ&#f_yXJiގNSz; Tx(i8%#4 ~AS+IjerIUrIj362v885+IjAhK__5X%nV%Iͳ-y|7XV2v4fzo_68"S/I-qbf; LkF)KSM$ Ms>K WNV}^`-큧32ŒVؙGdu,^^m%6~Nn&͓3ŒVZMsRpfEW%IwdǀLm[7W&bIRL@Q|)* i ImsIMmKmyV`i$G+R 0tV'!V)֏28vU7͒vHꦼtxꗞT ;S}7Mf+fIRHNZUkUx5SAJㄌ9MqμAIRi|j5)o*^'<$TwI1hEU^c_j?Е$%d`z cyf,XO IJnTgA UXRD }{H}^S,P5V2\Xx`pZ|Yk:$e ~ @nWL.j+ϝYb퇪bZ BVu)u/IJ_ 1[p.p60bC >|X91P:N\!5qUB}5a5ja `ubcVxYt1N0Zzl4]7­gKj]?4ϻ *[bg$)+À*x쳀ogO$~,5 زUS9 lq3+5mgw@np1sso Ӻ=|N6 /g(Wv7U;zωM=wk,0uTg_`_P`uz?2yI!b`kĸSo+Qx%!\οe|އԁKS-s6pu_(ֿ$i++T8=eY; צP+phxWQv*|p1. ά. XRkIQYP,drZ | B%wP|S5`~́@i޾ E;Չaw{o'Q?%iL{u D?N1BD!owPHReFZ* k_-~{E9b-~P`fE{AܶBJAFO wx6Rox5 K5=WwehS8 (JClJ~ p+Fi;ŗo+:bD#g(C"wA^ r.F8L;dzdIHUX݆ϞXg )IFqem%I4dj&ppT{'{HOx( Rk6^C٫O.)3:s(۳(Z?~ٻ89zmT"PLtw䥈5&b<8GZ-Y&K?e8,`I6e(֍xb83 `rzXj)F=l($Ij 2*(F?h(/9ik:I`m#p3MgLaKjc/U#n5S# m(^)=y=đx8ŬI[U]~SцA4p$-F i(R,7Cx;X=cI>{Km\ o(Tv2vx2qiiDJN,Ҏ!1f 5quBj1!8 rDFd(!WQl,gSkL1Bxg''՞^ǘ;pQ P(c_ IRujg(Wz bs#P­rz> k c&nB=q+ؔXn#r5)co*Ũ+G?7< |PQӣ'G`uOd>%Mctz# Ԫڞ&7CaQ~N'-P.W`Oedp03C!IZcIAMPUۀ5J<\u~+{9(FbbyAeBhOSܳ1 bÈT#ŠyDžs,`5}DC-`̞%r&ڙa87QWWp6e7 Rϫ/oY ꇅ Nܶըtc!LA T7V4Jsū I-0Pxz7QNF_iZgúWkG83 0eWr9 X]㾮݁#Jˢ C}0=3ݱtBi]_ &{{[/o[~ \q鯜00٩|cD3=4B_b RYb$óBRsf&lLX#M*C_L܄:gx)WΘsGSbuL rF$9';\4Ɍq'n[%p.Q`u hNb`eCQyQ|l_C>Lb꟟3hSb #xNxSs^ 88|Mz)}:](vbۢamŖ࿥ 0)Q7@0=?^k(*J}3ibkFn HjB׻NO z x}7p 0tfDX.lwgȔhԾŲ }6g E |LkLZteu+=q\Iv0쮑)QٵpH8/2?Σo>Jvppho~f>%bMM}\//":PTc(v9v!gոQ )UfVG+! 35{=x\2+ki,y$~A1iC6#)vC5^>+gǵ@1Hy٪7u;p psϰu/S <aʸGu'tD1ԝI<pg|6j'p:tպhX{o(7v],*}6a_ wXRk,O]Lܳ~Vo45rp"N5k;m{rZbΦ${#)`(Ŵg,;j%6j.pyYT?}-kBDc3qA`NWQū20/^AZW%NQ MI.X#P#,^Ebc&?XR tAV|Y.1!؅⨉ccww>ivl(JT~ u`ٵDm q)+Ri x/x8cyFO!/*!/&,7<.N,YDŽ&ܑQF1Bz)FPʛ?5d 6`kQձ λc؎%582Y&nD_$Je4>a?! ͨ|ȎWZSsv8 j(I&yj Jb5m?HWp=g}G3#|I,5v珿] H~R3@B[☉9Ox~oMy=J;xUVoj bUsl_35t-(ՃɼRB7U!qc+x4H_Qo֮$[GO<4`&č\GOc[.[*Af%mG/ ňM/r W/Nw~B1U3J?P&Y )`ѓZ1p]^l“W#)lWZilUQu`-m|xĐ,_ƪ|9i:_{*(3Gѧ}UoD+>m_?VPۅ15&}2|/pIOʵ> GZ9cmíتmnz)yߐbD >e}:) r|@R5qVSA10C%E_'^8cR7O;6[eKePGϦX7jb}OTGO^jn*媓7nGMC t,k31Rb (vyܴʭ!iTh8~ZYZp(qsRL ?b}cŨʊGO^!rPJO15MJ[c&~Z`"ѓޔH1C&^|Ш|rʼ,AwĴ?b5)tLU)F| &g٣O]oqSUjy(x<Ϳ3 .FSkoYg2 \_#wj{u'rQ>o;%n|F*O_L"e9umDds?.fuuQbIWz |4\0 sb;OvxOSs; G%T4gFRurj(֍ڑb uԖKDu1MK{1^ q; C=6\8FR艇!%\YÔU| 88m)֓NcLve C6z;o&X x59:q61Z(T7>C?gcļxѐ Z oo-08jہ x,`' ҔOcRlf~`jj".Nv+sM_]Zk g( UOPyεx%pUh2(@il0ݽQXxppx-NS( WO+轾 nFߢ3M<;z)FBZjciu/QoF 7R¥ ZFLF~#ȣߨ^<쩡ݛкvџ))ME>ώx4m#!-m!L;vv#~Y[đKmx9.[,UFS CVkZ +ߟrY٧IZd/ioi$%͝ب_ֶX3ܫhNU ZZgk=]=bbJS[wjU()*I =ώ:}-蹞lUj:1}MWm=̛ _ ¾,8{__m{_PVK^n3esw5ӫh#$-q=A̟> ,^I}P^J$qY~Q[ Xq9{#&T.^GVj__RKpn,b=`żY@^՝;z{paVKkQXj/)y TIc&F;FBG7wg ZZDG!x r_tƢ!}i/V=M/#nB8 XxЫ ^@CR<{䤭YCN)eKOSƟa $&g[i3.C6xrOc8TI;o hH6P&L{@q6[ Gzp^71j(l`J}]e6X☉#͕ ׈$AB1Vjh㭦IRsqFBjwQ_7Xk>y"N=MB0 ,C #o6MRc0|$)ف"1!ixY<B9mx `,tA>)5ػQ?jQ?cn>YZe Tisvh# GMމȇp:ԴVuږ8ɼH]C.5C!UV;F`mbBk LTMvPʍϤj?ԯ/Qr1NB`9s"s TYsz &9S%U԰> {<ؿSMxB|H\3@!U| k']$U+> |HHMLޢ?V9iD!-@x TIî%6Z*9X@HMW#?nN ,oe6?tQwڱ.]-y':mW0#!J82qFjH -`ѓ&M0u Uγmxϵ^-_\])@0Rt.8/?ٰCY]x}=sD3ojަЫNuS%U}ԤwHH>ڗjܷ_3gN q7[q2la*ArǓԖ+p8/RGM ]jacd(JhWko6ڎbj]i5Bj3+3!\j1UZLsLTv8HHmup<>gKMJj0@H%,W΃7R) ">c, xixј^ aܖ>H[i.UIHc U1=yW\=S*GR~)AF=`&2h`DzT󑓶J+?W+}C%P:|0H܆}-<;OC[~o.$~i}~HQ TvXΈr=b}$vizL4:ȰT|4~*!oXQR6Lk+#t/g lԁߖ[Jڶ_N$k*". xsxX7jRVbAAʯKҎU3)zSNN _'s?f)6X!%ssAkʱ>qƷb hg %n ~p1REGMHH=BJiy[<5 ǁJҖgKR*倳e~HUy)Ag,K)`Vw6bRR:qL#\rclK/$sh*$ 6덤 KԖc 3Z9=Ɣ=o>X Ώ"1 )a`SJJ6k(<c e{%kϊP+SL'TcMJWRm ŏ"w)qc ef꒵i?b7b('"2r%~HUS1\<(`1Wx9=8HY9m:X18bgD1u ~|H;K-Uep,, C1 RV.MR5άh,tWO8WC$ XRVsQS]3GJ|12 [vM :k#~tH30Rf-HYݺ-`I9%lIDTm\ S{]9gOڒMNCV\G*2JRŨ;Rҏ^ڽ̱mq1Eu?To3I)y^#jJw^Ńj^vvlB_⋌P4x>0$c>K†Aļ9s_VjTt0l#m>E-,,x,-W)سo&96RE XR.6bXw+)GAEvL)͞K4$p=Ũi_ѱOjb HY/+@θH9޼]Nԥ%n{ &zjT? Ty) s^ULlb,PiTf^<À] 62R^V7)S!nllS6~͝V}-=%* ʻ>G DnK<y&>LPy7'r=Hj 9V`[c"*^8HpcO8bnU`4JȪAƋ#1_\ XϘHPRgik(~G~0DAA_2p|J묭a2\NCr]M_0 ^T%e#vD^%xy-n}-E\3aS%yN!r_{ )sAw ڼp1pEAk~v<:`'ӭ^5 ArXOI驻T (dk)_\ PuA*BY]yB"l\ey hH*tbK)3 IKZ򹞋XjN n *n>k]X_d!ryBH ]*R 0(#'7 %es9??ښFC,ՁQPjARJ\Ρw K#jahgw;2$l*) %Xq5!U᢯6Re] |0[__64ch&_}iL8KEgҎ7 M/\`|.p,~`a=BR?xܐrQ8K XR2M8f ?`sgWS%" Ԉ 7R%$ N}?QL1|-эټwIZ%pvL3Hk>,ImgW7{E xPHx73RA @RS CC !\ȟ5IXR^ZxHл$Q[ŝ40 (>+ _C >BRt<,TrT {O/H+˟Pl6 I B)/VC<6a2~(XwV4gnXR ϱ5ǀHٻ?tw똤Eyxp{#WK qG%5],(0ӈH HZ])ג=K1j&G(FbM@)%I` XRg ʔ KZG(vP,<`[ Kn^ SJRsAʠ5xՅF`0&RbV tx:EaUE/{fi2;.IAwW8/tTxAGOoN?G}l L(n`Zv?pB8K_gI+ܗ #i?ޙ.) p$utc ~DžfՈEo3l/)I-U?aԅ^jxArA ΧX}DmZ@QLےbTXGd.^|xKHR{|ΕW_h] IJ`[G9{).y) 0X YA1]qp?p_k+J*Y@HI>^?gt.06Rn ,` ?);p pSF9ZXLBJPWjgQ|&)7! HjQt<| ؅W5 x W HIzYoVMGP Hjn`+\(dNW)F+IrS[|/a`K|ͻ0Hj{R,Q=\ (F}\WR)AgSG`IsnAR=|8$}G(vC$)s FBJ?]_u XRvύ6z ŨG[36-T9HzpW̞ú Xg큽=7CufzI$)ki^qk-) 0H*N` QZkk]/tnnsI^Gu't=7$ Z;{8^jB% IItRQS7[ϭ3 $_OQJ`7!]W"W,)Iy W AJA;KWG`IY{8k$I$^%9.^(`N|LJ%@$I}ֽp=FB*xN=gI?Q{٥4B)mw $Igc~dZ@G9K X?7)aK%݅K$IZ-`IpC U6$I\0>!9k} Xa IIS0H$I H ?1R.Чj:4~Rw@p$IrA*u}WjWFPJ$I➓/6#! LӾ+ X36x8J |+L;v$Io4301R20M I$-E}@,pS^ޟR[/s¹'0H$IKyfŸfVOπFT*a$I>He~VY/3R/)>d$I>28`Cjw,n@FU*9ttf$I~<;=/4RD~@ X-ѕzἱI$: ԍR a@b X{+Qxuq$IЛzo /~3\8ڒ4BN7$IҀj V]n18H$IYFBj3̵̚ja pp $Is/3R Ӻ-Yj+L;.0ŔI$Av? #!5"aʄj}UKmɽH$IjCYs?h$IDl843.v}m7UiI=&=0Lg0$I4: embe` eQbm0u? $IT!Sƍ'-sv)s#C0:XB2a w I$zbww{."pPzO =Ɔ\[ o($Iaw]`E).Kvi:L*#gР7[$IyGPI=@R 4yR~̮´cg I$I/<tPͽ hDgo 94Z^k盇΄8I56^W$I^0̜N?4*H`237}g+hxoq)SJ@p|` $I%>-hO0eO>\ԣNߌZD6R=K ~n($I$y3D>o4b#px2$yڪtzW~a $I~?x'BwwpH$IZݑnC㧄Pc_9sO gwJ=l1:mKB>Ab<4Lp$Ib o1ZQ@85b̍ S'F,Fe,^I$IjEdù{l4 8Ys_s Z8.x m"+{~?q,Z D!I$ϻ'|XhB)=…']M>5 rgotԎ 獽PH$IjIPhh)n#cÔqA'ug5qwU&rF|1E%I$%]!'3AFD/;Ck_`9 v!ٴtPV;x`'*bQa w I$Ix5 FC3D_~A_#O݆DvV?<qw+I$I{=Z8".#RIYyjǪ=fDl9%M,a8$I$Ywi[7ݍFe$s1ՋBVA?`]#!oz4zjLJo8$I$%@3jAa4(o ;p,,dya=F9ً[LSPH$IJYЉ+3> 5"39aZ<ñh!{TpBGkj}Sp $IlvF.F$I z< '\K*qq.f<2Y!S"-\I$IYwčjF$ w9 \ߪB.1v!Ʊ?+r:^!I$BϹB H"B;L'G[ 4U#5>੐)|#o0aڱ$I>}k&1`U#V?YsV x>{t1[I~D&(I$I/{H0fw"q"y%4 IXyE~M3 8XψL}qE$I[> nD?~sf ]o΁ cT6"?'_Ἣ $I>~.f|'!N?⟩0G KkXZE]ޡ;/&?k OۘH$IRۀwXӨ<7@PnS04aӶp.:@\IWQJ6sS%I$e5ڑv`3:x';wq_vpgHyXZ 3gЂ7{{EuԹn±}$I$8t;b|591nءQ"P6O5i }iR̈́%Q̄p!I䮢]O{H$IRϻ9s֧ a=`- aB\X0"+5"C1Hb?߮3x3&gşggl_hZ^,`5?ߎvĸ%̀M!OZC2#0x LJ0 Gw$I$I}<{Eb+y;iI,`ܚF:5ܛA8-O-|8K7s|#Z8a&><a&/VtbtLʌI$I$I$I$I$I$IRjDD%tEXtdate:create2022-05-31T04:40:26+00:00!Î%tEXtdate:modify2022-05-31T04:40:26+00:00|{2IENDB`Mini Shell

HOME


Mini Shell 1.0
DIR:/proc/self/root/usr/local/lib/node_modules/pm2/lib/
Upload File :
Current File : //proc/self/root/usr/local/lib/node_modules/pm2/lib/API.js
/**
 * Copyright 2013-2022 the PM2 project authors. All rights reserved.
 * Use of this source code is governed by a license that
 * can be found in the LICENSE file.
 */
'use strict';

const commander   = require('commander');
const fs          = require('fs');
const path        = require('path');
const eachLimit   = require('async/eachLimit');
const series      = require('async/series');
const debug       = require('debug')('pm2:cli');
const util        = require('util');
const chalk       = require('chalk');
const fclone      = require('fclone');

var DockerMgmt  = require('./API/ExtraMgmt/Docker.js')
var conf        = require('../constants.js');
var Client      = require('./Client');
var Common      = require('./Common');
var KMDaemon    = require('@pm2/agent/src/InteractorClient');
var Config      = require('./tools/Config');
var Modularizer = require('./API/Modules/Modularizer.js');
var path_structure = require('../paths.js');
var UX          = require('./API/UX');
var pkg         = require('../package.json');
var hf = require('./API/Modules/flagExt.js');
var Configuration = require('./Configuration.js');
const sexec = require('./tools/sexec.js')

var IMMUTABLE_MSG = chalk.bold.blue('Use --update-env to update environment variables');

/**
 * Main Function to be imported
 * can be aliased to PM2
 *
 * To use it when PM2 is installed as a module:
 *
 * var PM2 = require('pm2');
 *
 * var pm2 = PM2(<opts>);
 *
 *
 * @param {Object}  opts
 * @param {String}  [opts.cwd=<current>]         override pm2 cwd for starting scripts
 * @param {String}  [opts.pm2_home=[<paths.js>]] pm2 directory for log, pids, socket files
 * @param {Boolean} [opts.independent=false]     unique PM2 instance (random pm2_home)
 * @param {Boolean} [opts.daemon_mode=true]      should be called in the same process or not
 * @param {String}  [opts.public_key=null]       pm2 plus bucket public key
 * @param {String}  [opts.secret_key=null]       pm2 plus bucket secret key
 * @param {String}  [opts.machine_name=null]     pm2 plus instance name
 */
class API {

  constructor (opts) {
    if (!opts) opts = {};
    var that = this;

    this.daemon_mode = typeof(opts.daemon_mode) == 'undefined' ? true : opts.daemon_mode;
    this.pm2_home = conf.PM2_ROOT_PATH;
    this.public_key = conf.PUBLIC_KEY || opts.public_key || null;
    this.secret_key = conf.SECRET_KEY || opts.secret_key || null;
    this.machine_name = conf.MACHINE_NAME || opts.machine_name || null

    /**
     * CWD resolution
     */
    this.cwd = process.cwd();
    if (opts.cwd) {
      this.cwd = path.resolve(opts.cwd);
    }

    /**
     * PM2 HOME resolution
     */
    if (opts.pm2_home && opts.independent == true)
      throw new Error('You cannot set a pm2_home and independent instance in same time');

    if (opts.pm2_home) {
      // Override default conf file
      this.pm2_home = opts.pm2_home;
      conf = Object.assign(conf, path_structure(this.pm2_home));
    }
    else if (opts.independent == true && conf.IS_WINDOWS === false) {
      // Create an unique pm2 instance
      const crypto = require('crypto');
      var random_file = crypto.randomBytes(8).toString('hex');
      this.pm2_home = path.join('/tmp', random_file);

      // If we dont explicitly tell to have a daemon
      // It will go as in proc
      if (typeof(opts.daemon_mode) == 'undefined')
        this.daemon_mode = false;
      conf = Object.assign(conf, path_structure(this.pm2_home));
    }

    this._conf = conf;

    if (conf.IS_WINDOWS) {
      // Weird fix, may need to be dropped
      // @todo windows connoisseur double check
      if (process.stdout._handle && process.stdout._handle.setBlocking)
        process.stdout._handle.setBlocking(true);
    }

    this.Client = new Client({
      pm2_home: that.pm2_home,
      conf: this._conf,
      secret_key: this.secret_key,
      public_key: this.public_key,
      daemon_mode: this.daemon_mode,
      machine_name: this.machine_name
    });

    this.pm2_configuration = Configuration.getSync('pm2') || {}

    this.gl_interact_infos = null;
    this.gl_is_km_linked = false;

    try {
      var pid = fs.readFileSync(conf.INTERACTOR_PID_PATH);
      pid = parseInt(pid.toString().trim());
      process.kill(pid, 0);
      that.gl_is_km_linked = true;
    } catch (e) {
      that.gl_is_km_linked = false;
    }

    // For testing purposes
    if (this.secret_key && process.env.NODE_ENV == 'local_test')
      that.gl_is_km_linked = true;

    KMDaemon.ping(this._conf, function(err, result) {
      if (!err && result === true) {
        fs.readFile(conf.INTERACTION_CONF, (err, _conf) => {
          if (!err) {
            try {
              that.gl_interact_infos = JSON.parse(_conf.toString())
            } catch(e) {
              var json5 = require('./tools/json5.js')
              try {
                that.gl_interact_infos = json5.parse(_conf.toString())
              } catch(e) {
                console.error(e)
                that.gl_interact_infos = null
              }
            }
          }
        })
      }
    })

    this.gl_retry = 0;
  }

  /**
   * Connect to PM2
   * Calling this command is now optional
   *
   * @param {Function} cb callback once pm2 is ready for commands
   */
  connect (noDaemon, cb) {
    var that = this;
    this.start_timer = new Date();

    if (typeof(cb) == 'undefined') {
      cb = noDaemon;
      noDaemon = false;
    } else if (noDaemon === true) {
      // Backward compatibility with PM2 1.x
      this.Client.daemon_mode = false;
      this.daemon_mode = false;
    }

    this.Client.start(function(err, meta) {
      if (err)
        return cb(err);

      if (meta.new_pm2_instance == false && that.daemon_mode === true)
        return cb(err, meta);

      that.launchSysMonitoring(() => {})
      // If new pm2 instance has been popped
      // Launch all modules
      that.launchAll(that, function(err_mod) {
        return cb(err, meta);
      });
    });
  }

  /**
   * Usefull when custom PM2 created with independent flag set to true
   * This will cleanup the newly created instance
   * by removing folder, killing PM2 and so on
   *
   * @param {Function} cb callback once cleanup is successfull
   */
  destroy (cb) {
    var that = this;

    debug('Killing and deleting current deamon');

    this.killDaemon(function() {
      var cmd = 'rm -rf ' + that.pm2_home;
      var test_path = path.join(that.pm2_home, 'module_conf.json');
      var test_path_2 = path.join(that.pm2_home, 'pm2.pid');

      if (that.pm2_home.indexOf('.pm2') > -1)
        return cb(new Error('Destroy is not a allowed method on .pm2'));

      fs.access(test_path, fs.R_OK, function(err) {
        if (err) return cb(err);
        debug('Deleting temporary folder %s', that.pm2_home);
        sexec(cmd, cb);
      });
    });
  }

  /**
   * Disconnect from PM2 instance
   * This will allow your software to exit by itself
   *
   * @param {Function} [cb] optional callback once connection closed
   */
  disconnect (cb) {
    var that = this;

    if (!cb) cb = function() {};

    this.Client.close(function(err, data) {
      debug('The session lasted %ds', (new Date() - that.start_timer) / 1000);
      return cb(err, data);
    });
  };

  /**
   * Alias on disconnect
   * @param cb
   */
  close (cb) {
    this.disconnect(cb);
  }

  /**
   * Launch modules
   *
   * @param {Function} cb callback once pm2 has launched modules
   */
  launchModules (cb) {
    this.launchAll(this, cb);
  }

  /**
   * Enable bus allowing to retrieve various process event
   * like logs, restarts, reloads
   *
   * @param {Function} cb callback called with 1st param err and 2nb param the bus
   */
  launchBus (cb) {
    this.Client.launchBus(cb);
  }

  /**
   * Exit methods for API
   * @param {Integer} code exit code for terminal
   */
  exitCli (code) {
    var that = this;

    // Do nothing if PM2 called programmatically (also in speedlist)
    if (conf.PM2_PROGRAMMATIC && process.env.PM2_USAGE != 'CLI') return false;

    KMDaemon.disconnectRPC(function() {
      that.Client.close(function() {
        code = code || 0;
        // Safe exits process after all streams are drained.
        // file descriptor flag.
        var fds = 0;
        // exits process when stdout (1) and sdterr(2) are both drained.
        function tryToExit() {
          if ((fds & 1) && (fds & 2)) {
            debug('This command took %ds to execute', (new Date() - that.start_timer) / 1000);
            process.exit(code);
          }
        }

        [process.stdout, process.stderr].forEach(function(std) {
          var fd = std.fd;
          if (!std.bufferSize) {
            // bufferSize equals 0 means current stream is drained.
            fds = fds | fd;
          } else {
            // Appends nothing to the std queue, but will trigger `tryToExit` event on `drain`.
            std.write && std.write('', function() {
              fds = fds | fd;
              tryToExit();
            });
          }
          // Does not write anything more.
          delete std.write;
        });
        tryToExit();
      });
    });
  }

////////////////////////////
// Application management //
////////////////////////////

  /**
   * Start a file or json with configuration
   * @param {Object||String} cmd script to start or json
   * @param {Function} cb called when application has been started
   */
  start (cmd, opts, cb) {
    if (typeof(opts) == "function") {
      cb = opts;
      opts = {};
    }
    if (!opts) opts = {};

    var that = this;
    if (Array.isArray(opts.watch) && opts.watch.length === 0)
      opts.watch = (opts.rawArgs ? !!~opts.rawArgs.indexOf('--watch') : !!~process.argv.indexOf('--watch')) || false;

    if (Common.isConfigFile(cmd) || (typeof(cmd) === 'object')) {
      that._startJson(cmd, opts, 'restartProcessId', (err, procs) => {
        return cb ? cb(err, procs) : this.speedList()
      })
    }
    else {
      that._startScript(cmd, opts, (err, procs) => {
        return cb ? cb(err, procs) : this.speedList(0)
      })
    }
  }

  /**
   * Reset process counters
   *
   * @method resetMetaProcess
   */
  reset (process_name, cb) {
    var that = this;

    function processIds(ids, cb) {
      eachLimit(ids, conf.CONCURRENT_ACTIONS, function(id, next) {
        that.Client.executeRemote('resetMetaProcessId', id, function(err, res) {
          if (err) console.error(err);
          Common.printOut(conf.PREFIX_MSG + 'Resetting meta for process id %d', id);
          return next();
        });
      }, function(err) {
        if (err) return cb(Common.retErr(err));
        return cb ? cb(null, {success:true}) : that.speedList();
      });
    }

    if (process_name == 'all') {
      that.Client.getAllProcessId(function(err, ids) {
        if (err) {
          Common.printError(err);
          return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);
        }
        return processIds(ids, cb);
      });
    }
    else if (isNaN(process_name)) {
      that.Client.getProcessIdByName(process_name, function(err, ids) {
        if (err) {
          Common.printError(err);
          return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);
        }
        if (ids.length === 0) {
          Common.printError('Unknown process name');
          return cb ? cb(new Error('Unknown process name')) : that.exitCli(conf.ERROR_EXIT);
        }
        return processIds(ids, cb);
      });
    } else {
      processIds([process_name], cb);
    }
  }

  /**
   * Update daemonized PM2 Daemon
   *
   * @param {Function} cb callback when pm2 has been upgraded
   */
  update (cb) {
    var that = this;

    Common.printOut('Be sure to have the latest version by doing `npm install pm2@latest -g` before doing this procedure.');

    // Dump PM2 processes
    that.Client.executeRemote('notifyKillPM2', {}, function() {});

    that.getVersion(function(err, new_version) {
      // If not linked to PM2 plus, and update PM2 to latest, display motd.update
      if (!that.gl_is_km_linked && !err && (pkg.version != new_version)) {
        var dt = fs.readFileSync(path.join(__dirname, that._conf.PM2_UPDATE));
        console.log(dt.toString());
      }

      that.dump(function(err) {
        that.killDaemon(function() {
          that.Client.launchDaemon({interactor:false}, function(err, child) {
            that.Client.launchRPC(function() {
              that.resurrect(function() {
                Common.printOut(chalk.blue.bold('>>>>>>>>>> PM2 updated'));
                that.launchSysMonitoring(() => {})
                that.launchAll(that, function() {
                  KMDaemon.launchAndInteract(that._conf, {
                    pm2_version: pkg.version
                  }, function(err, data, interactor_proc) {
                  })
                  setTimeout(() => {
                    return cb ? cb(null, {success:true}) : that.speedList();
                  }, 250)
                });
              });
            });
          });
        });
      });
    });

    return false;
  }

  /**
   * Reload an application
   *
   * @param {String} process_name Application Name or All
   * @param {Object} opts         Options
   * @param {Function} cb         Callback
   */
  reload (process_name, opts, cb) {
    var that = this;

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

    var delay = Common.lockReload();
    if (delay > 0 && opts.force != true) {
      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');
      return cb ? cb(new Error('Reload in progress')) : that.exitCli(conf.ERROR_EXIT);
    }

    if (Common.isConfigFile(process_name))
      that._startJson(process_name, opts, 'reloadProcessId', function(err, apps) {
        Common.unlockReload();
        if (err)
          return cb ? cb(err) : that.exitCli(conf.ERROR_EXIT);
        return cb ? cb(null, apps) : that.exitCli(conf.SUCCESS_EXIT);
      });
    else {
      if (opts && opts.env) {
        var err = 'Using --env [env] without passing the ecosystem.config.js does not work'
        Common.err(err);
        Common.unlockReload();
        return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);
      }

      if (opts && !opts.updateEnv)
        Common.printOut(IMMUTABLE_MSG);

      that._operate('reloadProcessId', process_name, opts, function(err, apps) {
        Common.unlockReload();

        if (err)
          return cb ? cb(err) : that.exitCli(conf.ERROR_EXIT);
        return cb ? cb(null, apps) : that.exitCli(conf.SUCCESS_EXIT);
      });
    }
  }

  /**
   * Restart process
   *
   * @param {String} cmd   Application Name / Process id / JSON application file / 'all'
   * @param {Object} opts  Extra options to be updated
   * @param {Function} cb  Callback
   */
  restart (cmd, opts, cb) {
    if (typeof(opts) == "function") {
      cb = opts;
      opts = {};
    }
    var that = this;

    if (typeof(cmd) === 'number')
      cmd = cmd.toString();

    if (cmd == "-") {
      // Restart from PIPED JSON
      process.stdin.resume();
      process.stdin.setEncoding('utf8');
      process.stdin.on('data', function (param) {
        process.stdin.pause();
        that.actionFromJson('restartProcessId', param, opts, 'pipe', cb);
      });
    }
    else if (Common.isConfigFile(cmd) || typeof(cmd) === 'object')
      that._startJson(cmd, opts, 'restartProcessId', cb);
    else {
      if (opts && opts.env) {
        var err = 'Using --env [env] without passing the ecosystem.config.js does not work'
        Common.err(err);
        return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);
      }
      if (opts && !opts.updateEnv)
        Common.printOut(IMMUTABLE_MSG);
      that._operate('restartProcessId', cmd, opts, cb);
    }
  }

  /**
   * Delete process
   *
   * @param {String} process_name Application Name / Process id / Application file / 'all'
   * @param {Function} cb Callback
   */
  delete (process_name, jsonVia, cb) {
    var that = this;

    if (typeof(jsonVia) === "function") {
      cb = jsonVia;
      jsonVia = null;
    }

    if (typeof(process_name) === "number") {
      process_name = process_name.toString();
    }

    if (jsonVia == 'pipe')
      return that.actionFromJson('deleteProcessId', process_name, commander, 'pipe', (err, procs) => {
        return cb ? cb(err, procs) : this.speedList()
      });
    if (Common.isConfigFile(process_name))
      return that.actionFromJson('deleteProcessId', process_name, commander, 'file', (err, procs) => {
        return cb ? cb(err, procs) : this.speedList()
      });
    else {
      that._operate('deleteProcessId', process_name, (err, procs) => {
        return cb ? cb(err, procs) : this.speedList()
      });
    }
  }

  /**
   * Stop process
   *
   * @param {String} process_name Application Name / Process id / Application file / 'all'
   * @param {Function} cb Callback
   */
  stop (process_name, cb) {
    var that = this;

    if (typeof(process_name) === 'number')
      process_name = process_name.toString();

    if (process_name == "-") {
      process.stdin.resume();
      process.stdin.setEncoding('utf8');
      process.stdin.on('data', function (param) {
        process.stdin.pause();
        that.actionFromJson('stopProcessId', param, commander, 'pipe', (err, procs) => {
          return cb ? cb(err, procs) : this.speedList()
        })
      });
    }
    else if (Common.isConfigFile(process_name))
      that.actionFromJson('stopProcessId', process_name, commander, 'file', (err, procs) => {
        return cb ? cb(err, procs) : this.speedList()
      });
    else
      that._operate('stopProcessId', process_name, (err, procs) => {
        return cb ? cb(err, procs) : this.speedList()
      });
  }

  /**
   * Get list of all processes managed
   *
   * @param {Function} cb Callback
   */
  list (opts, cb) {
    var that = this;

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

    that.Client.executeRemote('getMonitorData', {}, function(err, list) {
      if (err) {
        Common.printError(err);
        return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);
      }

      if (opts && opts.rawArgs && opts.rawArgs.indexOf('--watch') > -1) {
        var dayjs = require('dayjs');
        function show() {
          process.stdout.write('\x1b[2J');
          process.stdout.write('\x1b[0f');
          console.log('Last refresh: ', dayjs().format());
          that.Client.executeRemote('getMonitorData', {}, function(err, list) {
            UX.list(list, null);
          });
        }

        show();
        setInterval(show, 900);
        return false;
      }

      return cb ? cb(null, list) : that.speedList(null);
    });
  }

  /**
   * Kill Daemon
   *
   * @param {Function} cb Callback
   */
  killDaemon (cb) {
    process.env.PM2_STATUS = 'stopping'

    var that = this;

    that.Client.executeRemote('notifyKillPM2', {}, function() {});

    that._operate('deleteProcessId', 'all', function(err, list) {
      Common.printOut(conf.PREFIX_MSG + '[v] All Applications Stopped');
      process.env.PM2_SILENT = 'false';

      that.killAgent(function(err, data) {
        if (!err) {
          Common.printOut(conf.PREFIX_MSG + '[v] Agent Stopped');
        }

        that.Client.killDaemon(function(err, res) {
          if (err) Common.printError(err);
          Common.printOut(conf.PREFIX_MSG + '[v] PM2 Daemon Stopped');
          return cb ? cb(err, res) : that.exitCli(conf.SUCCESS_EXIT);
        });

      });
    })
  }

  kill (cb) {
    this.killDaemon(cb);
  }

  /////////////////////
  // Private methods //
  /////////////////////

  /**
   * Method to START / RESTART a script
   *
   * @private
   * @param {string} script script name (will be resolved according to location)
   */
  _startScript (script, opts, cb) {
    if (typeof opts == "function") {
      cb = opts;
      opts = {};
    }
    var that = this;

    /**
     * Commander.js tricks
     */
    var app_conf = Config.filterOptions(opts);
    var appConf = {};

    if (typeof app_conf.name == 'function')
      delete app_conf.name;

    delete app_conf.args;

    // Retrieve arguments via -- <args>
    var argsIndex;

    if (opts.rawArgs && (argsIndex = opts.rawArgs.indexOf('--')) >= 0)
      app_conf.args = opts.rawArgs.slice(argsIndex + 1);
    else if (opts.scriptArgs)
      app_conf.args = opts.scriptArgs;

    app_conf.script = script;
    if(!app_conf.namespace)
      app_conf.namespace = 'default';

    if ((appConf = Common.verifyConfs(app_conf)) instanceof Error) {
      Common.err(appConf)
      return cb ? cb(Common.retErr(appConf)) : that.exitCli(conf.ERROR_EXIT);
    }

    app_conf = appConf[0];

    if (opts.watchDelay) {
      if (typeof opts.watchDelay === "string" && opts.watchDelay.indexOf("ms") !== -1)
        app_conf.watch_delay = parseInt(opts.watchDelay);
      else {
        app_conf.watch_delay = parseFloat(opts.watchDelay) * 1000;
      }
    }

    var mas = [];
    if(typeof opts.ext != 'undefined')
      hf.make_available_extension(opts, mas); // for -e flag
    mas.length > 0 ? app_conf.ignore_watch = mas : 0;

    /**
     * If -w option, write configuration to configuration.json file
     */
    if (app_conf.write) {
      var dst_path = path.join(process.env.PWD || process.cwd(), app_conf.name + '-pm2.json');
      Common.printOut(conf.PREFIX_MSG + 'Writing configuration to', chalk.blue(dst_path));
      // pretty JSON
      try {
        fs.writeFileSync(dst_path, JSON.stringify(app_conf, null, 2));
      } catch (e) {
        console.error(e.stack || e);
      }
    }

    series([
      restartExistingProcessName,
      restartExistingNameSpace,
      restartExistingProcessId,
      restartExistingProcessPathOrStartNew
    ], function(err, data) {
      if (err instanceof Error)
        return cb ? cb(err) : that.exitCli(conf.ERROR_EXIT);

      var ret = {};

      data.forEach(function(_dt) {
        if (_dt !== undefined)
          ret = _dt;
      });

      return cb ? cb(null, ret) : that.speedList();
    });

    /**
     * If start <app_name> start/restart application
     */
    function restartExistingProcessName(cb) {
      if (!isNaN(script) ||
        (typeof script === 'string' && script.indexOf('/') != -1) ||
        (typeof script === 'string' && path.extname(script) !== ''))
        return cb(null);

        that.Client.getProcessIdByName(script, function(err, ids) {
          if (err && cb) return cb(err);
          if (ids.length > 0) {
            that._operate('restartProcessId', script, opts, function(err, list) {
              if (err) return cb(err);
              Common.printOut(conf.PREFIX_MSG + 'Process successfully started');
              return cb(true, list);
            });
          }
          else return cb(null);
        });
    }

    /**
     * If start <namespace> start/restart namespace
     */
    function restartExistingNameSpace(cb) {
      if (!isNaN(script) ||
        (typeof script === 'string' && script.indexOf('/') != -1) ||
        (typeof script === 'string' && path.extname(script) !== ''))
        return cb(null);

      if (script !== 'all') {
        that.Client.getProcessIdsByNamespace(script, function (err, ids) {
          if (err && cb) return cb(err);
          if (ids.length > 0) {
            that._operate('restartProcessId', script, opts, function (err, list) {
              if (err) return cb(err);
              Common.printOut(conf.PREFIX_MSG + 'Process successfully started');
              return cb(true, list);
            });
          }
          else return cb(null);
        });
      }
      else {
        that._operate('restartProcessId', 'all', function(err, list) {
          if (err) return cb(err);
          Common.printOut(conf.PREFIX_MSG + 'Process successfully started');
          return cb(true, list);
        });
      }
    }

    function restartExistingProcessId(cb) {
      if (isNaN(script)) return cb(null);

      that._operate('restartProcessId', script, opts, function(err, list) {
        if (err) return cb(err);
        Common.printOut(conf.PREFIX_MSG + 'Process successfully started');
        return cb(true, list);
      });
    }

    /**
     * Restart a process with the same full path
     * Or start it
     */
    function restartExistingProcessPathOrStartNew(cb) {
      that.Client.executeRemote('getMonitorData', {}, function(err, procs) {
        if (err) return cb ? cb(new Error(err)) : that.exitCli(conf.ERROR_EXIT);

        var full_path = path.resolve(that.cwd, script);
        var managed_script = null;

        procs.forEach(function(proc) {
          if (proc.pm2_env.pm_exec_path == full_path &&
              proc.pm2_env.name == app_conf.name)
            managed_script = proc;
        });

        if (managed_script &&
          (managed_script.pm2_env.status == conf.STOPPED_STATUS ||
            managed_script.pm2_env.status == conf.STOPPING_STATUS ||
            managed_script.pm2_env.status == conf.ERRORED_STATUS)) {
          // Restart process if stopped
          var app_name = managed_script.pm2_env.name;

          that._operate('restartProcessId', app_name, opts, function(err, list) {
            if (err) return cb ? cb(new Error(err)) : that.exitCli(conf.ERROR_EXIT);
            Common.printOut(conf.PREFIX_MSG + 'Process successfully started');
            return cb(true, list);
          });
          return false;
        }
        else if (managed_script && !opts.force) {
          Common.err('Script already launched, add -f option to force re-execution');
          return cb(new Error('Script already launched'));
        }

        var resolved_paths = null;

        try {
          resolved_paths = Common.resolveAppAttributes({
            cwd      : that.cwd,
            pm2_home : that.pm2_home
          }, app_conf);
        } catch(e) {
          Common.err(e.message);
          return cb(Common.retErr(e));
        }

        Common.printOut(conf.PREFIX_MSG + 'Starting %s in %s (%d instance' + (resolved_paths.instances > 1 ? 's' : '') + ')',
          resolved_paths.pm_exec_path, resolved_paths.exec_mode, resolved_paths.instances);

        if (!resolved_paths.env) resolved_paths.env = {};

        // Set PM2 HOME in case of child process using PM2 API
        resolved_paths.env['PM2_HOME'] = that.pm2_home;

        var additional_env = Modularizer.getAdditionalConf(resolved_paths.name);
        Object.assign(resolved_paths.env, additional_env);

        // Is KM linked?
        resolved_paths.km_link = that.gl_is_km_linked;

        that.Client.executeRemote('prepare', resolved_paths, function(err, data) {
          if (err) {
            Common.printError(conf.PREFIX_MSG_ERR + 'Error while launching application', err.stack || err);
            return cb(Common.retErr(err));
          }

          Common.printOut(conf.PREFIX_MSG + 'Done.');
          return cb(true, data);
        });
        return false;
      });
    }
  }

  /**
   * Method to start/restart/reload processes from a JSON file
   * It will start app not started
   * Can receive only option to skip applications
   *
   * @private
   */
  _startJson (file, opts, action, pipe, cb) {
    var config     = {};
    var appConf    = {};
    var staticConf = [];
    var deployConf = {};
    var apps_info  = [];
    var that = this;

    /**
     * Get File configuration
     */
    if (typeof(cb) === 'undefined' && typeof(pipe) === 'function') {
      cb = pipe;
    }
    if (typeof(file) === 'object') {
      config = file;
    } else if (pipe === 'pipe') {
      config = Common.parseConfig(file, 'pipe');
    } else {
      var data = null;

      var isAbsolute = path.isAbsolute(file)
      var file_path = isAbsolute ? file : path.join(that.cwd, file);

      debug('Resolved filepath %s', file_path);

      try {
        data = fs.readFileSync(file_path);
      } catch(e) {
        Common.printError(conf.PREFIX_MSG_ERR + 'File ' + file +' not found');
        return cb ? cb(Common.retErr(e)) : that.exitCli(conf.ERROR_EXIT);
      }

      try {
        config = Common.parseConfig(data, file);
      } catch(e) {
        Common.printError(conf.PREFIX_MSG_ERR + 'File ' + file + ' malformated');
        console.error(e);
        return cb ? cb(Common.retErr(e)) : that.exitCli(conf.ERROR_EXIT);
      }
    }

    /**
     * Alias some optional fields
     */
    if (config.deploy)
      deployConf = config.deploy;
    if (config.static)
      staticConf = config.static;
    if (config.apps)
      appConf = config.apps;
    else if (config.pm2)
      appConf = config.pm2;
    else
      appConf = config;
    if (!Array.isArray(appConf))
      appConf = [appConf];

    if ((appConf = Common.verifyConfs(appConf)) instanceof Error)
      return cb ? cb(appConf) : that.exitCli(conf.ERROR_EXIT);

    process.env.PM2_JSON_PROCESSING = true;

    // Get App list
    var apps_name = [];
    var proc_list = {};

    // Add statics to apps
    staticConf.forEach(function(serve) {
      appConf.push({
        name: serve.name ? serve.name : `static-page-server-${serve.port}`,
        script: path.resolve(__dirname, 'API', 'Serve.js'),
        env: {
          PM2_SERVE_PORT: serve.port,
          PM2_SERVE_HOST: serve.host,
          PM2_SERVE_PATH: serve.path,
          PM2_SERVE_SPA: serve.spa,
          PM2_SERVE_DIRECTORY: serve.directory,
          PM2_SERVE_BASIC_AUTH: serve.basic_auth !== undefined,
          PM2_SERVE_BASIC_AUTH_USERNAME: serve.basic_auth ? serve.basic_auth.username : null,
          PM2_SERVE_BASIC_AUTH_PASSWORD: serve.basic_auth ? serve.basic_auth.password : null,
          PM2_SERVE_MONITOR: serve.monitor
        }
      });
    });

    // Here we pick only the field we want from the CLI when starting a JSON
    appConf.forEach(function(app) {
      if (!app.env) { app.env = {}; }
      app.env.io = app.io;
      // --only <app>
      if (opts.only) {
        var apps = opts.only.split(/,| /)
        if (apps.indexOf(app.name) == -1)
          return false
      }
      // Namespace
      if (!app.namespace) {
        if (opts.namespace)
          app.namespace = opts.namespace;
        else
          app.namespace = 'default';
      }
      // --watch
      if (!app.watch && opts.watch && opts.watch === true)
        app.watch = true;
      // --ignore-watch
      if (!app.ignore_watch && opts.ignore_watch)
        app.ignore_watch = opts.ignore_watch;
      if (opts.install_url)
        app.install_url = opts.install_url;
      // --instances <nb>
      if (opts.instances && typeof(opts.instances) === 'number')
        app.instances = opts.instances;
      // --uid <user>
      if (opts.uid)
        app.uid = opts.uid;
      // --gid <user>
      if (opts.gid)
        app.gid = opts.gid;
      // Specific
      if (app.append_env_to_name && opts.env)
        app.name += ('-' + opts.env);
      if (opts.name_prefix && app.name.indexOf(opts.name_prefix) == -1)
        app.name = `${opts.name_prefix}:${app.name}`

      app.username = Common.getCurrentUsername();
      apps_name.push(app.name);
    });

    that.Client.executeRemote('getMonitorData', {}, function(err, raw_proc_list) {
      if (err) {
        Common.printError(err);
        return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);
      }

      /**
       * Uniquify in memory process list
       */
      raw_proc_list.forEach(function(proc) {
        proc_list[proc.name] = proc;
      });

      /**
       * Auto detect application already started
       * and act on them depending on action
       */
      eachLimit(Object.keys(proc_list), conf.CONCURRENT_ACTIONS, function(proc_name, next) {
        // Skip app name (--only option)
        if (apps_name.indexOf(proc_name) == -1)
          return next();

        if (!(action == 'reloadProcessId' ||
            action == 'softReloadProcessId' ||
            action == 'restartProcessId'))
          throw new Error('Wrong action called');

        var apps = appConf.filter(function(app) {
          return app.name == proc_name;
        });

        var envs = apps.map(function(app){
          // Binds env_diff to env and returns it.
          return Common.mergeEnvironmentVariables(app, opts.env, deployConf);
        });

        // Assigns own enumerable properties of all
        // Notice: if people use the same name in different apps,
        //         duplicated envs will be overrode by the last one
        var env = envs.reduce(function(e1, e2){
          return Object.assign(e1, e2);
        });

        // When we are processing JSON, allow to keep the new env by default
        env.updateEnv = true;

        // Pass `env` option
        that._operate(action, proc_name, env, function(err, ret) {
          if (err) Common.printError(err);

          // For return
          apps_info = apps_info.concat(ret);

          that.Client.notifyGod(action, proc_name);
          // And Remove from array to spy
          apps_name.splice(apps_name.indexOf(proc_name), 1);
          return next();
        });

      }, function(err) {
        if (err) return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);
        if (apps_name.length > 0 && action != 'start')
          Common.printOut(conf.PREFIX_MSG_WARNING + 'Applications %s not running, starting...', apps_name.join(', '));
        // Start missing apps
        return startApps(apps_name, function(err, apps) {
          apps_info = apps_info.concat(apps);
          return cb ? cb(err, apps_info) : that.speedList(err ? 1 : 0);
        });
      });
      return false;
    });

    function startApps(app_name_to_start, cb) {
      var apps_to_start = [];
      var apps_started = [];
      var apps_errored = [];

      appConf.forEach(function(app, i) {
        if (app_name_to_start.indexOf(app.name) != -1) {
          apps_to_start.push(appConf[i]);
        }
      });

      eachLimit(apps_to_start, conf.CONCURRENT_ACTIONS, function(app, next) {
        if (opts.cwd)
          app.cwd = opts.cwd;
        if (opts.force_name)
          app.name = opts.force_name;
        if (opts.started_as_module)
          app.pmx_module = true;

        var resolved_paths = null;

        // hardcode script name to use `serve` feature inside a process file
        if (app.script === 'serve') {
          app.script = path.resolve(__dirname, 'API', 'Serve.js')
        }

        try {
          resolved_paths = Common.resolveAppAttributes({
            cwd      : that.cwd,
            pm2_home : that.pm2_home
          }, app);
        } catch (e) {
          apps_errored.push(e)
          Common.err(`Error: ${e.message}`)
          return next();
        }

        if (!resolved_paths.env) resolved_paths.env = {};

        // Set PM2 HOME in case of child process using PM2 API
        resolved_paths.env['PM2_HOME'] = that.pm2_home;

        var additional_env = Modularizer.getAdditionalConf(resolved_paths.name);
        Object.assign(resolved_paths.env, additional_env);

        resolved_paths.env = Common.mergeEnvironmentVariables(resolved_paths, opts.env, deployConf);

        delete resolved_paths.env.current_conf;

        // Is KM linked?
        resolved_paths.km_link = that.gl_is_km_linked;

        if (resolved_paths.wait_ready) {
          Common.warn(`App ${resolved_paths.name} has option 'wait_ready' set, waiting for app to be ready...`)
        }
        that.Client.executeRemote('prepare', resolved_paths, function(err, data) {
          if (err) {
            Common.printError(conf.PREFIX_MSG_ERR + 'Process failed to launch %s', err.message ? err.message : err);
            return next();
          }
          if (data.length === 0) {
            Common.printError(conf.PREFIX_MSG_ERR + 'Process config loading failed', data);
            return next();
          }

          Common.printOut(conf.PREFIX_MSG + 'App [%s] launched (%d instances)', data[0].pm2_env.name, data.length);
          apps_started = apps_started.concat(data);
          next();
        });

      }, function(err) {
        var final_error = err || apps_errored.length > 0 ? apps_errored : null
        return cb ? cb(final_error, apps_started) : that.speedList();
      });
      return false;
    }
  }

  /**
   * Apply a RPC method on the json file
   * @private
   * @method actionFromJson
   * @param {string} action RPC Method
   * @param {object} options
   * @param {string|object} file file
   * @param {string} jsonVia action type (=only 'pipe' ?)
   * @param {Function}
   */
  actionFromJson (action, file, opts, jsonVia, cb) {
    var appConf = {};
    var ret_processes = [];
    var that = this;

    //accept programmatic calls
    if (typeof file == 'object') {
      cb = typeof jsonVia == 'function' ? jsonVia : cb;
      appConf = file;
    }
    else if (jsonVia == 'file') {
      var data = null;

      try {
        data = fs.readFileSync(file);
      } catch(e) {
        Common.printError(conf.PREFIX_MSG_ERR + 'File ' + file +' not found');
        return cb ? cb(Common.retErr(e)) : that.exitCli(conf.ERROR_EXIT);
      }

      try {
        appConf = Common.parseConfig(data, file);
      } catch(e) {
        Common.printError(conf.PREFIX_MSG_ERR + 'File ' + file + ' malformated');
        console.error(e);
        return cb ? cb(Common.retErr(e)) : that.exitCli(conf.ERROR_EXIT);
      }
    } else if (jsonVia == 'pipe') {
      appConf = Common.parseConfig(file, 'pipe');
    } else {
      Common.printError('Bad call to actionFromJson, jsonVia should be one of file, pipe');
      return that.exitCli(conf.ERROR_EXIT);
    }

    // Backward compatibility
    if (appConf.apps)
      appConf = appConf.apps;

    if (!Array.isArray(appConf))
      appConf = [appConf];

    if ((appConf = Common.verifyConfs(appConf)) instanceof Error)
      return cb ? cb(appConf) : that.exitCli(conf.ERROR_EXIT);

    eachLimit(appConf, conf.CONCURRENT_ACTIONS, function(proc, next1) {
      var name = '';
      var new_env;

      if (!proc.name)
        name = path.basename(proc.script);
      else
        name = proc.name;

      if (opts.only && opts.only != name)
        return process.nextTick(next1);

      if (opts && opts.env)
        new_env = Common.mergeEnvironmentVariables(proc, opts.env);
      else
        new_env = Common.mergeEnvironmentVariables(proc);

      that.Client.getProcessIdByName(name, function(err, ids) {
        if (err) {
          Common.printError(err);
          return next1();
        }
        if (!ids) return next1();

        eachLimit(ids, conf.CONCURRENT_ACTIONS, function(id, next2) {
          var opts = {};

          //stopProcessId could accept options to?
          if (action == 'restartProcessId') {
            opts = {id : id, env : new_env};
          } else {
            opts = id;
          }

          that.Client.executeRemote(action, opts, function(err, res) {
            ret_processes.push(res);
            if (err) {
              Common.printError(err);
              return next2();
            }

            if (action == 'restartProcessId') {
              that.Client.notifyGod('restart', id);
            } else if (action == 'deleteProcessId') {
              that.Client.notifyGod('delete', id);
            } else if (action == 'stopProcessId') {
              that.Client.notifyGod('stop', id);
            }

            Common.printOut(conf.PREFIX_MSG + '[%s](%d) \u2713', name, id);
            return next2();
          });
        }, function(err) {
          return next1(null, ret_processes);
        });
      });
    }, function(err) {
      if (cb) return cb(null, ret_processes);
      else return that.speedList();
    });
  }


  /**
   * Main function to operate with PM2 daemon
   *
   * @param {String} action_name  Name of action (restartProcessId, deleteProcessId, stopProcessId)
   * @param {String} process_name can be 'all', a id integer or process name
   * @param {Object} envs         object with CLI options / environment
   */
  _operate (action_name, process_name, envs, cb) {
    var that = this;
    var update_env = false;
    var ret = [];

    // Make sure all options exist
    if (!envs)
      envs = {};

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

    // Set via env.update (JSON processing)
    if (envs.updateEnv === true)
      update_env = true;

    var concurrent_actions = envs.parallel || conf.CONCURRENT_ACTIONS;

    if (!process.env.PM2_JSON_PROCESSING || envs.commands) {
      envs = that._handleAttributeUpdate(envs);
    }

    /**
     * Set current updated configuration if not passed
     */
    if (!envs.current_conf) {
      var _conf = fclone(envs);
      envs = {
        current_conf : _conf
      }

      // Is KM linked?
      envs.current_conf.km_link = that.gl_is_km_linked;
    }

    /**
     * Operate action on specific process id
     */
    function processIds(ids, cb) {
      Common.printOut(conf.PREFIX_MSG + 'Applying action %s on app [%s](ids: %s)', action_name, process_name, ids);

      if (ids.length <= 2)
        concurrent_actions = 1;

      if (action_name == 'deleteProcessId')
        concurrent_actions = 10;

      eachLimit(ids, concurrent_actions, function(id, next) {
        var opts;

        // These functions need extra param to be passed
        if (action_name == 'restartProcessId' ||
          action_name == 'reloadProcessId' ||
          action_name == 'softReloadProcessId') {
          var new_env = {};

          if (update_env === true) {
            if (conf.PM2_PROGRAMMATIC == true)
              new_env = Common.safeExtend({}, process.env);
            else
              new_env = Object.assign({}, process.env);

            Object.keys(envs).forEach(function(k) {
              new_env[k] = envs[k];
            });
          }
          else {
            new_env = envs;
          }

          opts = {
            id  : id,
            env : new_env
          };
        }
        else {
          opts = id;
        }

        that.Client.executeRemote(action_name, opts, function(err, res) {
          if (err) {
            Common.printError(conf.PREFIX_MSG_ERR + 'Process %s not found', id);
            return next(`Process ${id} not found`);
          }

          if (action_name == 'restartProcessId') {
            that.Client.notifyGod('restart', id);
          } else if (action_name == 'deleteProcessId') {
            that.Client.notifyGod('delete', id);
          } else if (action_name == 'stopProcessId') {
            that.Client.notifyGod('stop', id);
          } else if (action_name == 'reloadProcessId') {
            that.Client.notifyGod('reload', id);
          } else if (action_name == 'softReloadProcessId') {
            that.Client.notifyGod('graceful reload', id);
          }

          if (!Array.isArray(res))
            res = [res];

          // Filter return
          res.forEach(function(proc) {
            Common.printOut(conf.PREFIX_MSG + '[%s](%d) \u2713', proc.pm2_env ? proc.pm2_env.name : process_name, id);

            if (action_name == 'stopProcessId' && proc.pm2_env && proc.pm2_env.cron_restart) {
              Common.warn(`App ${chalk.bold(proc.pm2_env.name)} stopped but CRON RESTART is still UP ${proc.pm2_env.cron_restart}`)
            }

            if (!proc.pm2_env) return false;

            ret.push({
              name         : proc.pm2_env.name,
              namespace: proc.pm2_env.namespace,
              pm_id        : proc.pm2_env.pm_id,
              status       : proc.pm2_env.status,
              restart_time : proc.pm2_env.restart_time,
              pm2_env : {
                name         : proc.pm2_env.name,
                namespace: proc.pm2_env.namespace,
                pm_id        : proc.pm2_env.pm_id,
                status       : proc.pm2_env.status,
                restart_time : proc.pm2_env.restart_time,
                env          : proc.pm2_env.env
              }
            });
          });

          return next();
        });
      }, function(err) {
        if (err) return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);
        return cb ? cb(null, ret) : that.speedList();
      });
    }

    if (process_name == 'all') {
      // When using shortcuts like 'all', do not delete modules
      var fn

      if (process.env.PM2_STATUS == 'stopping')
        that.Client.getAllProcessId(function(err, ids) {
          reoperate(err, ids)
        });
      else
        that.Client.getAllProcessIdWithoutModules(function(err, ids) {
          reoperate(err, ids)
        });

      function reoperate(err, ids) {
        if (err) {
          Common.printError(err);
          return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);
        }
        if (!ids || ids.length === 0) {
          Common.printError(conf.PREFIX_MSG_WARNING + 'No process found');
          return cb ? cb(new Error('process name not found')) : that.exitCli(conf.ERROR_EXIT);
        }
        return processIds(ids, cb);
      }
    }
    // operate using regex
    else if (isNaN(process_name) && process_name[0] === '/' && process_name[process_name.length - 1] === '/') {
      var regex = new RegExp(process_name.replace(/\//g, ''));

      that.Client.executeRemote('getMonitorData', {}, function(err, list) {
        if (err) {
          Common.printError('Error retrieving process list: ' + err);
          return cb(err);
        }
        var found_proc = [];
        list.forEach(function(proc) {
          if (regex.test(proc.pm2_env.name)) {
            found_proc.push(proc.pm_id);
          }
        });

        if (found_proc.length === 0) {
          Common.printError(conf.PREFIX_MSG_WARNING + 'No process found');
          return cb ? cb(new Error('process name not found')) : that.exitCli(conf.ERROR_EXIT);
        }

        return processIds(found_proc, cb);
      });
    }
    else if (isNaN(process_name)) {
      /**
       * We can not stop or delete a module but we can restart it
       * to refresh configuration variable
       */
      var allow_module_restart = action_name == 'restartProcessId' ? true : false;

      that.Client.getProcessIdByName(process_name, allow_module_restart, function (err, ids) {
        if (err) {
          Common.printError(err);
          return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);
        }
        if (ids && ids.length > 0) {
          /**
         * Determine if the process to restart is a module
         * if yes load configuration variables and merge with the current environment
         */
          var additional_env = Modularizer.getAdditionalConf(process_name);
          Object.assign(envs, additional_env);
          return processIds(ids, cb);
        }

        that.Client.getProcessIdsByNamespace(process_name, allow_module_restart, function (err, ns_process_ids) {
          if (err) {
            Common.printError(err);
            return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);
          }
          if (!ns_process_ids || ns_process_ids.length === 0) {
            Common.printError(conf.PREFIX_MSG_ERR + 'Process or Namespace %s not found', process_name);
            return cb ? cb(new Error('process or namespace not found')) : that.exitCli(conf.ERROR_EXIT);
          }

          /**
           * Determine if the process to restart is a module
           * if yes load configuration variables and merge with the current environment
           */
          var ns_additional_env = Modularizer.getAdditionalConf(process_name);
          Object.assign(envs, ns_additional_env);
          return processIds(ns_process_ids, cb);
        });
      });
    } else {
      if (that.pm2_configuration.docker == "true" ||
          that.pm2_configuration.docker == true) {
        // Docker/Systemd process interaction detection
        that.Client.executeRemote('getMonitorData', {}, (err, proc_list) => {
          var higher_id = 0
          proc_list.forEach(p => { p.pm_id > higher_id ? higher_id = p.pm_id : null })

          // Is Docker/Systemd
          if (process_name > higher_id)
            return DockerMgmt.processCommand(that, higher_id, process_name, action_name, (err) => {
              if (err) {
                Common.printError(conf.PREFIX_MSG_ERR + (err.message ? err.message : err));
                return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);
              }

              return cb ? cb(null, ret) : that.speedList();
            })

          // Check if application name as number is an app name
          that.Client.getProcessIdByName(process_name, function(err, ids) {
            if (ids.length > 0)
              return processIds(ids, cb);

            // Check if application name as number is an namespace
            that.Client.getProcessIdsByNamespace(process_name, function(err, ns_process_ids) {
              if (ns_process_ids.length > 0)
                return processIds(ns_process_ids, cb);
              // Else operate on pm id
              return processIds([process_name], cb);
            });
          });
        })
      }
      else {
        // Check if application name as number is an app name
        that.Client.getProcessIdByName(process_name, function(err, ids) {
          if (ids.length > 0)
            return processIds(ids, cb);

          // Check if application name as number is an namespace
          that.Client.getProcessIdsByNamespace(process_name, function(err, ns_process_ids) {
            if (ns_process_ids.length > 0)
              return processIds(ns_process_ids, cb);
            // Else operate on pm id
            return processIds([process_name], cb);
          });
        });
      }
    }
  }

  /**
   * Converts CamelCase Commander.js arguments
   * to Underscore
   * (nodeArgs -> node_args)
   */
  _handleAttributeUpdate (opts) {
    var conf = Config.filterOptions(opts);
    var that = this;

    if (typeof(conf.name) != 'string')
      delete conf.name;

    var argsIndex = 0;
    if (opts.rawArgs && (argsIndex = opts.rawArgs.indexOf('--')) >= 0) {
      conf.args = opts.rawArgs.slice(argsIndex + 1);
    }

    var appConf = Common.verifyConfs(conf)[0];

    if (appConf instanceof Error) {
      Common.printError('Error while transforming CamelCase args to underscore');
      return appConf;
    }

    if (argsIndex == -1)
      delete appConf.args;
    if (appConf.name == 'undefined')
      delete appConf.name;

    delete appConf.exec_mode;

    if (Array.isArray(appConf.watch) && appConf.watch.length === 0) {
      if (!~opts.rawArgs.indexOf('--watch'))
        delete appConf.watch
    }

    // Options set via environment variables
    if (process.env.PM2_DEEP_MONITORING)
      appConf.deep_monitoring = true;

    // Force deletion of defaults values set by commander
    // to avoid overriding specified configuration by user
    if (appConf.treekill === true)
      delete appConf.treekill;
    if (appConf.pmx === true)
      delete appConf.pmx;
    if (appConf.vizion === true)
      delete appConf.vizion;
    if (appConf.automation === true)
      delete appConf.automation;
    if (appConf.autostart === true)
      delete appConf.autostart;
    if (appConf.autorestart === true)
      delete appConf.autorestart;

    return appConf;
  }

  getProcessIdByName (name, cb) {
    var that = this;

    this.Client.getProcessIdByName(name, function(err, id) {
      if (err) {
        Common.printError(err);
        return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);
      }
      console.log(id);
      return cb ? cb(null, id) : that.exitCli(conf.SUCCESS_EXIT);
    });
  }

  /**
   * Description
   * @method jlist
   * @param {} debug
   * @return
   */
  jlist (debug) {
    var that = this;

    that.Client.executeRemote('getMonitorData', {}, function(err, list) {
      if (err) {
        Common.printError(err);
        return that.exitCli(conf.ERROR_EXIT);
      }

      if (debug) {
        process.stdout.write(util.inspect(list, false, null, false));
      }
      else {
        process.stdout.write(JSON.stringify(list));
      }

      that.exitCli(conf.SUCCESS_EXIT);

    });
  }

  /**
   * Display system information
   * @method slist
   * @return
   */
  slist (tree) {
    this.Client.executeRemote('getSystemData', {}, (err, sys_infos) => {
      if (err) {
        Common.err(err)
        return this.exitCli(conf.ERROR_EXIT)
      }

      if (tree === true) {
        var treeify = require('./tools/treeify.js')
        console.log(treeify.asTree(sys_infos, true))
      }
      else
        process.stdout.write(util.inspect(sys_infos, false, null, false))
      this.exitCli(conf.SUCCESS_EXIT)
    })
  }

  /**
   * Description
   * @method speedList
   * @return
   */
  speedList (code, apps_acted) {
    var that = this;
    var systemdata = null
    var acted = []

    if ((code != 0 && code != null)) {
      return that.exitCli(code ? code : conf.SUCCESS_EXIT);
    }

    if (apps_acted && apps_acted.length > 0) {
      apps_acted.forEach(proc => {
        acted.push(proc.pm2_env ? proc.pm2_env.pm_id : proc.pm_id)
      })
    }

    // Do nothing if PM2 called programmatically and not called from CLI (also in exitCli)
    if ((conf.PM2_PROGRAMMATIC && process.env.PM2_USAGE != 'CLI'))
      return false;

    return that.Client.executeRemote('getMonitorData', {}, (err, proc_list) => {
      doList(err, proc_list)
    })

    function doList(err, list) {
      if (err) {
        if (that.gl_retry == 0) {
          that.gl_retry += 1;
          return setTimeout(that.speedList.bind(that), 1400);
        }
        console.error('Error retrieving process list: %s.\nA process seems to be on infinite loop, retry in 5 seconds',err);
        return that.exitCli(conf.ERROR_EXIT);
      }
      if (process.stdout.isTTY === false) {
        UX.list_min(list);
      }
      else if (commander.miniList && !commander.silent)
        UX.list_min(list);
      else if (!commander.silent) {
        if (that.gl_interact_infos) {
          var dashboard_url = `https://app.pm2.io/#/r/${that.gl_interact_infos.public_key}`

          if (that.gl_interact_infos.info_node != 'https://root.keymetrics.io') {
            dashboard_url = `${that.gl_interact_infos.info_node}/#/r/${that.gl_interact_infos.public_key}`
          }

          Common.printOut('%s PM2+ activated | Instance Name: %s | Dash: %s',
                          chalk.green.bold('⇆'),
                          chalk.bold(that.gl_interact_infos.machine_name),
                          chalk.bold(dashboard_url))
        }
        UX.list(list, commander);
        //Common.printOut(chalk.white.italic(' Use `pm2 show <id|name>` to get more details about an app'));
      }

      if (that.Client.daemon_mode == false) {
        Common.printOut('[--no-daemon] Continue to stream logs');
        Common.printOut('[--no-daemon] Exit on target PM2 exit pid=' + fs.readFileSync(conf.PM2_PID_FILE_PATH).toString());
        global._auto_exit = true;
        return that.streamLogs('all', 0, false, 'HH:mm:ss', false);
      }
      // if (process.stdout.isTTY) if looking for start logs
      else if (!process.env.TRAVIS && process.env.NODE_ENV != 'test' && acted.length > 0 && (commander.attach === true)) {
        Common.info(`Log streaming apps id: ${chalk.cyan(acted.join(' '))}, exit with Ctrl-C or will exit in 10secs`)

        // setTimeout(() => {
        //   Common.info(`Log streaming exited automatically, run 'pm2 logs' to continue watching logs`)
        //   return that.exitCli(code ? code : conf.SUCCESS_EXIT);
        // }, 10000)

        return acted.forEach((proc_name) => {
          that.streamLogs(proc_name, 0, false, null, false);
        })
      }
      else {
        return that.exitCli(code ? code : conf.SUCCESS_EXIT);
      }
    }
  }

  /**
   * Scale up/down a process
   * @method scale
   */
  scale (app_name, number, cb) {
    var that = this;

    function addProcs(proc, value, cb) {
      (function ex(proc, number) {
        if (number-- === 0) return cb();
        Common.printOut(conf.PREFIX_MSG + 'Scaling up application');
        that.Client.executeRemote('duplicateProcessId', proc.pm2_env.pm_id, ex.bind(this, proc, number));
      })(proc, number);
    }

    function rmProcs(procs, value, cb) {
      var i = 0;

      (function ex(procs, number) {
        if (number++ === 0) return cb();
        that._operate('deleteProcessId', procs[i++].pm2_env.pm_id, ex.bind(this, procs, number));
      })(procs, number);
    }

    function end() {
      return cb ? cb(null, {success:true}) : that.speedList();
    }

    this.Client.getProcessByName(app_name, function(err, procs) {
      if (err) {
        Common.printError(err);
        return cb ? cb(Common.retErr(err)) : that.exitCli(conf.ERROR_EXIT);
      }

      if (!procs || procs.length === 0) {
        Common.printError(conf.PREFIX_MSG_ERR + 'Application %s not found', app_name);
        return cb ? cb(new Error('App not found')) : that.exitCli(conf.ERROR_EXIT);
      }

      var proc_number = procs.length;

      if (typeof(number) === 'string' && number.indexOf('+') >= 0) {
        number = parseInt(number, 10);
        return addProcs(procs[0], number, end);
      }
      else if (typeof(number) === 'string' && number.indexOf('-') >= 0) {
        number = parseInt(number, 10);
        return rmProcs(procs[0], number, end);
      }
      else {
        number = parseInt(number, 10);
        number = number - proc_number;

        if (number < 0)
          return rmProcs(procs, number, end);
        else if (number > 0)
          return addProcs(procs[0], number, end);
        else {
          Common.printError(conf.PREFIX_MSG_ERR + 'Nothing to do');
          return cb ? cb(new Error('Same process number')) : that.exitCli(conf.ERROR_EXIT);
        }
      }
    });
  }

  /**
   * Description
   * @method describeProcess
   * @param {} pm2_id
   * @return
   */
  describe (pm2_id, cb) {
    var that = this;

    var found_proc = [];

    that.Client.executeRemote('getMonitorData', {}, function(err, list) {
      if (err) {
        Common.printError('Error retrieving process list: ' + err);
        that.exitCli(conf.ERROR_EXIT);
      }

      list.forEach(function(proc) {
        if ((!isNaN(pm2_id)    && proc.pm_id == pm2_id) ||
          (typeof(pm2_id) === 'string' && proc.name  == pm2_id)) {
          found_proc.push(proc);
        }
      });

      if (found_proc.length === 0) {
        Common.printError(conf.PREFIX_MSG_WARNING + '%s doesn\'t exist', pm2_id);
        return cb ? cb(null, []) : that.exitCli(conf.ERROR_EXIT);
      }

      if (!cb) {
        found_proc.forEach(function(proc) {
          UX.describe(proc);
        });
      }

      return cb ? cb(null, found_proc) : that.exitCli(conf.SUCCESS_EXIT);
    });
  }

  /**
   * API method to perform a deep update of PM2
   * @method deepUpdate
   */
  deepUpdate (cb) {
    var that = this;

    Common.printOut(conf.PREFIX_MSG + 'Updating PM2...');

    var child = sexec("npm i -g pm2@latest; pm2 update");

    child.stdout.on('end', function() {
      Common.printOut(conf.PREFIX_MSG + 'PM2 successfully updated');
      cb ? cb(null, {success:true}) : that.exitCli(conf.SUCCESS_EXIT);
    });
  }
};


//////////////////////////
// Load all API methods //
//////////////////////////

require('./API/Extra.js')(API);
require('./API/Deploy.js')(API);
require('./API/Modules/index.js')(API);

require('./API/pm2-plus/link.js')(API);
require('./API/pm2-plus/process-selector.js')(API);
require('./API/pm2-plus/helpers.js')(API);

require('./API/Configuration.js')(API);
require('./API/Version.js')(API);
require('./API/Startup.js')(API);
require('./API/LogManagement.js')(API);
require('./API/Containerizer.js')(API);


module.exports = API;