@milaboratories/pl-deployments 1.7.2 → 1.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,11 +1,11 @@
1
- "use strict";var it=Object.defineProperty;var nt=(o,r,e)=>r in o?it(o,r,{enumerable:!0,configurable:!0,writable:!0,value:e}):o[r]=e;var f=(o,r,e)=>nt(o,typeof r!="symbol"?r+"":r,e);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const ot=require("node:child_process"),w=require("@milaboratories/ts-helpers"),D=require("node:fs"),m=require("node:fs/promises"),h=require("upath"),st=require("undici"),_=require("node:stream"),at=require("node:stream/consumers"),ct=require("tar"),lt=require("decompress"),F=require("node:os"),S=require("ssh2"),q=require("node:net"),dt=require("node:dns"),x=require("node:crypto"),v=require("@milaboratories/pl-config"),p=require("zod");function ut(o){const r=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(o){for(const e in o)if(e!=="default"){const t=Object.getOwnPropertyDescriptor(o,e);Object.defineProperty(r,e,t.get?t:{enumerable:!0,get:()=>o[e]})}}return r.default=o,Object.freeze(r)}const ht=ut(ct);function wt(o,r){return o.info(`Running:
1
+ "use strict";var st=Object.defineProperty;var at=(n,r,e)=>r in n?st(n,r,{enumerable:!0,configurable:!0,writable:!0,value:e}):n[r]=e;var f=(n,r,e)=>at(n,typeof r!="symbol"?r+"":r,e);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const ct=require("node:child_process"),w=require("@milaboratories/ts-helpers"),F=require("node:fs"),g=require("node:fs/promises"),h=require("upath"),lt=require("undici"),B=require("node:stream"),ut=require("node:stream/consumers"),dt=require("tar"),ht=require("decompress"),C=require("node:os"),S=require("ssh2"),q=require("node:net"),wt=require("node:dns"),D=require("node:crypto"),v=require("@milaboratories/pl-config"),m=require("zod");function H(n){const r=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(n){for(const e in n)if(e!=="default"){const t=Object.getOwnPropertyDescriptor(n,e);Object.defineProperty(r,e,t.get?t:{enumerable:!0,get:()=>n[e]})}}return r.default=n,Object.freeze(r)}const ft=H(dt),pt=H(C);function mt(n,r){return n.info(`Running:
2
2
  cmd: ${JSON.stringify([r.cmd,...r.args])}
3
- wd: ${r.opts.cwd}`),o.info(" spawning child process"),ot.spawn(r.cmd,r.args,r.opts)}async function O(o){try{return process.kill(o,0),!0}catch{return!1}}function H(o){return process.kill(o,"SIGINT")}async function G(o,r){let t=0;for(;await O(o);)if(await w.sleep(100),t+=100,t>r)throw new Error(`The process did not stopped after ${r} ms.`)}const ft=["linux","macos","windows"];function k(o){switch(o.toLowerCase()){case"darwin":return"macos";case"linux":return"linux";case"win32":return"windows";default:throw new Error(`operating system '${o}' is not currently supported by Platforma ecosystem. The list of OSes supported: `+JSON.stringify(ft))}}const pt=["amd64","arm64"];function P(o){switch(o){case"aarch64":case"aarch64_be":case"arm64":return"arm64";case"x86_64":case"x64":return"amd64";default:throw new Error(`processor architecture '${o}' is not currently supported by Platforma ecosystem. The list of architectures supported: `+JSON.stringify(pt))}}const mt="https://cdn.platforma.bio/software",gt="https://cdn-ga.pl-open.science/software";async function yt(o,r,e,t,i,n){const s=M(e,t,r,P(i),k(n)),{archiveUrl:a,alternativeArchiveGAUrl:c,archivePath:l}=s;try{await C(o,a,l),s.wasDownloadedFrom=a}catch{await C(o,c,l),s.wasDownloadedFrom=c}return s}async function vt(o,r,e,t,i,n){const s=M(e,t,r,P(i),k(n)),{archiveUrl:a,alternativeArchiveGAUrl:c,archivePath:l,archiveType:d,targetFolder:u}=s;try{await C(o,a,l),s.wasDownloadedFrom=a}catch{await C(o,c,l),s.wasDownloadedFrom=c}return await Pt(o,l,d,u),s}function M(o,r,e,t,i){const n=`${r}-${t}`,s=St[i],a=`${n}.${s}`,c=`${mt}/${o}/${i}/${a}`,l=`${gt}/${o}/${i}/${a}`,d=h.join(e,a),u=h.join(e,n);return{archiveUrl:c,alternativeArchiveGAUrl:l,archivePath:d,archiveType:s,targetFolder:u,baseName:n}}async function C(o,r,e){const t={};t.dstArchive=e;try{if(t.fileExisted=await w.fileExists(e),t.fileExisted)return o.info(`Platforma Backend archive download skipped: '${e}' already exists`),t;await m.mkdir(h.dirname(e),{recursive:!0}),t.dirnameCreated=!0,o.info(`Downloading archive:
3
+ wd: ${r.opts.cwd}`),n.info(" spawning child process"),ct.spawn(r.cmd,r.args,r.opts)}async function x(n){try{return process.kill(n,0),!0}catch{return!1}}function M(n){return process.kill(n,"SIGINT")}async function G(n,r){let t=0;for(;await x(n);)if(await w.sleep(100),t+=100,t>r)throw new Error(`The process did not stopped after ${r} ms.`)}const gt=["linux","macos","windows"];function k(n){switch(n.toLowerCase()){case"darwin":return"macos";case"linux":return"linux";case"win32":return"windows";default:throw new Error(`operating system '${n}' is not currently supported by Platforma ecosystem. The list of OSes supported: `+JSON.stringify(gt))}}const yt=["amd64","arm64"];function P(n){switch(n){case"aarch64":case"aarch64_be":case"arm64":return"arm64";case"x86_64":case"x64":return"amd64";default:throw new Error(`processor architecture '${n}' is not currently supported by Platforma ecosystem. The list of architectures supported: `+JSON.stringify(yt))}}const vt="https://cdn.platforma.bio/software",$t="https://cdn-ga.pl-open.science/software";async function Pt(n,r,e,t,i,o){const s=z(e,t,r,P(i),k(o)),{archiveUrl:a,alternativeArchiveGAUrl:c,archivePath:l}=s;try{await E(n,a,l),s.wasDownloadedFrom=a}catch{await E(n,c,l),s.wasDownloadedFrom=c}return s}async function St(n,r,e,t,i,o){const s=z(e,t,r,P(i),k(o)),{archiveUrl:a,alternativeArchiveGAUrl:c,archivePath:l,archiveType:u,targetFolder:d}=s;try{await E(n,a,l),s.wasDownloadedFrom=a}catch{await E(n,c,l),s.wasDownloadedFrom=c}return await Et(n,l,u,d),s}function z(n,r,e,t,i){const o=`${r}-${t}`,s=bt[i],a=`${o}.${s}`,c=`${vt}/${n}/${i}/${a}`,l=`${$t}/${n}/${i}/${a}`,u=h.join(e,a),d=h.join(e,o);return{archiveUrl:c,alternativeArchiveGAUrl:l,archivePath:u,archiveType:s,targetFolder:d,baseName:o}}async function E(n,r,e){const t={};t.dstArchive=e;try{if(t.fileExisted=await w.fileExists(e),t.fileExisted)return n.info(`Platforma Backend archive download skipped: '${e}' already exists`),t;await g.mkdir(h.dirname(e),{recursive:!0}),t.dirnameCreated=!0,n.info(`Downloading archive:
4
4
  URL: ${r}
5
- Save to: ${e}`);const{body:i,statusCode:n}=await st.request(r);if(t.statusCode=n,n!=200){const s=await at.text(i);throw t.errorMsg=`failed to download archive: ${n}, response: ${s.slice(0,1e3)}`,o.error(t.errorMsg),new Error(t.errorMsg)}return t.tmpPath=e+".tmp",await _.Readable.toWeb(i).pipeTo(_.Writable.toWeb(D.createWriteStream(t.tmpPath))),t.wroteTmp=!0,t.tmpExisted=await w.fileExists(t.tmpPath),await m.rename(t.tmpPath,e),t.renamed=!0,t.newExisted=await w.fileExists(e),t}catch(i){const n=`downloadArchive: ${JSON.stringify(i)}, state: ${JSON.stringify(t)}`;throw o.error(n),new Error(n)}}const $t=".ok";async function Pt(o,r,e,t){if(o.info("extracting archive..."),o.info(` archive path: '${r}'`),o.info(` target dir: '${t}'`),!await w.fileExists(r)){const n=`Platforma Backend binary archive not found at '${r}'`;throw o.error(n),new Error(n)}const i=h.join(t,$t);if(await w.fileExists(i)){o.info(`Platforma Backend binaries unpack skipped: '${t}' exists`);return}switch(await w.fileExists(t)&&(o.info(`Removing previous incompletely unpacked folder: '${t}'`),await m.rm(t,{recursive:!0})),o.info(` creating target dir '${t}'`),await m.mkdir(t,{recursive:!0}),o.info(`Unpacking Platforma Backend archive:
5
+ Save to: ${e}`);const{body:i,statusCode:o}=await lt.request(r);if(t.statusCode=o,o!=200){const s=await ut.text(i);throw t.errorMsg=`failed to download archive: ${o}, response: ${s.slice(0,1e3)}`,n.error(t.errorMsg),new Error(t.errorMsg)}return t.tmpPath=e+".tmp",await B.Readable.toWeb(i).pipeTo(B.Writable.toWeb(F.createWriteStream(t.tmpPath))),t.wroteTmp=!0,t.tmpExisted=await w.fileExists(t.tmpPath),await g.rename(t.tmpPath,e),t.renamed=!0,t.newExisted=await w.fileExists(e),t}catch(i){const o=`downloadArchive: ${JSON.stringify(i)}, state: ${JSON.stringify(t)}`;throw n.error(o),new Error(o)}}const Ct=".ok";async function Et(n,r,e,t){if(n.info("extracting archive..."),n.info(` archive path: '${r}'`),n.info(` target dir: '${t}'`),!await w.fileExists(r)){const o=`Platforma Backend binary archive not found at '${r}'`;throw n.error(o),new Error(o)}const i=h.join(t,Ct);if(await w.fileExists(i)){n.info(`Platforma Backend binaries unpack skipped: '${t}' exists`);return}switch(await w.fileExists(t)&&(n.info(`Removing previous incompletely unpacked folder: '${t}'`),await g.rm(t,{recursive:!0})),n.info(` creating target dir '${t}'`),await g.mkdir(t,{recursive:!0}),n.info(`Unpacking Platforma Backend archive:
6
6
  Archive: ${r}
7
- Target dir: ${t}`),e){case"tgz":await ht.x({file:r,cwd:t,gzip:!0});break;case"zip":await lt(r,t);break;default:w.assertNever(e)}await m.writeFile(i,"ok"),o.info(" ... unpack done.")}const St={linux:"tgz",macos:"tgz",windows:"zip"};function E(){return"1.29.2"}function Ct(){return{type:"Download",version:E()}}async function Et(o,r,e){switch(e.type){case"Download":const t=await vt(o,r,"pl",`pl-${e.version}`,F.arch(),F.platform());return h.join(t.baseName,"binaries",bt[k(F.platform())]);case"Local":return e.path;default:w.assertNever(e)}}const bt={linux:"platforma",macos:"platforma",windows:"platforma.exe"};function z(o){return h.join(o,"pl_pid")}async function At(o){if(!await w.fileExists(o))return;const r=await m.readFile(o);return Number(r.toString())}async function Ft(o,r){await m.writeFile(o,JSON.stringify(r))}function Dt(){return{}}function xt(o,r,e){return o[r]=e,e}async function R(o,r){const e=Dt();try{return await r((i,n)=>xt(e,i,n),e)}catch(t){throw o.error(`error ${t} while doing traced operation, state: ${JSON.stringify(e)}`),t}}const J="config-local.yaml";class L{constructor(r,e,t,i,n,s,a,c){f(this,"instance");f(this,"pid");f(this,"nRuns",0);f(this,"lastRunHistory",{});f(this,"wasStopped",!1);this.logger=r,this.workingDir=e,this.startOptions=t,this.initialStartHistory=i,this.onClose=n,this.onError=s,this.onCloseAndError=a,this.onCloseAndErrorNoStop=c}async start(){await R(this.logger,async(r,e)=>{this.wasStopped=!1;const t=wt(this.logger,this.startOptions);t.on("error",n=>{this.logger.error(`error '${n}', while running platforma, started opts: ${JSON.stringify(this.debugInfo())}`),this.onError!==void 0&&this.onError(this),this.onCloseAndError!==void 0&&this.onCloseAndError(this),this.onCloseAndErrorNoStop!==void 0&&!this.wasStopped&&this.onCloseAndErrorNoStop(this)}),t.on("close",()=>{this.logger.warn(`platforma was closed, started opts: ${JSON.stringify(this.debugInfo())}`),this.onClose!==void 0&&this.onClose(this),this.onCloseAndError!==void 0&&this.onCloseAndError(this),this.onCloseAndErrorNoStop!==void 0&&!this.wasStopped&&this.onCloseAndErrorNoStop(this)}),r("started",!0);const i=r("pidFile",z(this.workingDir));r("pid",w.notEmpty(t.pid)),r("pidWritten",await Ft(i,w.notEmpty(t.pid))),this.nRuns++,this.instance=t,this.pid=t.pid,this.lastRunHistory=e})}stop(){this.wasStopped=!0,H(w.notEmpty(this.pid))}async waitStopped(){await G(w.notEmpty(this.pid),15e3)}stopped(){return this.wasStopped}async isAlive(){return await O(w.notEmpty(this.pid))}debugInfo(){return{lastRunHistory:this.lastRunHistory,nRuns:this.nRuns,pid:this.pid,workingDir:this.workingDir,initialStartHistory:this.initialStartHistory,wasStopped:this.wasStopped}}}async function Ot(o,r){const e={plBinary:Ct(),spawnOptions:{},closeOld:!0,...r};return await R(o,async(t,i)=>{t("startOptions",{...e,config:"too wordy"});const n=h.resolve(e.workingDir);e.closeOld&&t("closeOld",await kt(o,n));const s=h.join(n,J);o.info(`writing configuration '${s}'...`),await m.writeFile(s,e.config);const a=h.join(n,"binaries"),c=await Et(o,a,e.plBinary),d={cmd:t("binaryPath",h.join("binaries",c)),args:["--config",s],opts:{env:{...process.env},cwd:n,stdio:["pipe","ignore","inherit"],windowsHide:!0,...e.spawnOptions}};t("processOpts",{cmd:d.cmd,args:d.args,cwd:d.opts.cwd});const u=new L(o,e.workingDir,d,i,e.onClose,e.onError,e.onCloseAndError,e.onCloseAndErrorNoStop);return await u.start(),u})}async function kt(o,r){return await R(o,async(e,t)=>{const i=e("pidFilePath",z(r)),n=e("pid",await At(i)),s=e("wasAlive",await O(n));return n!==void 0&&s&&(e("stopped",H(n)),e("waitStopped",await G(n,1e4))),t})}const Rt={keepaliveInterval:6e4,keepaliveCountMax:10};class b{constructor(r,e){f(this,"config");f(this,"homeDir");f(this,"forwardedServers",[]);this.logger=r,this.client=e}static async init(r,e){const t={...Rt,...e},i=new b(r,new S.Client);return await i.connect(t),i}getForwardedServers(){return this.forwardedServers}getFullHostName(){var r,e;return`${(r=this.config)==null?void 0:r.host}:${(e=this.config)==null?void 0:e.port}`}getUserName(){var r;return(r=this.config)==null?void 0:r.username}async connect(r){return this.config=r,await Nt(this.client,r)}async exec(r){return new Promise((e,t)=>{this.client.exec(r,(i,n)=>{if(i)return t(new Error(`ssh.exec: ${r}: ${i}`));let s="",a="";n.on("close",c=>{c===0?e({stdout:s,stderr:a}):t(new Error(`Command ${r} exited with code ${c}, stdout: ${s}, stderr: ${a}`))}).on("data",c=>{s+=c.toString()}).stderr.on("data",c=>{a+=c.toString()})})})}static async getAuthTypes(r,e){return new Promise(t=>{let i="";const n=new S.Client;n.on("ready",()=>{n.end();const s=this.extractAuthMethods(i);t(s.length===0?["publickey","password"]:s)}),n.on("error",()=>{n.end(),t(["publickey","password"])}),n.connect({host:r,port:e,username:new Date().getTime().toString(),debug:s=>{i+=`${s}
8
- `}})})}static extractAuthMethods(r){const e=r.match(/Inbound: Received USERAUTH_FAILURE \((.+)\)/);return e&&e[1]?e[1].split(",").map(t=>t.trim()):[]}async forwardPort(r,e){const t=`ssh.forward:${r.localPort}:${r.remotePort}.id_${x.randomBytes(1).toString("hex")}`;e=e??this.config;const i=new w.RetryablePromise(n=>new Promise((s,a)=>{const c=new S.Client;c.on("ready",()=>{this.logger.info(`${t}.client.ready`),s(c)}),c.on("error",l=>{this.logger.info(`${t}.client.error: ${l}`),n.reset(),a(l)}),c.on("close",()=>{this.logger.info(`${t}.client.closed`),n.reset()}),c.connect(e)}));return await i.ensure(),new Promise((n,s)=>{const a=q.createServer({pauseOnConnect:!0},async c=>{const l=`${t}.sock_${x.randomBytes(1).toString("hex")}`;let d;try{d=await i.ensure()}catch(y){this.logger.info(`${l}.persistentClient.catch: ${y}`),c.end();return}d.setNoDelay(!0),c.setNoDelay(!0);let u;try{u=await Bt(this.logger,d,"127.0.0.1",0,"127.0.0.1",r.remotePort)}catch(y){this.logger.error(`${l}.forwardOut.err: ${y}`),c.end();return}c.pipe(u),u.pipe(c),c.resume(),u.on("error",y=>{this.logger.error(`${l}.stream.error: ${y}`),c.end(),u.end()}),u.on("close",()=>{c.end(),u.end()}),c.on("close",()=>{this.logger.info(`${l}.localSocket: closed`),c.end(),u.end()})});a.listen(r.localPort,"127.0.0.1",()=>{this.logger.info(`${t}.server: started listening`),this.forwardedServers.push(a),n({server:a})}),a.on("error",c=>{a.close(),s(new Error(`${t}.server: error: ${JSON.stringify(c)}`))}),a.on("close",()=>{this.logger.info(`${t}.server: closed ${JSON.stringify(r)}`),this.forwardedServers=this.forwardedServers.filter(c=>c!==a)})})}closeForwardedPorts(){this.logger.info("[SSH] Closing all forwarded ports..."),this.forwardedServers.forEach(r=>{const e=r.address();if(e&&typeof e!="string"){const t=e;this.logger.info(`[SSH] Closing port forward for server ${t.address}:${t.port}`)}r.close()}),this.forwardedServers=[]}static async checkHostAvailability(r){return new Promise(e=>{dt.lookup(r,t=>{e(!t)})})}static async isPassphraseRequiredForKey(r){return new Promise((e,t)=>{try{return S.utils.parseKey(r)instanceof Error&&e(!0),e(!1)}catch(i){console.log("Error parsing privateKey"),t(new Error(`ssh.isPassphraseRequiredForKey: err ${i}`))}})}async uploadFile(r,e){return await this.withSftp(async t=>new Promise((i,n)=>{t.fastPut(r,e,s=>{if(s){const a=new Error(`ssh.uploadFile: err: ${s}, localPath: ${r}, remotePath: ${e}`);return n(a)}i(!0)})}))}async withSftp(r){return new Promise((e,t)=>{this.client.sftp((i,n)=>{if(i)return t(new Error(`ssh.withSftp: sftp err: ${i}`));r(n).then(e).catch(s=>{t(new Error(`ssh.withSftp.callback: err ${s}`))}).finally(()=>{n==null||n.end()})})})}async writeFileOnTheServer(r,e,t=432){return this.withSftp(async i=>this.writeFile(i,r,e,t))}async getForderStructure(r,e,t={files:[],directories:[]}){return new Promise((i,n)=>{r.readdir(e,async(s,a)=>{if(s)return n(s);for(const c of a){const l=`${e}/${c.filename}`;if(c.attrs.isDirectory()){t.directories.push(l);try{await this.getForderStructure(r,l,t)}catch(d){return n(d instanceof Error?d:new Error(String(d)))}}else t.files.push(l)}i(t)})})}rmdir(r,e){return new Promise((t,i)=>{r.rmdir(e,n=>n?i(n):t(!0))})}unlink(r,e){return new Promise((t,i)=>{r.unlink(e,n=>n?i(n):t(!0))})}async deleteFolder(r){return this.withSftp(async e=>{try{const t=await this.getForderStructure(e,r);this.logger.info("ssh.deleteFolder list of files and directories"),this.logger.info(`ssh.deleteFolder list of files: ${t.files}`),this.logger.info(`ssh.deleteFolder list of directories: ${t.directories}`);for(const i of t.files)this.logger.info(`ssh.deleteFolder unlink file ${i}`),await this.unlink(e,i);t.directories.sort((i,n)=>n.length-i.length);for(const i of t.directories)this.logger.info(`ssh.deleteFolder rmdir ${i}`),await this.rmdir(e,i);return await this.rmdir(e,r),!0}catch(t){this.logger.error(t);const i=t instanceof Error?t.message:"";throw new Error(`ssh.deleteFolder: path: ${r}, message: ${i}`)}})}async readFile(r){return this.withSftp(async e=>new Promise((t,i)=>{e.readFile(r,(n,s)=>{if(n)return i(new Error(`ssh.readFile: ${n}`));t(s.toString())})}))}async chmod(r,e){return this.withSftp(async t=>new Promise((i,n)=>{t.chmod(r,e,s=>s?n(new Error(`ssh.chmod: ${s}, path: ${r}, mode: ${e}`)):i(void 0))}))}async checkFileExists(r){return this.withSftp(async e=>new Promise((t,i)=>{e.stat(r,(n,s)=>{if(n)return(n==null?void 0:n.code)===2?t(!1):i(new Error(`ssh.checkFileExists: err ${n}`));t(s.isFile())})}))}async checkPathExists(r){return this.withSftp(async e=>new Promise((t,i)=>{e.stat(r,(n,s)=>{if(n)return n.code===2?t({exists:!1,isFile:!1,isDirectory:!1}):i(new Error(`ssh.checkPathExists: ${n}`));t({exists:!0,isFile:s.isFile(),isDirectory:s.isDirectory()})})}))}async writeFile(r,e,t,i=432){return new Promise((n,s)=>{r.writeFile(e,t,{mode:i},a=>{if(a)return s(new Error(`ssh.writeFile: err ${a}, remotePath: ${e}`));n(!0)})})}uploadFileUsingExistingSftp(r,e,t,i=432){return new Promise((n,s)=>{m.readFile(e).then(async a=>this.writeFile(r,t,a,i).then(()=>{n(void 0)}).catch(c=>{const l=`uploadFileUsingExistingSftp: ${c}`;this.logger.error(l),s(new Error(l))}))})}async __uploadDirectory(r,e,t,i=432){return new Promise((n,s)=>{D.readdir(e,async(a,c)=>{if(a)return s(new Error(`ssh.__uploadDir: err ${a}, localDir: ${e}, remoteDir: ${t}`));try{await this.__createRemoteDirectory(r,t);for(const l of c){const d=h.join(e,l),u=`${t}/${l}`;D.lstatSync(d).isDirectory()?await this.__uploadDirectory(r,d,u,i):await this.uploadFileUsingExistingSftp(r,d,u,i)}n()}catch(l){const d=`ssh.__uploadDir: catched err ${l}`;this.logger.error(d),s(new Error(d))}})})}async uploadDirectory(r,e,t=432){return new Promise((i,n)=>{this.withSftp(async s=>{try{await this.__uploadDirectory(s,r,e,t),i()}catch(a){n(new Error(`ssh.uploadDirectory: ${a}`))}})})}__createRemoteDirectory(r,e){return new Promise((t,i)=>{const n=e.split("/");let s="";const a=c=>{if(c>=n.length)return t();s+=`${n[c]}/`,r.stat(s,l=>{l?r.mkdir(s,d=>{if(d)return i(new Error(`ssh.__createRemDir: err ${d}, remotePath: ${e}`));a(c+1)}):a(c+1)})};a(0)})}ensureRemoteDirCreated(r,e=493){return this.withSftp(async t=>{const i=r.split("/");let n="";for(const s of i){n+=`${s}/`;try{await new Promise((a,c)=>{t.stat(n,l=>{if(!l)return a();t.mkdir(n,{mode:e},d=>{if(d)return c(new Error(`ssh.createRemoteDir: err ${d}, remotePath: ${r}`));a()})})})}catch(a){throw console.error(`Failed to create directory: ${n}`,a),a}}})}async downloadFile(r,e){return this.withSftp(async t=>new Promise((i,n)=>{t.fastGet(r,e,s=>{if(s)return n(new Error(`ssh.downloadFile: err ${s}, remotePath: ${r}, localPath: ${e}`));i(!0)})}))}close(){this.closeForwardedPorts(),this.client.end()}}async function Nt(o,r,e,t){return new Promise((i,n)=>{o.on("ready",()=>{i(o)}),o.on("error",s=>{n(new Error(`ssh.connect: ${s}`))}),o.on("close",()=>{}),o.connect(r),o.setNoDelay(!0)})}async function Bt(o,r,e,t,i,n){return new Promise((s,a)=>{r.forwardOut(e,t,i,n,(c,l)=>c?(o.error(`forwardOut.error: ${c}`),a(c)):s(l))})}const _t="minio-2024-12-18T13-15-44Z",Ut="supervisord-0.7.3",It="supervisord_0.7.3_Linux_64-bit";function g(o){return h.join(o,".platforma_ssh")}function A(o){return h.join(g(o),"binaries")}function Tt(o,r){return h.join(A(o),`pl-${E()}-${P(r)}`)}function V(o,r){return h.join(Tt(o,r),"binaries")}function U(o,r){return h.join(V(o,r),"platforma")}function jt(o,r){return h.join(V(o,r),"free-port")}function W(o,r){return h.join(A(o),`minio-2024-12-18T13-15-44Z-${P(r)}`)}function qt(o,r){return h.join(W(o,r),"minio")}function Ht(o,r){return h.join(A(o),`supervisord-0.7.3-${P(r)}`,It)}function K(o,r){return h.join(Ht(o,r),"supervisord")}function Y(o){return h.join(g(o),"supervisor.conf")}function I(o){return h.join(g(o),"connection.txt")}async function Gt(o,r,e){const t=await N(o,r,e,"--daemon");if(t.stderr)throw new Error(`Can not run ssh Platforma ${t.stderr}`)}async function Mt(o,r,e){const t=await N(o,r,e,"ctl shutdown");if(t.stderr)throw new Error(`Can not stop ssh Platforma ${t.stderr}`)}async function zt(o,r,e,t){let i;try{i=await N(r,e,t,"ctl status")}catch(c){return{execError:String(c),allAlive:!1}}if(i.stderr)return o.info(`supervisord ctl status: stderr occurred: ${i.stderr}, stdout: ${i.stdout}`),{rawResult:i,allAlive:!1};const n=T(i.stdout,"platforma"),s=T(i.stdout,"minio"),a={rawResult:i,platforma:n,minio:s,allAlive:n&&s};return a.allAlive||(a.minio||o.warn("Minio is not running on the server"),a.platforma||o.warn("Platforma is not running on the server")),a}function Jt(o,r,e,t,i,n,s){const a=Object.entries(r).map(([d,u])=>`${d}="${u}"`).join(","),c=x.randomBytes(16).toString("hex"),l=e;return`
7
+ Target dir: ${t}`),e){case"tgz":await ft.x({file:r,cwd:t,gzip:!0});break;case"zip":await ht(r,t);break;default:w.assertNever(e)}await g.writeFile(i,"ok"),n.info(" ... unpack done.")}const bt={linux:"tgz",macos:"tgz",windows:"zip"};function b(){return"1.31.2"}function At(){return{type:"Download",version:b()}}async function Ot(n,r,e){switch(e.type){case"Download":const t=await St(n,r,"pl",`pl-${e.version}`,C.arch(),C.platform());return h.join(t.baseName,"binaries",Ft[k(C.platform())]);case"Local":return e.path;default:w.assertNever(e)}}const Ft={linux:"platforma",macos:"platforma",windows:"platforma.exe"};function J(n){return h.join(n,"pl_pid")}async function Dt(n){if(!await w.fileExists(n))return;const r=await g.readFile(n);return Number(r.toString())}async function xt(n,r){await g.writeFile(n,JSON.stringify(r))}function kt(){return{}}function Rt(n,r,e){return n[r]=e,e}async function R(n,r){const e=kt();try{return await r((i,o)=>Rt(e,i,o),e)}catch(t){throw n.error(`error ${t} while doing traced operation, state: ${JSON.stringify(e)}`),t}}const L="config-local.yaml";class V{constructor(r,e,t,i,o,s,a,c){f(this,"instance");f(this,"pid");f(this,"nRuns",0);f(this,"lastRunHistory",{});f(this,"wasStopped",!1);this.logger=r,this.workingDir=e,this.startOptions=t,this.initialStartHistory=i,this.onClose=o,this.onError=s,this.onCloseAndError=a,this.onCloseAndErrorNoStop=c}async start(){await R(this.logger,async(r,e)=>{this.wasStopped=!1;const t=mt(this.logger,this.startOptions);t.on("error",o=>{this.logger.error(`error '${o}', while running platforma, started opts: ${JSON.stringify(this.debugInfo())}`),this.onError!==void 0&&this.onError(this),this.onCloseAndError!==void 0&&this.onCloseAndError(this),this.onCloseAndErrorNoStop!==void 0&&!this.wasStopped&&this.onCloseAndErrorNoStop(this)}),t.on("close",()=>{this.logger.warn(`platforma was closed, started opts: ${JSON.stringify(this.debugInfo())}`),this.onClose!==void 0&&this.onClose(this),this.onCloseAndError!==void 0&&this.onCloseAndError(this),this.onCloseAndErrorNoStop!==void 0&&!this.wasStopped&&this.onCloseAndErrorNoStop(this)}),r("started",!0);const i=r("pidFile",J(this.workingDir));r("pid",w.notEmpty(t.pid)),r("pidWritten",await xt(i,w.notEmpty(t.pid))),this.nRuns++,this.instance=t,this.pid=t.pid,this.lastRunHistory=e})}stop(){this.wasStopped=!0,M(w.notEmpty(this.pid))}async waitStopped(){await G(w.notEmpty(this.pid),15e3)}stopped(){return this.wasStopped}async isAlive(){return await x(w.notEmpty(this.pid))}debugInfo(){return{lastRunHistory:this.lastRunHistory,nRuns:this.nRuns,pid:this.pid,workingDir:this.workingDir,initialStartHistory:this.initialStartHistory,wasStopped:this.wasStopped}}}async function Nt(n,r){const e=Math.max(pt.cpus().length-2,1),t=W(r,e);return await R(n,async(i,o)=>{i("startOptions",{...t,config:"too wordy"});const s=h.resolve(t.workingDir);t.closeOld&&i("closeOld",await _t(n,s));const a=h.join(s,L);n.info(`writing configuration '${a}'...`),await g.writeFile(a,t.config);const c=h.join(s,"binaries"),l=await Ot(n,c,t.plBinary),u=i("binaryPath",h.join("binaries",l)),d=K(u,a,t,s,process.env);i("processOpts",{cmd:d.cmd,args:d.args,cwd:d.opts.cwd});const p=new V(n,t.workingDir,d,o,t.onClose,t.onError,t.onCloseAndError,t.onCloseAndErrorNoStop);return await p.start(),p})}async function _t(n,r){return await R(n,async(e,t)=>{const i=e("pidFilePath",J(r)),o=e("pid",await Dt(i)),s=e("wasAlive",await x(o));return o!==void 0&&s&&(e("stopped",M(o)),e("waitStopped",await G(o,1e4))),t})}function W(n,r){var i;const e={plBinary:At(),spawnOptions:{env:{GOMAXPROCS:String(r)}},closeOld:!0};if((i=n.spawnOptions)!=null&&i.env&&(e.spawnOptions.env={...e.spawnOptions.env,...n.spawnOptions.env}),n.spawnOptions){const o={...n.spawnOptions};delete o.env,e.spawnOptions={...e.spawnOptions,...o}}const t={...n};return delete t.spawnOptions,{...e,...t}}function K(n,r,e,t,i){var a;const o={cmd:n,args:["--config",r],opts:{env:{...i},cwd:t,stdio:["pipe","ignore","inherit"],windowsHide:!0}};(a=e.spawnOptions)!=null&&a.env&&(o.opts.env={...o.opts.env,...e.spawnOptions.env});const s={...e.spawnOptions};return delete s.env,o.opts={...o.opts,...s},o}const Bt={keepaliveInterval:6e4,keepaliveCountMax:10};class A{constructor(r,e){f(this,"config");f(this,"homeDir");f(this,"forwardedServers",[]);this.logger=r,this.client=e}static async init(r,e){const t={...Bt,...e},i=new A(r,new S.Client);return await i.connect(t),i}getForwardedServers(){return this.forwardedServers}getFullHostName(){var r,e;return`${(r=this.config)==null?void 0:r.host}:${(e=this.config)==null?void 0:e.port}`}getUserName(){var r;return(r=this.config)==null?void 0:r.username}async connect(r){return this.config=r,await Ut(this.client,r)}async exec(r){return new Promise((e,t)=>{this.client.exec(r,(i,o)=>{if(i)return t(new Error(`ssh.exec: ${r}: ${i}`));let s="",a="";o.on("close",c=>{c===0?e({stdout:s,stderr:a}):t(new Error(`Command ${r} exited with code ${c}, stdout: ${s}, stderr: ${a}`))}).on("data",c=>{s+=c.toString()}).stderr.on("data",c=>{a+=c.toString()})})})}static async getAuthTypes(r,e){return new Promise(t=>{let i="";const o=new S.Client;o.on("ready",()=>{o.end();const s=this.extractAuthMethods(i);t(s.length===0?["publickey","password"]:s)}),o.on("error",()=>{o.end(),t(["publickey","password"])}),o.connect({host:r,port:e,username:new Date().getTime().toString(),debug:s=>{i+=`${s}
8
+ `}})})}static extractAuthMethods(r){const e=r.match(/Inbound: Received USERAUTH_FAILURE \((.+)\)/);return e&&e[1]?e[1].split(",").map(t=>t.trim()):[]}async forwardPort(r,e){const t=`ssh.forward:${r.localPort}:${r.remotePort}.id_${D.randomBytes(1).toString("hex")}`;e=e??this.config;const i=new w.RetryablePromise(o=>new Promise((s,a)=>{const c=new S.Client;c.on("ready",()=>{this.logger.info(`${t}.client.ready`),s(c)}),c.on("error",l=>{this.logger.info(`${t}.client.error: ${l}`),o.reset(),a(l)}),c.on("close",()=>{this.logger.info(`${t}.client.closed`),o.reset()}),c.connect(e)}));return await i.ensure(),new Promise((o,s)=>{const a=q.createServer({pauseOnConnect:!0},async c=>{const l=`${t}.sock_${D.randomBytes(1).toString("hex")}`;let u;try{u=await i.ensure()}catch(p){this.logger.info(`${l}.persistentClient.catch: ${p}`),c.end();return}u.setNoDelay(!0),c.setNoDelay(!0);let d;try{d=await It(this.logger,u,"127.0.0.1",0,"127.0.0.1",r.remotePort)}catch(p){this.logger.error(`${l}.forwardOut.err: ${p}`),c.end();return}c.pipe(d),d.pipe(c),c.resume(),d.on("error",p=>{this.logger.error(`${l}.stream.error: ${p}`),c.end(),d.end()}),d.on("close",()=>{c.end(),d.end()}),c.on("close",()=>{this.logger.info(`${l}.localSocket: closed`),c.end(),d.end()})});a.listen(r.localPort,"127.0.0.1",()=>{this.logger.info(`${t}.server: started listening`),this.forwardedServers.push(a),o({server:a})}),a.on("error",c=>{a.close(),s(new Error(`${t}.server: error: ${JSON.stringify(c)}`))}),a.on("close",()=>{this.logger.info(`${t}.server: closed ${JSON.stringify(r)}`),this.forwardedServers=this.forwardedServers.filter(c=>c!==a)})})}closeForwardedPorts(){this.logger.info("[SSH] Closing all forwarded ports..."),this.forwardedServers.forEach(r=>{const e=r.address();if(e&&typeof e!="string"){const t=e;this.logger.info(`[SSH] Closing port forward for server ${t.address}:${t.port}`)}r.close()}),this.forwardedServers=[]}static async checkHostAvailability(r){return new Promise(e=>{wt.lookup(r,t=>{e(!t)})})}static async isPassphraseRequiredForKey(r){return new Promise((e,t)=>{try{return S.utils.parseKey(r)instanceof Error&&e(!0),e(!1)}catch(i){console.log("Error parsing privateKey"),t(new Error(`ssh.isPassphraseRequiredForKey: err ${i}`))}})}async uploadFile(r,e){return await this.withSftp(async t=>new Promise((i,o)=>{t.fastPut(r,e,s=>{if(s){const a=new Error(`ssh.uploadFile: err: ${s}, localPath: ${r}, remotePath: ${e}`);return o(a)}i(!0)})}))}async withSftp(r){return new Promise((e,t)=>{this.client.sftp((i,o)=>{if(i)return t(new Error(`ssh.withSftp: sftp err: ${i}`));r(o).then(e).catch(s=>{t(new Error(`ssh.withSftp.callback: err ${s}`))}).finally(()=>{o==null||o.end()})})})}async writeFileOnTheServer(r,e,t=432){return this.withSftp(async i=>this.writeFile(i,r,e,t))}async getForderStructure(r,e,t={files:[],directories:[]}){return new Promise((i,o)=>{r.readdir(e,async(s,a)=>{if(s)return o(s);for(const c of a){const l=`${e}/${c.filename}`;if(c.attrs.isDirectory()){t.directories.push(l);try{await this.getForderStructure(r,l,t)}catch(u){return o(u instanceof Error?u:new Error(String(u)))}}else t.files.push(l)}i(t)})})}rmdir(r,e){return new Promise((t,i)=>{r.rmdir(e,o=>o?i(o):t(!0))})}unlink(r,e){return new Promise((t,i)=>{r.unlink(e,o=>o?i(o):t(!0))})}async deleteFolder(r){return this.withSftp(async e=>{try{const t=await this.getForderStructure(e,r);this.logger.info("ssh.deleteFolder list of files and directories"),this.logger.info(`ssh.deleteFolder list of files: ${t.files}`),this.logger.info(`ssh.deleteFolder list of directories: ${t.directories}`);for(const i of t.files)this.logger.info(`ssh.deleteFolder unlink file ${i}`),await this.unlink(e,i);t.directories.sort((i,o)=>o.length-i.length);for(const i of t.directories)this.logger.info(`ssh.deleteFolder rmdir ${i}`),await this.rmdir(e,i);return await this.rmdir(e,r),!0}catch(t){this.logger.error(t);const i=t instanceof Error?t.message:"";throw new Error(`ssh.deleteFolder: path: ${r}, message: ${i}`)}})}async readFile(r){return this.withSftp(async e=>new Promise((t,i)=>{e.readFile(r,(o,s)=>{if(o)return i(new Error(`ssh.readFile: ${o}`));t(s.toString())})}))}async chmod(r,e){return this.withSftp(async t=>new Promise((i,o)=>{t.chmod(r,e,s=>s?o(new Error(`ssh.chmod: ${s}, path: ${r}, mode: ${e}`)):i(void 0))}))}async checkFileExists(r){return this.withSftp(async e=>new Promise((t,i)=>{e.stat(r,(o,s)=>{if(o)return(o==null?void 0:o.code)===2?t(!1):i(new Error(`ssh.checkFileExists: err ${o}`));t(s.isFile())})}))}async checkPathExists(r){return this.withSftp(async e=>new Promise((t,i)=>{e.stat(r,(o,s)=>{if(o)return o.code===2?t({exists:!1,isFile:!1,isDirectory:!1}):i(new Error(`ssh.checkPathExists: ${o}`));t({exists:!0,isFile:s.isFile(),isDirectory:s.isDirectory()})})}))}async writeFile(r,e,t,i=432){return new Promise((o,s)=>{r.writeFile(e,t,{mode:i},a=>{if(a)return s(new Error(`ssh.writeFile: err ${a}, remotePath: ${e}`));o(!0)})})}uploadFileUsingExistingSftp(r,e,t,i=432){return new Promise((o,s)=>{g.readFile(e).then(async a=>this.writeFile(r,t,a,i).then(()=>{o(void 0)}).catch(c=>{const l=`uploadFileUsingExistingSftp: ${c}`;this.logger.error(l),s(new Error(l))}))})}async __uploadDirectory(r,e,t,i=432){return new Promise((o,s)=>{F.readdir(e,async(a,c)=>{if(a)return s(new Error(`ssh.__uploadDir: err ${a}, localDir: ${e}, remoteDir: ${t}`));try{await this.__createRemoteDirectory(r,t);for(const l of c){const u=h.join(e,l),d=`${t}/${l}`;F.lstatSync(u).isDirectory()?await this.__uploadDirectory(r,u,d,i):await this.uploadFileUsingExistingSftp(r,u,d,i)}o()}catch(l){const u=`ssh.__uploadDir: catched err ${l}`;this.logger.error(u),s(new Error(u))}})})}async uploadDirectory(r,e,t=432){return new Promise((i,o)=>{this.withSftp(async s=>{try{await this.__uploadDirectory(s,r,e,t),i()}catch(a){o(new Error(`ssh.uploadDirectory: ${a}`))}})})}__createRemoteDirectory(r,e){return new Promise((t,i)=>{const o=e.split("/");let s="";const a=c=>{if(c>=o.length)return t();s+=`${o[c]}/`,r.stat(s,l=>{l?r.mkdir(s,u=>{if(u)return i(new Error(`ssh.__createRemDir: err ${u}, remotePath: ${e}`));a(c+1)}):a(c+1)})};a(0)})}ensureRemoteDirCreated(r,e=493){return this.withSftp(async t=>{const i=r.split("/");let o="";for(const s of i){o+=`${s}/`;try{await new Promise((a,c)=>{t.stat(o,l=>{if(!l)return a();t.mkdir(o,{mode:e},u=>{if(u)return c(new Error(`ssh.createRemoteDir: err ${u}, remotePath: ${r}`));a()})})})}catch(a){throw console.error(`Failed to create directory: ${o}`,a),a}}})}async downloadFile(r,e){return this.withSftp(async t=>new Promise((i,o)=>{t.fastGet(r,e,s=>{if(s)return o(new Error(`ssh.downloadFile: err ${s}, remotePath: ${r}, localPath: ${e}`));i(!0)})}))}close(){this.closeForwardedPorts(),this.client.end()}}async function Ut(n,r,e,t){return new Promise((i,o)=>{n.on("ready",()=>{i(n)}),n.on("error",s=>{o(new Error(`ssh.connect: ${s}`))}),n.on("close",()=>{}),n.connect(r),n.setNoDelay(!0)})}async function It(n,r,e,t,i,o){return new Promise((s,a)=>{r.forwardOut(e,t,i,o,(c,l)=>c?(n.error(`forwardOut.error: ${c}`),a(c)):s(l))})}const Tt="minio-2024-12-18T13-15-44Z",jt="supervisord-0.7.3",qt="supervisord_0.7.3_Linux_64-bit";function y(n){return h.join(n,".platforma_ssh")}function O(n){return h.join(y(n),"binaries")}function Ht(n,r){return h.join(O(n),`pl-${b()}-${P(r)}`)}function Y(n,r){return h.join(Ht(n,r),"binaries")}function U(n,r){return h.join(Y(n,r),"platforma")}function Mt(n,r){return h.join(Y(n,r),"free-port")}function Z(n,r){return h.join(O(n),`minio-2024-12-18T13-15-44Z-${P(r)}`)}function Gt(n,r){return h.join(Z(n,r),"minio")}function zt(n,r){return h.join(O(n),`supervisord-0.7.3-${P(r)}`,qt)}function X(n,r){return h.join(zt(n,r),"supervisord")}function Q(n){return h.join(y(n),"supervisor.conf")}function I(n){return h.join(y(n),"connection.txt")}async function Jt(n,r,e){const t=await N(n,r,e,"--daemon");if(t.stderr)throw new Error(`Can not run ssh Platforma ${t.stderr}`)}async function Lt(n,r,e){const t=await N(n,r,e,"ctl shutdown");if(t.stderr)throw new Error(`Can not stop ssh Platforma ${t.stderr}`)}async function Vt(n,r,e,t){let i;try{i=await N(r,e,t,"ctl status")}catch(c){return{execError:String(c),allAlive:!1}}if(i.stderr)return n.info(`supervisord ctl status: stderr occurred: ${i.stderr}, stdout: ${i.stdout}`),{rawResult:i,allAlive:!1};const o=T(i.stdout,"platforma"),s=T(i.stdout,"minio"),a={rawResult:i,platforma:o,minio:s,allAlive:o&&s};return a.allAlive||(a.minio||n.warn("Minio is not running on the server"),a.platforma||n.warn("Platforma is not running on the server")),a}function Wt(n,r,e,t,i,o,s){const a=Object.entries(r).map(([u,d])=>`${u}="${d}"`).join(","),c=D.randomBytes(16).toString("hex"),l=e;return`
9
9
  [supervisord]
10
10
  logfile=${t}/supervisord.log
11
11
  loglevel=info
@@ -31,10 +31,10 @@ autorestart=true
31
31
  [program:minio]
32
32
  autostart=true
33
33
  environment=${a}
34
- command=${n} server ${o}
34
+ command=${o} server ${n}
35
35
  directory=${t}
36
36
  autorestart=true
37
- `}async function N(o,r,e,t){const i=K(r,e),n=Y(r),s=`${i} --configuration ${n} ${t}`;return await o.exec(s)}function T(o,r){return(i=>i.replace(/\x1B\[[0-9;]*m/g,""))(o).split(`
38
- `).some(i=>{const[n,s]=i.trim().split(/\s{2,}/);return n===r&&s==="Running"})}const $=p.z.object({local:p.z.number(),remote:p.z.number()}),Z=p.z.object({grpc:$,monitoring:$,debug:$,minioPort:$,minioConsolePort:$}),Q=p.z.object({plUser:p.z.string(),plPassword:p.z.string(),ports:Z,useGlobalAccess:p.z.boolean().default(!1),plVersion:p.z.string().default("1.18.3")});function X(o,r,e,t,i){return{plUser:o,plPassword:r,ports:e,useGlobalAccess:t,plVersion:i}}function tt(o){return Q.parse(JSON.parse(o))}function et(o){return JSON.stringify(o,void 0,2)}const j=2.28;class B{constructor(r,e,t){f(this,"initState",{});this.logger=r,this.sshClient=e,this.username=t}info(){return{username:this.username,initState:this.initState}}static async init(r,e){try{const t=await b.init(r,e);return new B(r,t,w.notEmpty(e.username))}catch(t){throw r.error(`Connection error in SshClient.init: ${t}`),t}}cleanUp(){this.sshClient.close()}async isAlive(){const r=await this.getArch(),e=await this.getUserHomeDirectory();return await zt(this.logger,this.sshClient,e,r.arch)}async start(){const r=await this.getArch(),e=await this.getUserHomeDirectory();try{if(!(await this.isAlive()).allAlive)return await Gt(this.sshClient,e,r.arch),await this.checkIsAliveWithInterval()}catch(t){const i=`SshPl.start: ${t}`;throw this.logger.error(i),new Error(i)}}async stop(){const r=await this.getArch(),e=await this.getUserHomeDirectory();try{if((await this.isAlive()).allAlive)return await Mt(this.sshClient,e,r.arch),await this.checkIsAliveWithInterval(void 0,void 0,!1)}catch(t){const i=`PlSsh.stop: ${t}`;throw this.logger.error(i),new Error(i)}}async reset(){return await this.stopAndClean(),this.cleanUp(),!0}async stopAndClean(){const r=await this.getUserHomeDirectory();this.logger.info("pl.reset: Stop Platforma on the server"),await this.stop(),this.logger.info(`pl.reset: Deleting Platforma workDir ${g(r)} on the server`),await this.sshClient.deleteFolder(g(r))}async platformaInit(r){const e={localWorkdir:r.localWorkdir},{onProgress:t}=r;try{const i={...Lt,...r};if(e.plBinaryOps=i.plBinary,await(t==null?void 0:t("Detecting server architecture...")),e.arch=await this.getArch(),await(t==null?void 0:t("Server architecture detected.")),await(t==null?void 0:t("Fetching user home directory...")),e.remoteHome=await this.getUserHomeDirectory(),await(t==null?void 0:t("User home directory retrieved.")),await(t==null?void 0:t("Checking platform status...")),e.alive=await this.isAlive(),e.alive.allAlive&&await(t==null?void 0:t("All required services are running.")),e.alive.allAlive){if(e.userCredentials=await this.getUserCredentials(e.remoteHome),!e.userCredentials)throw new Error("SshPl.platformaInit: platforma is alive but userCredentials are not found");const d=e.userCredentials.useGlobalAccess==i.useGlobalAccess,u=e.userCredentials.plVersion==i.plBinary.version;if(e.needRestart=!(d&&u),this.logger.info(`SshPl.platformaInit: need restart? ${e.needRestart}`),!e.needRestart)return await(t==null?void 0:t("Server setup completed.")),e.userCredentials;await(t==null?void 0:t("Stopping services...")),await this.stop()}await(t==null?void 0:t("Downloading and uploading required binaries..."));const n=await Vt(this.logger,this.sshClient);if(n<j)throw new Error(`glibc version ${n} is too old. Version ${j} or higher is required for Platforma.`);const s=await this.downloadBinariesAndUploadToTheServer(i.localWorkdir,i.plBinary,e.remoteHome,e.arch);if(await(t==null?void 0:t("All required binaries have been downloaded and uploaded.")),e.binPaths={...s,history:void 0},e.downloadedBinaries=s.history,e.ports=await this.fetchPorts(e.remoteHome,e.arch),!e.ports.debug.remote||!e.ports.grpc.remote||!e.ports.minioPort.remote||!e.ports.minioConsolePort.remote||!e.ports.monitoring.remote)throw new Error("SshPl.platformaInit: remote ports are not defined");await(t==null?void 0:t("Generating server configuration..."));const a=await v.generateSshPlConfigs({logger:this.logger,workingDir:g(e.remoteHome),portsMode:{type:"customWithMinio",ports:{debug:e.ports.debug.remote,grpc:e.ports.grpc.remote,minio:e.ports.minioPort.remote,minioConsole:e.ports.minioConsolePort.remote,monitoring:e.ports.monitoring.remote,grpcLocal:e.ports.grpc.local,minioLocal:e.ports.minioPort.local}},licenseMode:i.license,useGlobalAccess:w.notEmpty(i.useGlobalAccess),plConfigPostprocessing:i.plConfigPostprocessing});e.generatedConfig={...a,filesToCreate:{skipped:"it is too wordy"}},await(t==null?void 0:t("Server configuration generated.")),await(t==null?void 0:t("Generating folder structure..."));for(const[d,u]of Object.entries(a.filesToCreate))await this.sshClient.writeFileOnTheServer(d,u),this.logger.info(`Created file ${d}`);for(const d of a.dirsToCreate)await this.sshClient.ensureRemoteDirCreated(d),this.logger.info(`Created directory ${d}`);await(t==null?void 0:t("Folder structure created.")),await(t==null?void 0:t("Writing supervisord configuration..."));const c=Jt(a.minioConfig.storageDir,a.minioConfig.envs,await this.getFreePortForPlatformaOnServer(e.remoteHome,e.arch),a.workingDir,a.plConfig.configPath,e.binPaths.minioRelPath,e.binPaths.downloadedPl);if(!await this.sshClient.writeFileOnTheServer(Y(e.remoteHome),c))throw new Error(`Can not write supervisord config on the server ${g(e.remoteHome)}`);return await(t==null?void 0:t("Supervisord configuration written.")),await(t==null?void 0:t("Saving connection information...")),e.connectionInfo=X(a.plUser,a.plPassword,e.ports,w.notEmpty(i.useGlobalAccess),i.plBinary.version),await this.sshClient.writeFileOnTheServer(I(e.remoteHome),et(e.connectionInfo)),await(t==null?void 0:t("Connection information saved.")),await(t==null?void 0:t("Starting Platforma on the server...")),await this.start(),e.started=!0,this.initState=e,await(t==null?void 0:t("Platforma has been started successfully.")),e.connectionInfo}catch(i){const n=`SshPl.platformaInit: ${i}, state: ${JSON.stringify(e)}`;throw this.logger.error(n),new Error(n)}}async downloadBinariesAndUploadToTheServer(r,e,t,i){const n=[];try{const s=await this.downloadAndUntar(r,t,i,"pl",`pl-${e.version}`);n.push(s);const a=await this.downloadAndUntar(r,t,i,"supervisord",Ut);n.push(a);const c=qt(t,i.arch),l=await this.downloadAndUntar(r,t,i,"minio",_t);return n.push(l),await this.sshClient.chmod(c,488),{history:n,minioRelPath:c,downloadedPl:U(t,i.arch)}}catch(s){const a=`SshPl.downloadBinariesAndUploadToServer: ${s}, state: ${JSON.stringify(n)}`;throw this.logger.error(a),s}}async downloadAndUntar(r,e,t,i,n){const s={};s.binBasePath=A(e),await this.sshClient.ensureRemoteDirCreated(s.binBasePath),s.binBasePathCreated=!0;let a=null;const c=5;for(let d=1;d<=c;d++)try{a=await yt(this.logger,r,i,n,t.arch,t.platform);break}catch(u){if(await w.sleep(300),d==c)throw new Error(`downloadAndUntar: ${c} attempts, last error: ${u}`)}s.downloadResult=w.notEmpty(a),s.localArchivePath=h.resolve(s.downloadResult.archivePath),s.remoteDir=h.join(s.binBasePath,s.downloadResult.baseName),s.remoteArchivePath=s.remoteDir+".tgz",await this.sshClient.ensureRemoteDirCreated(s.remoteDir),await this.sshClient.uploadFile(s.localArchivePath,s.remoteArchivePath),s.uploadDone=!0;try{await this.sshClient.exec("hash tar")}catch{throw new Error("tar is not installed on the server. Please install it before running Platforma.")}const l=await this.sshClient.exec(`tar --warning=no-all -xvf ${s.remoteArchivePath} --directory=${s.remoteDir}`);if(l.stderr)throw new Error(`downloadAndUntar: untar: stderr occurred: ${l.stderr}, stdout: ${l.stdout}`);return s.untarDone=!0,s}async needDownload(r,e){const t=K(r,e.arch),i=W(r,e.arch),n=U(r,e.arch);return!await this.sshClient.checkFileExists(n)||!await this.sshClient.checkFileExists(i)||!await this.sshClient.checkFileExists(t)}async checkIsAliveWithInterval(r=1e3,e=15,t=!0){const i=e*r;let n=0,s=await this.isAlive();for(;t?!s.allAlive:s.allAlive;){if(await w.sleep(r),n+=r,n>i)throw new Error(`isAliveWithInterval: The process did not ${t?"started":"stopped"} after ${i} ms. Live status: ${JSON.stringify(s)}`);s=await this.isAlive()}}async getUserCredentials(r){const e=await this.sshClient.readFile(I(r));return tt(e)}async fetchPorts(r,e){return{grpc:{local:await v.getFreePort(),remote:await this.getFreePortForPlatformaOnServer(r,e)},monitoring:{local:await v.getFreePort(),remote:await this.getFreePortForPlatformaOnServer(r,e)},debug:{local:await v.getFreePort(),remote:await this.getFreePortForPlatformaOnServer(r,e)},minioPort:{local:await v.getFreePort(),remote:await this.getFreePortForPlatformaOnServer(r,e)},minioConsolePort:{local:await v.getFreePort(),remote:await this.getFreePortForPlatformaOnServer(r,e)}}}async getLocalFreePort(){return new Promise(r=>{const e=q.createServer();e.listen(0,()=>{const t=e.address().port;e.close(i=>r(t))})})}async getFreePortForPlatformaOnServer(r,e){const t=jt(r,e.arch),{stdout:i,stderr:n}=await this.sshClient.exec(`${t}`);if(n)throw new Error(`getFreePortForPlatformaOnServer: stderr is not empty: ${n}, stdout: ${i}`);return+i}async getArch(){const{stdout:r,stderr:e}=await this.sshClient.exec("uname -s && uname -m");if(e)throw new Error(`getArch: stderr is not empty: ${e}, stdout: ${r}`);const t=r.split(`
39
- `);return{platform:t[0],arch:t[1]}}async getUserHomeDirectory(){const{stdout:r,stderr:e}=await this.sshClient.exec("echo $HOME");if(e){const t=`/home/${this.username}`;return console.warn(`getUserHomeDirectory: stderr is not empty: ${e}, stdout: ${r}, will get a default home: ${t}`),t}return r.trim()}}const Lt={useGlobalAccess:!1,plBinary:{type:"Download",version:E()}};async function Vt(o,r){try{const{stdout:e,stderr:t}=await r.exec("ldd --version | head -n 1");if(t)throw new Error(`Failed to check glibc version: ${t}`);return rt(e)}catch(e){throw o.error(`glibc version check failed: ${e}`),e}}function rt(o){const r=o.match(/\d+\.\d+/);if(!r)throw new Error(`Could not parse glibc version from: ${o}`);return parseFloat(r[0])}exports.ConnectionInfo=Q;exports.LocalConfigYaml=J;exports.LocalPl=L;exports.PortPair=$;exports.SshClient=b;exports.SshPl=B;exports.SshPlPorts=Z;exports.getDefaultPlVersion=E;exports.localPlatformaInit=Ot;exports.newConnectionInfo=X;exports.parseConnectionInfo=tt;exports.parseGlibcVersion=rt;exports.stringifyConnectionInfo=et;
37
+ `}async function N(n,r,e,t){const i=X(r,e),o=Q(r),s=`${i} --configuration ${o} ${t}`;return await n.exec(s)}function T(n,r){return(i=>i.replace(/\x1B\[[0-9;]*m/g,""))(n).split(`
38
+ `).some(i=>{const[o,s]=i.trim().split(/\s{2,}/);return o===r&&s==="Running"})}const $=m.z.object({local:m.z.number(),remote:m.z.number()}),tt=m.z.object({grpc:$,monitoring:$,debug:$,minioPort:$,minioConsolePort:$}),et=m.z.object({plUser:m.z.string(),plPassword:m.z.string(),ports:tt,useGlobalAccess:m.z.boolean().default(!1),plVersion:m.z.string().default("1.18.3")});function rt(n,r,e,t,i){return{plUser:n,plPassword:r,ports:e,useGlobalAccess:t,plVersion:i}}function it(n){return et.parse(JSON.parse(n))}function nt(n){return JSON.stringify(n,void 0,2)}const j=2.28;class _{constructor(r,e,t){f(this,"initState",{});this.logger=r,this.sshClient=e,this.username=t}info(){return{username:this.username,initState:this.initState}}static async init(r,e){try{const t=await A.init(r,e);return new _(r,t,w.notEmpty(e.username))}catch(t){throw r.error(`Connection error in SshClient.init: ${t}`),t}}cleanUp(){this.sshClient.close()}async isAlive(){const r=await this.getArch(),e=await this.getUserHomeDirectory();return await Vt(this.logger,this.sshClient,e,r.arch)}async start(){const r=await this.getArch(),e=await this.getUserHomeDirectory();try{if(!(await this.isAlive()).allAlive)return await Jt(this.sshClient,e,r.arch),await this.checkIsAliveWithInterval()}catch(t){const i=`SshPl.start: ${t}`;throw this.logger.error(i),new Error(i)}}async stop(){const r=await this.getArch(),e=await this.getUserHomeDirectory();try{if((await this.isAlive()).allAlive)return await Lt(this.sshClient,e,r.arch),await this.checkIsAliveWithInterval(void 0,void 0,!1)}catch(t){const i=`PlSsh.stop: ${t}`;throw this.logger.error(i),new Error(i)}}async reset(){return await this.stopAndClean(),this.cleanUp(),!0}async stopAndClean(){const r=await this.getUserHomeDirectory();this.logger.info("pl.reset: Stop Platforma on the server"),await this.stop(),this.logger.info(`pl.reset: Deleting Platforma workDir ${y(r)} on the server`),await this.sshClient.deleteFolder(y(r))}async platformaInit(r){const e={localWorkdir:r.localWorkdir},{onProgress:t}=r;try{const i={...Kt,...r};if(e.plBinaryOps=i.plBinary,await(t==null?void 0:t("Detecting server architecture...")),e.arch=await this.getArch(),await(t==null?void 0:t("Server architecture detected.")),await(t==null?void 0:t("Fetching user home directory...")),e.remoteHome=await this.getUserHomeDirectory(),await(t==null?void 0:t("User home directory retrieved.")),await(t==null?void 0:t("Checking platform status...")),e.alive=await this.isAlive(),e.alive.allAlive&&await(t==null?void 0:t("All required services are running.")),e.alive.allAlive){if(e.userCredentials=await this.getUserCredentials(e.remoteHome),!e.userCredentials)throw new Error("SshPl.platformaInit: platforma is alive but userCredentials are not found");const u=e.userCredentials.useGlobalAccess==i.useGlobalAccess,d=e.userCredentials.plVersion==i.plBinary.version;if(e.needRestart=!(u&&d),this.logger.info(`SshPl.platformaInit: need restart? ${e.needRestart}`),!e.needRestart)return await(t==null?void 0:t("Server setup completed.")),e.userCredentials;await(t==null?void 0:t("Stopping services...")),await this.stop()}await(t==null?void 0:t("Downloading and uploading required binaries..."));const o=await Yt(this.logger,this.sshClient);if(o<j)throw new Error(`glibc version ${o} is too old. Version ${j} or higher is required for Platforma.`);const s=await this.downloadBinariesAndUploadToTheServer(i.localWorkdir,i.plBinary,e.remoteHome,e.arch);if(await(t==null?void 0:t("All required binaries have been downloaded and uploaded.")),e.binPaths={...s,history:void 0},e.downloadedBinaries=s.history,e.ports=await this.fetchPorts(e.remoteHome,e.arch),!e.ports.debug.remote||!e.ports.grpc.remote||!e.ports.minioPort.remote||!e.ports.minioConsolePort.remote||!e.ports.monitoring.remote)throw new Error("SshPl.platformaInit: remote ports are not defined");await(t==null?void 0:t("Generating server configuration..."));const a=await v.generateSshPlConfigs({logger:this.logger,workingDir:y(e.remoteHome),portsMode:{type:"customWithMinio",ports:{debug:e.ports.debug.remote,grpc:e.ports.grpc.remote,minio:e.ports.minioPort.remote,minioConsole:e.ports.minioConsolePort.remote,monitoring:e.ports.monitoring.remote,grpcLocal:e.ports.grpc.local,minioLocal:e.ports.minioPort.local}},licenseMode:i.license,useGlobalAccess:w.notEmpty(i.useGlobalAccess),plConfigPostprocessing:i.plConfigPostprocessing});e.generatedConfig={...a,filesToCreate:{skipped:"it is too wordy"}},await(t==null?void 0:t("Server configuration generated.")),await(t==null?void 0:t("Generating folder structure..."));for(const[u,d]of Object.entries(a.filesToCreate))await this.sshClient.writeFileOnTheServer(u,d),this.logger.info(`Created file ${u}`);for(const u of a.dirsToCreate)await this.sshClient.ensureRemoteDirCreated(u),this.logger.info(`Created directory ${u}`);await(t==null?void 0:t("Folder structure created.")),await(t==null?void 0:t("Writing supervisord configuration..."));const c=Wt(a.minioConfig.storageDir,a.minioConfig.envs,await this.getFreePortForPlatformaOnServer(e.remoteHome,e.arch),a.workingDir,a.plConfig.configPath,e.binPaths.minioRelPath,e.binPaths.downloadedPl);if(!await this.sshClient.writeFileOnTheServer(Q(e.remoteHome),c))throw new Error(`Can not write supervisord config on the server ${y(e.remoteHome)}`);return await(t==null?void 0:t("Supervisord configuration written.")),await(t==null?void 0:t("Saving connection information...")),e.connectionInfo=rt(a.plUser,a.plPassword,e.ports,w.notEmpty(i.useGlobalAccess),i.plBinary.version),await this.sshClient.writeFileOnTheServer(I(e.remoteHome),nt(e.connectionInfo)),await(t==null?void 0:t("Connection information saved.")),await(t==null?void 0:t("Starting Platforma on the server...")),await this.start(),e.started=!0,this.initState=e,await(t==null?void 0:t("Platforma has been started successfully.")),e.connectionInfo}catch(i){const o=`SshPl.platformaInit: ${i}, state: ${JSON.stringify(e)}`;throw this.logger.error(o),new Error(o)}}async downloadBinariesAndUploadToTheServer(r,e,t,i){const o=[];try{const s=await this.downloadAndUntar(r,t,i,"pl",`pl-${e.version}`);o.push(s);const a=await this.downloadAndUntar(r,t,i,"supervisord",jt);o.push(a);const c=Gt(t,i.arch),l=await this.downloadAndUntar(r,t,i,"minio",Tt);return o.push(l),await this.sshClient.chmod(c,488),{history:o,minioRelPath:c,downloadedPl:U(t,i.arch)}}catch(s){const a=`SshPl.downloadBinariesAndUploadToServer: ${s}, state: ${JSON.stringify(o)}`;throw this.logger.error(a),s}}async downloadAndUntar(r,e,t,i,o){const s={};s.binBasePath=O(e),await this.sshClient.ensureRemoteDirCreated(s.binBasePath),s.binBasePathCreated=!0;let a=null;const c=5;for(let u=1;u<=c;u++)try{a=await Pt(this.logger,r,i,o,t.arch,t.platform);break}catch(d){if(await w.sleep(300),u==c)throw new Error(`downloadAndUntar: ${c} attempts, last error: ${d}`)}s.downloadResult=w.notEmpty(a),s.localArchivePath=h.resolve(s.downloadResult.archivePath),s.remoteDir=h.join(s.binBasePath,s.downloadResult.baseName),s.remoteArchivePath=s.remoteDir+".tgz",await this.sshClient.ensureRemoteDirCreated(s.remoteDir),await this.sshClient.uploadFile(s.localArchivePath,s.remoteArchivePath),s.uploadDone=!0;try{await this.sshClient.exec("hash tar")}catch{throw new Error("tar is not installed on the server. Please install it before running Platforma.")}const l=await this.sshClient.exec(`tar --warning=no-all -xvf ${s.remoteArchivePath} --directory=${s.remoteDir}`);if(l.stderr)throw new Error(`downloadAndUntar: untar: stderr occurred: ${l.stderr}, stdout: ${l.stdout}`);return s.untarDone=!0,s}async needDownload(r,e){const t=X(r,e.arch),i=Z(r,e.arch),o=U(r,e.arch);return!await this.sshClient.checkFileExists(o)||!await this.sshClient.checkFileExists(i)||!await this.sshClient.checkFileExists(t)}async checkIsAliveWithInterval(r=1e3,e=15,t=!0){const i=e*r;let o=0,s=await this.isAlive();for(;t?!s.allAlive:s.allAlive;){if(await w.sleep(r),o+=r,o>i)throw new Error(`isAliveWithInterval: The process did not ${t?"started":"stopped"} after ${i} ms. Live status: ${JSON.stringify(s)}`);s=await this.isAlive()}}async getUserCredentials(r){const e=await this.sshClient.readFile(I(r));return it(e)}async fetchPorts(r,e){return{grpc:{local:await v.getFreePort(),remote:await this.getFreePortForPlatformaOnServer(r,e)},monitoring:{local:await v.getFreePort(),remote:await this.getFreePortForPlatformaOnServer(r,e)},debug:{local:await v.getFreePort(),remote:await this.getFreePortForPlatformaOnServer(r,e)},minioPort:{local:await v.getFreePort(),remote:await this.getFreePortForPlatformaOnServer(r,e)},minioConsolePort:{local:await v.getFreePort(),remote:await this.getFreePortForPlatformaOnServer(r,e)}}}async getLocalFreePort(){return new Promise(r=>{const e=q.createServer();e.listen(0,()=>{const t=e.address().port;e.close(i=>r(t))})})}async getFreePortForPlatformaOnServer(r,e){const t=Mt(r,e.arch),{stdout:i,stderr:o}=await this.sshClient.exec(`${t}`);if(o)throw new Error(`getFreePortForPlatformaOnServer: stderr is not empty: ${o}, stdout: ${i}`);return+i}async getArch(){const{stdout:r,stderr:e}=await this.sshClient.exec("uname -s && uname -m");if(e)throw new Error(`getArch: stderr is not empty: ${e}, stdout: ${r}`);const t=r.split(`
39
+ `);return{platform:t[0],arch:t[1]}}async getUserHomeDirectory(){const{stdout:r,stderr:e}=await this.sshClient.exec("echo $HOME");if(e){const t=`/home/${this.username}`;return console.warn(`getUserHomeDirectory: stderr is not empty: ${e}, stdout: ${r}, will get a default home: ${t}`),t}return r.trim()}}const Kt={useGlobalAccess:!1,plBinary:{type:"Download",version:b()}};async function Yt(n,r){try{const{stdout:e,stderr:t}=await r.exec("ldd --version | head -n 1");if(t)throw new Error(`Failed to check glibc version: ${t}`);return ot(e)}catch(e){throw n.error(`glibc version check failed: ${e}`),e}}function ot(n){const r=n.match(/\d+\.\d+/);if(!r)throw new Error(`Could not parse glibc version from: ${n}`);return parseFloat(r[0])}exports.ConnectionInfo=et;exports.LocalConfigYaml=L;exports.LocalPl=V;exports.PortPair=$;exports.SshClient=A;exports.SshPl=_;exports.SshPlPorts=tt;exports.getDefaultPlVersion=b;exports.localPlatformaInit=Nt;exports.mergeDefaultOps=W;exports.newConnectionInfo=rt;exports.parseConnectionInfo=it;exports.parseGlibcVersion=ot;exports.plProcessOps=K;exports.stringifyConnectionInfo=nt;
40
40
  //# sourceMappingURL=index.js.map