@milaboratories/pl-deployments 2.4.7 → 2.4.8

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.
Files changed (96) hide show
  1. package/dist/common/os_and_arch.cjs +39 -0
  2. package/dist/common/os_and_arch.cjs.map +1 -0
  3. package/dist/common/os_and_arch.d.ts +0 -1
  4. package/dist/common/os_and_arch.js +34 -0
  5. package/dist/common/os_and_arch.js.map +1 -0
  6. package/dist/common/pl_binary.cjs +34 -0
  7. package/dist/common/pl_binary.cjs.map +1 -0
  8. package/dist/common/pl_binary.d.ts +0 -1
  9. package/dist/common/pl_binary.js +30 -0
  10. package/dist/common/pl_binary.js.map +1 -0
  11. package/dist/common/pl_binary_download.cjs +172 -0
  12. package/dist/common/pl_binary_download.cjs.map +1 -0
  13. package/dist/common/pl_binary_download.d.ts +0 -1
  14. package/dist/common/pl_binary_download.js +148 -0
  15. package/dist/common/pl_binary_download.js.map +1 -0
  16. package/dist/common/pl_version.cjs +11 -0
  17. package/dist/common/pl_version.cjs.map +1 -0
  18. package/dist/common/pl_version.d.ts +0 -1
  19. package/dist/common/pl_version.js +9 -0
  20. package/dist/common/pl_version.js.map +1 -0
  21. package/dist/index.cjs +26 -0
  22. package/dist/index.cjs.map +1 -0
  23. package/dist/index.d.ts +0 -1
  24. package/dist/index.js +5 -64
  25. package/dist/index.js.map +1 -1
  26. package/dist/local/options.d.ts +0 -1
  27. package/dist/local/pid.cjs +24 -0
  28. package/dist/local/pid.cjs.map +1 -0
  29. package/dist/local/pid.d.ts +0 -1
  30. package/dist/local/pid.js +20 -0
  31. package/dist/local/pid.js.map +1 -0
  32. package/dist/local/pl.cjs +217 -0
  33. package/dist/local/pl.cjs.map +1 -0
  34. package/dist/local/pl.d.ts +0 -1
  35. package/dist/local/pl.js +192 -0
  36. package/dist/local/pl.js.map +1 -0
  37. package/dist/local/process.cjs +42 -0
  38. package/dist/local/process.cjs.map +1 -0
  39. package/dist/local/process.d.ts +0 -1
  40. package/dist/local/process.js +37 -0
  41. package/dist/local/process.js.map +1 -0
  42. package/dist/local/trace.cjs +27 -0
  43. package/dist/local/trace.cjs.map +1 -0
  44. package/dist/local/trace.d.ts +0 -1
  45. package/dist/local/trace.js +23 -0
  46. package/dist/local/trace.js.map +1 -0
  47. package/dist/package.json.cjs +7 -0
  48. package/dist/package.json.cjs.map +1 -0
  49. package/dist/package.json.js +5 -0
  50. package/dist/package.json.js.map +1 -0
  51. package/dist/ssh/__tests__/common-utils.d.ts +0 -1
  52. package/dist/ssh/connection_info.cjs +62 -0
  53. package/dist/ssh/connection_info.cjs.map +1 -0
  54. package/dist/ssh/connection_info.d.ts +0 -1
  55. package/dist/ssh/connection_info.js +55 -0
  56. package/dist/ssh/connection_info.js.map +1 -0
  57. package/dist/ssh/pl.cjs +500 -0
  58. package/dist/ssh/pl.cjs.map +1 -0
  59. package/dist/ssh/pl.d.ts +0 -1
  60. package/dist/ssh/pl.js +497 -0
  61. package/dist/ssh/pl.js.map +1 -0
  62. package/dist/ssh/pl_paths.cjs +67 -0
  63. package/dist/ssh/pl_paths.cjs.map +1 -0
  64. package/dist/ssh/pl_paths.d.ts +0 -1
  65. package/dist/ssh/pl_paths.js +50 -0
  66. package/dist/ssh/pl_paths.js.map +1 -0
  67. package/dist/ssh/ssh.cjs +621 -0
  68. package/dist/ssh/ssh.cjs.map +1 -0
  69. package/dist/ssh/ssh.d.ts +0 -1
  70. package/dist/ssh/ssh.js +619 -0
  71. package/dist/ssh/ssh.js.map +1 -0
  72. package/dist/ssh/supervisord.cjs +149 -0
  73. package/dist/ssh/supervisord.cjs.map +1 -0
  74. package/dist/ssh/supervisord.d.ts +0 -1
  75. package/dist/ssh/supervisord.js +140 -0
  76. package/dist/ssh/supervisord.js.map +1 -0
  77. package/package.json +16 -14
  78. package/src/common/pl_version.ts +3 -2
  79. package/dist/common/os_and_arch.d.ts.map +0 -1
  80. package/dist/common/pl_binary.d.ts.map +0 -1
  81. package/dist/common/pl_binary_download.d.ts.map +0 -1
  82. package/dist/common/pl_version.d.ts.map +0 -1
  83. package/dist/index.d.ts.map +0 -1
  84. package/dist/index.mjs +0 -1366
  85. package/dist/index.mjs.map +0 -1
  86. package/dist/local/options.d.ts.map +0 -1
  87. package/dist/local/pid.d.ts.map +0 -1
  88. package/dist/local/pl.d.ts.map +0 -1
  89. package/dist/local/process.d.ts.map +0 -1
  90. package/dist/local/trace.d.ts.map +0 -1
  91. package/dist/ssh/__tests__/common-utils.d.ts.map +0 -1
  92. package/dist/ssh/connection_info.d.ts.map +0 -1
  93. package/dist/ssh/pl.d.ts.map +0 -1
  94. package/dist/ssh/pl_paths.d.ts.map +0 -1
  95. package/dist/ssh/ssh.d.ts.map +0 -1
  96. package/dist/ssh/supervisord.d.ts.map +0 -1
package/dist/index.js CHANGED
@@ -1,65 +1,6 @@
1
- "use strict";var at=Object.defineProperty;var ct=(n,t,e)=>t in n?at(n,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):n[t]=e;var f=(n,t,e)=>ct(n,typeof t!="symbol"?t+"":t,e);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const lt=require("node:child_process"),p=require("@milaboratories/ts-helpers"),D=require("node:fs"),y=require("node:fs/promises"),u=require("upath"),dt=require("undici"),I=require("node:stream"),ut=require("node:stream/consumers"),ht=require("tar"),pt=require("decompress"),P=require("node:os"),C=require("ssh2"),H=require("node:net"),ft=require("node:dns"),b=require("node:crypto"),v=require("@milaboratories/pl-config"),w=require("zod");function q(n){const t=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(n){for(const e in n)if(e!=="default"){const r=Object.getOwnPropertyDescriptor(n,e);Object.defineProperty(t,e,r.get?r:{enumerable:!0,get:()=>n[e]})}}return t.default=n,Object.freeze(t)}const wt=q(ht),mt=q(P);function yt(n,t){return n.info(`Running:
2
- cmd: ${JSON.stringify([t.cmd,...t.args])}
3
- wd: ${t.opts.cwd}`),n.info(" spawning child process"),lt.spawn(t.cmd,t.args,t.opts)}async function k(n){try{return process.kill(n,0),!0}catch{return!1}}function G(n){return process.kill(n,"SIGINT")}async function z(n,t){let r=0;for(;await k(n);)if(await p.sleep(100),r+=100,r>t)throw new Error(`The process did not stopped after ${t} ms.`)}const gt=["linux","macos","windows"];function _(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 vt=["amd64","arm64"];function S(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(vt))}}const $t="https://cdn.platforma.bio/software",St="https://cdn-ga.pl-open.science/software";async function Ct(n,t,e,r,i,o){const s=L(e,r,t,S(i),_(o)),{archiveUrl:a,alternativeArchiveGAUrl:c,archivePath:l}=s;try{await F(n,a,l),s.wasDownloadedFrom=a}catch{await F(n,c,l),s.wasDownloadedFrom=c}return s}async function Et(n,t,e,r,i,o){const s=L(e,r,t,S(i),_(o)),{archiveUrl:a,alternativeArchiveGAUrl:c,archivePath:l,archiveType:d,targetFolder:h}=s;try{await F(n,a,l),s.wasDownloadedFrom=a}catch{await F(n,c,l),s.wasDownloadedFrom=c}return await bt(n,l,d,h),s}function L(n,t,e,r,i){const o=`${t}-${r}`,s=Ft[i],a=`${o}.${s}`,c=`${$t}/${n}/${i}/${a}`,l=`${St}/${n}/${i}/${a}`,d=u.join(e,a),h=u.join(e,o);return{archiveUrl:c,alternativeArchiveGAUrl:l,archivePath:d,archiveType:s,targetFolder:h,baseName:o}}async function F(n,t,e){const r={};r.dstArchive=e;try{if(r.fileExisted=await p.fileExists(e),r.fileExisted)return n.info(`Platforma Backend archive download skipped: '${e}' already exists`),r;await y.mkdir(u.dirname(e),{recursive:!0}),r.dirnameCreated=!0,n.info(`Downloading archive:
4
- URL: ${t}
5
- Save to: ${e}`);const{body:i,statusCode:o}=await dt.request(t);if(r.statusCode=o,o!=200){const s=await ut.text(i);throw r.errorMsg=`failed to download archive: ${o}, response: ${s.slice(0,1e3)}`,n.error(r.errorMsg),new Error(r.errorMsg)}return r.tmpPath=e+".tmp",await I.Readable.toWeb(i).pipeTo(I.Writable.toWeb(D.createWriteStream(r.tmpPath))),r.wroteTmp=!0,r.tmpExisted=await p.fileExists(r.tmpPath),await y.rename(r.tmpPath,e),r.renamed=!0,r.newExisted=await p.fileExists(e),r}catch(i){const o=`downloadArchive: ${JSON.stringify(i)}, state: ${JSON.stringify(r)}`;throw n.error(o),new Error(o)}}const Pt=".ok";async function bt(n,t,e,r){if(n.info("extracting archive..."),n.info(` archive path: '${t}'`),n.info(` target dir: '${r}'`),!await p.fileExists(t)){const o=`Platforma Backend binary archive not found at '${t}'`;throw n.error(o),new Error(o)}const i=u.join(r,Pt);if(await p.fileExists(i)){n.info(`Platforma Backend binaries unpack skipped: '${r}' exists`);return}switch(await p.fileExists(r)&&(n.info(`Removing previous incompletely unpacked folder: '${r}'`),await y.rm(r,{recursive:!0})),n.info(` creating target dir '${r}'`),await y.mkdir(r,{recursive:!0}),n.info(`Unpacking Platforma Backend archive:
6
- Archive: ${t}
7
- Target dir: ${r}`),e){case"tgz":await wt.x({file:t,cwd:r,gzip:!0});break;case"zip":await pt(t,r);break;default:p.assertNever(e)}await y.writeFile(i,"ok"),n.info(" ... unpack done.")}const Ft={linux:"tgz",macos:"tgz",windows:"zip"};function A(){return"1.35.2"}function At(){return{type:"Download",version:A()}}async function Ot(n,t,e){switch(e.type){case"Download":const r=await Et(n,t,"pl",`pl-${e.version}`,P.arch(),P.platform());return u.join(r.baseName,"binaries",xt[_(P.platform())]);case"Local":return e.path;default:p.assertNever(e)}}const xt={linux:"platforma",macos:"platforma",windows:"platforma.exe"};function J(n){return u.join(n,"pl_pid")}async function Dt(n){if(!await p.fileExists(n))return;const t=await y.readFile(n);return Number(t.toString())}async function kt(n,t){await y.writeFile(n,JSON.stringify(t))}function _t(){return{}}function Rt(n,t,e){return n[t]=e,e}async function R(n,t){const e=_t();try{return await t((i,o)=>Rt(e,i,o),e)}catch(r){throw n.error(`error ${r} while doing traced operation, state: ${JSON.stringify(e)}`),r}}const V="config-local.yaml";class W{constructor(t,e,r,i,o,s,a,c){f(this,"instance");f(this,"pid");f(this,"nRuns",0);f(this,"lastRunHistory",{});f(this,"wasStopped",!1);this.logger=t,this.workingDir=e,this.startOptions=r,this.initialStartHistory=i,this.onClose=o,this.onError=s,this.onCloseAndError=a,this.onCloseAndErrorNoStop=c}async start(){await R(this.logger,async(t,e)=>{this.wasStopped=!1;const r=yt(this.logger,this.startOptions);r.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)}),r.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)}),t("started",!0);const i=t("pidFile",J(this.workingDir));t("pid",p.notEmpty(r.pid)),t("pidWritten",await kt(i,p.notEmpty(r.pid))),this.nRuns++,this.instance=r,this.pid=r.pid,this.lastRunHistory=e})}stop(){this.wasStopped=!0,G(p.notEmpty(this.pid))}async waitStopped(){await z(p.notEmpty(this.pid),15e3)}stopped(){return this.wasStopped}async isAlive(){return await k(p.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,t){const e=Math.max(mt.cpus().length-2,1),r=K(t,e);return await R(n,async(i,o)=>{i("startOptions",{...r,config:"too wordy"});const s=u.resolve(r.workingDir);r.closeOld&&i("closeOld",await Bt(n,s));const a=u.join(s,V);n.info(`writing configuration '${a}'...`),await y.writeFile(a,r.config);const c=u.join(s,"binaries"),l=await Ot(n,c,r.plBinary),d=i("binaryPath",u.join("binaries",l)),h=Y(d,a,r,s,process.env);i("processOpts",{cmd:h.cmd,args:h.args,cwd:h.opts.cwd});const m=new W(n,r.workingDir,h,o,r.onClose,r.onError,r.onCloseAndError,r.onCloseAndErrorNoStop);return await m.start(),m})}async function Bt(n,t){return await R(n,async(e,r)=>{const i=e("pidFilePath",J(t)),o=e("pid",await Dt(i)),s=e("wasAlive",await k(o));return o!==void 0&&s&&(e("stopped",G(o)),e("waitStopped",await z(o,1e4))),r})}function K(n,t){var i;const e={plBinary:At(),spawnOptions:{env:{GOMAXPROCS:String(t)}},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 r={...n};return delete r.spawnOptions,{...e,...r}}function Y(n,t,e,r,i){var a;const o={cmd:n,args:["--config",t],opts:{env:{...i},cwd:r,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 It={keepaliveInterval:6e4,keepaliveCountMax:10};class O{constructor(t,e){f(this,"config");f(this,"homeDir");f(this,"forwardedServers",[]);this.logger=t,this.client=e}static async init(t,e){const r={...It,...e},i=new O(t,new C.Client);return await i.connect(r),i}getForwardedServers(){return this.forwardedServers}getFullHostName(){var t,e;return`${(t=this.config)==null?void 0:t.host}:${(e=this.config)==null?void 0:e.port}`}getUserName(){var t;return(t=this.config)==null?void 0:t.username}async connect(t){return this.config=t,await Ut(this.client,t)}async exec(t){return new Promise((e,r)=>{this.client.exec(t,(i,o)=>{if(i)return r(new Error(`ssh.exec: ${t}: ${i}`));let s="",a="";o.on("close",c=>{c===0?e({stdout:s,stderr:a}):r(new Error(`Command ${t} exited with code ${c}, stdout: ${s}, stderr: ${a}`))}).on("data",c=>{s+=c.toString()}).stderr.on("data",c=>{a+=c.toString()})})})}static async getAuthTypes(t,e){return new Promise(r=>{let i="";const o=new C.Client;o.on("ready",()=>{o.end();const s=this.extractAuthMethods(i);r(s.length===0?["publickey","password"]:s)}),o.on("error",()=>{o.end(),r(["publickey","password"])}),o.connect({host:t,port:e,username:new Date().getTime().toString(),debug:s=>{i+=`${s}
8
- `}})})}static extractAuthMethods(t){const e=t.match(/Inbound: Received USERAUTH_FAILURE \((.+)\)/);return e&&e[1]?e[1].split(",").map(r=>r.trim()):[]}async forwardPort(t,e){const r=`ssh.forward:${t.localPort}:${t.remotePort}.id_${b.randomBytes(1).toString("hex")}`;e=e??this.config;const i=new p.RetryablePromise(o=>new Promise((s,a)=>{const c=new C.Client;c.on("ready",()=>{this.logger.info(`${r}.client.ready`),s(c)}),c.on("error",l=>{this.logger.info(`${r}.client.error: ${l}`),o.reset(),a(l)}),c.on("close",()=>{this.logger.info(`${r}.client.closed`),o.reset()}),c.connect(e)}));return await i.ensure(),new Promise((o,s)=>{const a=H.createServer({pauseOnConnect:!0},async c=>{const l=`${r}.sock_${b.randomBytes(1).toString("hex")}`;let d;try{d=await i.ensure()}catch(m){this.logger.info(`${l}.persistentClient.catch: ${m}`),c.end();return}d.setNoDelay(!0),c.setNoDelay(!0);let h;try{h=await Tt(this.logger,d,"127.0.0.1",0,"127.0.0.1",t.remotePort)}catch(m){this.logger.error(`${l}.forwardOut.err: ${m}`),c.end();return}c.pipe(h),h.pipe(c),c.resume(),h.on("error",m=>{this.logger.error(`${l}.stream.error: ${m}`),c.end(),h.end()}),h.on("close",()=>{c.end(),h.end()}),c.on("close",()=>{this.logger.info(`${l}.localSocket: closed`),c.end(),h.end()})});a.listen(t.localPort,"127.0.0.1",()=>{this.logger.info(`${r}.server: started listening`),this.forwardedServers.push(a),o({server:a})}),a.on("error",c=>{a.close(),s(new Error(`${r}.server: error: ${JSON.stringify(c)}`))}),a.on("close",()=>{this.logger.info(`${r}.server: closed ${JSON.stringify(t)}`),this.forwardedServers=this.forwardedServers.filter(c=>c!==a)})})}closeForwardedPorts(){this.logger.info("[SSH] Closing all forwarded ports..."),this.forwardedServers.forEach(t=>{const e=t.address();if(e&&typeof e!="string"){const r=e;this.logger.info(`[SSH] Closing port forward for server ${r.address}:${r.port}`)}t.close()}),this.forwardedServers=[]}static async checkHostAvailability(t){return new Promise(e=>{ft.lookup(t,r=>{e(!r)})})}static async isPassphraseRequiredForKey(t){return new Promise((e,r)=>{try{return C.utils.parseKey(t)instanceof Error&&e(!0),e(!1)}catch(i){console.log("Error parsing privateKey"),r(new Error(`ssh.isPassphraseRequiredForKey: err ${i}`))}})}async uploadFile(t,e){return await this.withSftp(async r=>new Promise((i,o)=>{r.fastPut(t,e,s=>{if(s){const a=new Error(`ssh.uploadFile: err: ${s}, localPath: ${t}, remotePath: ${e}`);return o(a)}i(!0)})}))}async withSftp(t){return new Promise((e,r)=>{this.client.sftp((i,o)=>{if(i)return r(new Error(`ssh.withSftp: sftp err: ${i}`));t(o).then(e).catch(s=>{r(new Error(`ssh.withSftp.callback: err ${s}`))}).finally(()=>{o==null||o.end()})})})}async writeFileOnTheServer(t,e,r=432){return this.withSftp(async i=>this.writeFile(i,t,e,r))}async getForderStructure(t,e,r={files:[],directories:[]}){return new Promise((i,o)=>{t.readdir(e,async(s,a)=>{if(s)return o(s);for(const c of a){const l=`${e}/${c.filename}`;if(c.attrs.isDirectory()){r.directories.push(l);try{await this.getForderStructure(t,l,r)}catch(d){return o(d instanceof Error?d:new Error(String(d)))}}else r.files.push(l)}i(r)})})}rmdir(t,e){return new Promise((r,i)=>{t.rmdir(e,o=>o?i(o):r(!0))})}unlink(t,e){return new Promise((r,i)=>{t.unlink(e,o=>o?i(o):r(!0))})}async deleteFolder(t){return this.withSftp(async e=>{try{const r=await this.getForderStructure(e,t);this.logger.info("ssh.deleteFolder list of files and directories"),this.logger.info(`ssh.deleteFolder list of files: ${r.files}`),this.logger.info(`ssh.deleteFolder list of directories: ${r.directories}`);for(const i of r.files)this.logger.info(`ssh.deleteFolder unlink file ${i}`),await this.unlink(e,i);r.directories.sort((i,o)=>o.length-i.length);for(const i of r.directories)this.logger.info(`ssh.deleteFolder rmdir ${i}`),await this.rmdir(e,i);return await this.rmdir(e,t),!0}catch(r){this.logger.error(r);const i=r instanceof Error?r.message:"";throw new Error(`ssh.deleteFolder: path: ${t}, message: ${i}`)}})}async readFile(t){return this.withSftp(async e=>new Promise((r,i)=>{e.readFile(t,(o,s)=>{if(o)return i(new Error(`ssh.readFile: ${o}`));r(s.toString())})}))}async chmod(t,e){return this.withSftp(async r=>new Promise((i,o)=>{r.chmod(t,e,s=>s?o(new Error(`ssh.chmod: ${s}, path: ${t}, mode: ${e}`)):i(void 0))}))}async checkFileExists(t){return this.withSftp(async e=>new Promise((r,i)=>{e.stat(t,(o,s)=>{if(o)return(o==null?void 0:o.code)===2?r(!1):i(new Error(`ssh.checkFileExists: err ${o}`));r(s.isFile())})}))}async checkPathExists(t){return this.withSftp(async e=>new Promise((r,i)=>{e.stat(t,(o,s)=>{if(o)return o.code===2?r({exists:!1,isFile:!1,isDirectory:!1}):i(new Error(`ssh.checkPathExists: ${o}`));r({exists:!0,isFile:s.isFile(),isDirectory:s.isDirectory()})})}))}async writeFile(t,e,r,i=432){return new Promise((o,s)=>{t.writeFile(e,r,{mode:i},a=>{if(a)return s(new Error(`ssh.writeFile: err ${a}, remotePath: ${e}`));o(!0)})})}uploadFileUsingExistingSftp(t,e,r,i=432){return new Promise((o,s)=>{y.readFile(e).then(async a=>this.writeFile(t,r,a,i).then(()=>{o(void 0)}).catch(c=>{const l=`uploadFileUsingExistingSftp: ${c}`;this.logger.error(l),s(new Error(l))}))})}async __uploadDirectory(t,e,r,i=432){return new Promise((o,s)=>{D.readdir(e,async(a,c)=>{if(a)return s(new Error(`ssh.__uploadDir: err ${a}, localDir: ${e}, remoteDir: ${r}`));try{await this.__createRemoteDirectory(t,r);for(const l of c){const d=u.join(e,l),h=`${r}/${l}`;D.lstatSync(d).isDirectory()?await this.__uploadDirectory(t,d,h,i):await this.uploadFileUsingExistingSftp(t,d,h,i)}o()}catch(l){const d=`ssh.__uploadDir: catched err ${l}`;this.logger.error(d),s(new Error(d))}})})}async uploadDirectory(t,e,r=432){return new Promise((i,o)=>{this.withSftp(async s=>{try{await this.__uploadDirectory(s,t,e,r),i()}catch(a){o(new Error(`ssh.uploadDirectory: ${a}`))}})})}__createRemoteDirectory(t,e){return new Promise((r,i)=>{const o=e.split("/");let s="";const a=c=>{if(c>=o.length)return r();s+=`${o[c]}/`,t.stat(s,l=>{l?t.mkdir(s,d=>{if(d)return i(new Error(`ssh.__createRemDir: err ${d}, remotePath: ${e}`));a(c+1)}):a(c+1)})};a(0)})}ensureRemoteDirCreated(t,e=493){return this.withSftp(async r=>{const i=t.split("/");let o="";for(const s of i){o+=`${s}/`;try{await new Promise((a,c)=>{r.stat(o,l=>{if(!l)return a();r.mkdir(o,{mode:e},d=>{if(d)return c(new Error(`ssh.createRemoteDir: err ${d}, remotePath: ${t}`));a()})})})}catch(a){throw console.error(`Failed to create directory: ${o}`,a),a}}})}async downloadFile(t,e){return this.withSftp(async r=>new Promise((i,o)=>{r.fastGet(t,e,s=>{if(s)return o(new Error(`ssh.downloadFile: err ${s}, remotePath: ${t}, localPath: ${e}`));i(!0)})}))}close(){this.closeForwardedPorts(),this.client.end()}}async function Ut(n,t,e,r){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(t),n.setNoDelay(!0)})}async function Tt(n,t,e,r,i,o){return new Promise((s,a)=>{t.forwardOut(e,r,i,o,(c,l)=>c?(n.error(`forwardOut.error: ${c}`),a(c)):s(l))})}const jt="minio-2024-12-18T13-15-44Z",Mt="supervisord-0.7.3",Ht="supervisord_0.7.3_Linux_64-bit";function g(n){return u.join(n,".platforma_ssh")}function x(n){return u.join(g(n),"binaries")}function qt(n,t){return u.join(x(n),`pl-${A()}-${S(t)}`)}function Z(n,t){return u.join(qt(n,t),"binaries")}function U(n,t){return u.join(Z(n,t),"platforma")}function Gt(n,t){return u.join(Z(n,t),"free-port")}function X(n,t){return u.join(x(n),`minio-2024-12-18T13-15-44Z-${S(t)}`)}function zt(n,t){return u.join(X(n,t),"minio")}function Lt(n,t){return u.join(x(n),`supervisord-0.7.3-${S(t)}`,Ht)}function Q(n,t){return u.join(Lt(n,t),"supervisord")}function tt(n){return u.join(g(n),"supervisor.conf")}function T(n){return u.join(g(n),"connection.txt")}function Jt(n){return u.join(g(n),"platforma_cli_logs.log")}async function Vt(n,t,e){const r=await N(n,t,e,"--daemon");if(r.stderr)throw new Error(`Can not run ssh Platforma ${r.stderr}`)}async function Wt(n,t,e){const r=await N(n,t,e,"ctl shutdown");if(r.stderr)throw new Error(`Can not stop ssh Platforma ${r.stderr}`)}function E(n,t){return t?n.platforma&&n.minio:n.platforma}function Kt(n){return n.execError===void 0}async function Yt(n,t,e,r){let i;try{i=await N(t,e,r,"ctl status")}catch(c){return{execError:String(c)}}if(i.stderr)return n.info(`supervisord ctl status: stderr occurred: ${i.stderr}, stdout: ${i.stdout}`),{rawResult:i};const o=j(i.stdout,"platforma"),s=j(i.stdout,"minio"),a={rawResult:i,platforma:o,minio:s};return a.minio||n.warn("Minio is not running on the server"),a.platforma||n.warn("Platforma is not running on the server"),a}function Zt(n,t,e,r){const i=b.randomBytes(16).toString("hex"),o=n;return`
9
- [supervisord]
10
- logfile=${t}/supervisord.log
11
- loglevel=info
12
- pidfile=${t}/supervisord.pid
13
-
14
- [inet_http_server]
15
- port=127.0.0.1:${o}
16
- username=default-user
17
- password=${i}
18
-
19
- [supervisorctl]
20
- serverurl=http://127.0.0.1:${o}
21
- username=default-user
22
- password=${i}
23
-
24
- [program:platforma]
25
- autostart=true
26
- command=${r} --config ${e}
27
- directory=${t}
28
- autorestart=true
29
- stdout_logfile=${t}/platforma_cli_logs.log
30
- stdout_logfile_maxbytes=10000
31
- stdout_logfile_backups=10
32
- redirect_stderr=true
33
- `}function Xt(n,t,e,r,i,o,s){const a=Object.entries(t).map(([d,h])=>`${d}="${h}"`).join(","),c=b.randomBytes(16).toString("hex"),l=e;return`
34
- [supervisord]
35
- logfile=${r}/supervisord.log
36
- loglevel=info
37
- pidfile=${r}/supervisord.pid
38
-
39
- [inet_http_server]
40
- port=127.0.0.1:${l}
41
- username=default-user
42
- password=${c}
43
-
44
- [supervisorctl]
45
- serverurl=http://127.0.0.1:${l}
46
- username=default-user
47
- password=${c}
48
-
49
- [program:platforma]
50
- autostart=true
51
- depends_on=minio
52
- command=${s} --config ${i}
53
- directory=${r}
54
- autorestart=true
55
-
56
- [program:minio]
57
- autostart=true
58
- environment=${a}
59
- command=${o} server ${n}
60
- directory=${r}
61
- autorestart=true
62
- `}async function N(n,t,e,r){const i=Q(t,e),o=tt(t),s=`${i} --configuration ${o} ${r}`;return await n.exec(s)}function j(n,t){return(i=>i.replace(/\x1B\[[0-9;]*m/g,""))(n).split(`
63
- `).some(i=>{const[o,s]=i.trim().split(/\s{2,}/);return o===t&&s==="Running"})}const $=w.z.object({local:w.z.number(),remote:w.z.number()}),et=w.z.object({grpc:$,http:$.optional(),monitoring:$,debug:$,minioPort:$,minioConsolePort:$}),rt=w.z.object({plUser:w.z.string(),plPassword:w.z.string(),ports:et,useGlobalAccess:w.z.boolean().default(!1),plVersion:w.z.string().default("1.18.3"),minioIsUsed:w.z.boolean().default(!0)});function it(n,t,e,r,i,o){return{plUser:n,plPassword:t,ports:e,useGlobalAccess:r,plVersion:i,minioIsUsed:o}}function nt(n){return rt.parse(JSON.parse(n))}function ot(n){return JSON.stringify(n,void 0,2)}const M=2.28;class B{constructor(t,e,r){f(this,"initState",{step:"init"});this.logger=t,this.sshClient=e,this.username=r}info(){return{username:this.username,initState:this.initState}}static async init(t,e){try{const r=await O.init(t,e);return new B(t,r,p.notEmpty(e.username))}catch(r){throw t.error(`Connection error in SshClient.init: ${r}`),r}}cleanUp(){this.sshClient.close()}async isAlive(){const t=await this.getArch(),e=await this.getUserHomeDirectory();return await Yt(this.logger,this.sshClient,e,t.arch)}async start(t){const e=await this.getArch(),r=await this.getUserHomeDirectory();try{if(!E(await this.isAlive(),t))return await Vt(this.sshClient,r,e.arch),await this.checkIsAliveWithInterval(t)}catch(i){let o=`SshPl.start: ${i}`,s="";try{s=await this.sshClient.readFile(Jt(r)),o+=`, platforma cli logs: ${s}`}catch(a){o+=`, Can not read platforma cli logs: ${a}`}throw this.logger.error(o),new Error(o)}}async stop(){const t=await this.getArch(),e=await this.getUserHomeDirectory();try{const r=await this.isAlive();if(Kt(r)){await Wt(this.sshClient,e,t.arch);const i=r.minio===!0;return await this.checkIsAliveWithInterval(i,1e3,15,!1)}}catch(r){const i=`PlSsh.stop: ${r}`;throw this.logger.error(i),new Error(i)}}async reset(){return await this.stopAndClean(),this.cleanUp(),!0}async stopAndClean(){const t=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(t)} on the server`),await this.sshClient.deleteFolder(g(t))}async platformaInit(t){const e={localWorkdir:t.localWorkdir,step:"init"},{onProgress:r}=t,i={...Qt,...t};e.plBinaryOps=i.plBinary;try{return await this.doStepDetectArch(e,r),await this.doStepDetectHome(e,r),await this.doStepReadExistedConfig(e,i,r)?(await this.doStepStopExistedPlatforma(e,r),await(r==null?void 0:r("Installation platforma...")),await this.doStepDownloadBinaries(e,r,i),await this.doStepFetchPorts(e),await this.doStepGenerateNewConfig(e,r,i),await this.doStepCreateFoldersAndSaveFiles(e,r),await this.doStepConfigureSupervisord(e,r),await this.doStepSaveNewConnectionInfo(e,r,i),await this.doStepStartPlatforma(e,r),e.connectionInfo):(await(r==null?void 0:r("Platforma is already running. Skipping initialization.")),e.existedSettings)}catch(o){const s=`SshPl.platformaInit: ${o}, state: ${JSON.stringify(this.removeSensitiveData(e))}`;throw this.logger.error(s),new Error(s)}}async doStepStopExistedPlatforma(t,e){t.step="stopExistedPlatforma",E(t.alive,t.shouldUseMinio??!1)&&(await(e==null?void 0:e("Stopping services...")),await this.stop())}removeSensitiveData(t){const e={...t};return e.generatedConfig={...e.generatedConfig,filesToCreate:{skipped:"sanitized"}},e}async doStepStartPlatforma(t,e){t.step="startPlatforma",await(e==null?void 0:e("Starting Platforma on the server...")),await this.start(t.shouldUseMinio??!1),t.started=!0,this.initState=t,await(e==null?void 0:e("Platforma has been started successfully."))}async doStepSaveNewConnectionInfo(t,e,r){t.step="saveNewConnectionInfo";const i=t.generatedConfig;await(e==null?void 0:e("Saving connection information...")),t.connectionInfo=it(i.plUser,i.plPassword,t.ports,p.notEmpty(r.useGlobalAccess),r.plBinary.version,t.shouldUseMinio??!1),await this.sshClient.writeFileOnTheServer(T(t.remoteHome),ot(t.connectionInfo)),await(e==null?void 0:e("Connection information saved."))}async doStepConfigureSupervisord(t,e){await(e==null?void 0:e("Writing supervisord configuration...")),t.step="configureSupervisord";const r=t.generatedConfig;let i;if(t.shouldUseMinio?i=Xt(r.minioConfig.storageDir,r.minioConfig.envs,await this.getFreePortForPlatformaOnServer(t.remoteHome,t.arch),r.workingDir,r.plConfig.configPath,t.binPaths.minioRelPath,t.binPaths.downloadedPl):i=Zt(await this.getFreePortForPlatformaOnServer(t.remoteHome,t.arch),r.workingDir,r.plConfig.configPath,t.binPaths.downloadedPl),!await this.sshClient.writeFileOnTheServer(tt(t.remoteHome),i))throw new Error(`Can not write supervisord config on the server ${g(t.remoteHome)}`);await(e==null?void 0:e("Supervisord configuration written."))}async doStepCreateFoldersAndSaveFiles(t,e){t.step="createFoldersAndSaveFiles";const r=t.generatedConfig;await(e==null?void 0:e("Generating folder structure..."));for(const[i,o]of Object.entries(r.filesToCreate))await this.sshClient.writeFileOnTheServer(i,o),this.logger.info(`Created file ${i}`);for(const i of r.dirsToCreate)await this.sshClient.ensureRemoteDirCreated(i),this.logger.info(`Created directory ${i}`);await(e==null?void 0:e("Folder structure created."))}async doStepGenerateNewConfig(t,e,r){t.step="generateNewConfig",await(e==null?void 0:e("Generating new config..."));const i=await v.generateSshPlConfigs({logger:this.logger,workingDir:g(t.remoteHome),portsMode:{type:"customWithMinio",ports:{debug:t.ports.debug.remote,grpc:t.ports.grpc.remote,http:t.ports.http.remote,minio:t.ports.minioPort.remote,minioConsole:t.ports.minioConsolePort.remote,monitoring:t.ports.monitoring.remote,httpLocal:t.ports.http.local,grpcLocal:t.ports.grpc.local,minioLocal:t.ports.minioPort.local}},licenseMode:r.license,useGlobalAccess:p.notEmpty(r.useGlobalAccess),plConfigPostprocessing:r.plConfigPostprocessing,useMinio:t.shouldUseMinio??!1});t.generatedConfig={...i},await(e==null?void 0:e("New config generated"))}async doStepFetchPorts(t){var e;if(t.step="fetchPorts",t.ports=await this.fetchPorts(t.remoteHome,t.arch),!t.ports.debug.remote||!t.ports.grpc.remote||!t.ports.minioPort.remote||!t.ports.minioConsolePort.remote||!t.ports.monitoring.remote||!((e=t.ports.http)!=null&&e.remote))throw new Error("SshPl.platformaInit: remote ports are not defined")}async doStepDownloadBinaries(t,e,r){t.step="downloadBinaries",await(e==null?void 0:e("Downloading and uploading required binaries..."));const i=await te(this.logger,this.sshClient);if(i<M)throw new Error(`glibc version ${i} is too old. Version ${M} or higher is required for Platforma.`);const o=await this.downloadBinariesAndUploadToTheServer(r.localWorkdir,r.plBinary,t.remoteHome,t.arch,t.shouldUseMinio??!1);await(e==null?void 0:e("All required binaries have been downloaded and uploaded.")),t.binPaths={...o,history:void 0},t.downloadedBinaries=o.history}async doStepDetectArch(t,e){t.step="detectArch",await(e==null?void 0:e("Detecting server architecture...")),t.arch=await this.getArch(),await(e==null?void 0:e("Server architecture detected."))}async doStepDetectHome(t,e){t.step="detectHome",await(e==null?void 0:e("Fetching user home directory...")),t.remoteHome=await this.getUserHomeDirectory(),await(e==null?void 0:e("User home directory retrieved."))}async doStepReadExistedConfig(t,e,r){var s;if(t.step="checkAlive",await(r==null?void 0:r("Checking platform status...")),t.alive=await this.isAlive(),!((s=t.alive)!=null&&s.platforma))return!0;if(await(r==null?void 0:r("All required services are running.")),t.existedSettings=await this.readExistedConfig(t.remoteHome),!t.existedSettings)throw new Error("SshPl.platformaInit: platforma is alive but existed settings are not found");const i=t.existedSettings.useGlobalAccess==e.useGlobalAccess,o=t.existedSettings.plVersion==e.plBinary.version;return t.needRestart=!(i&&o),this.logger.info(`SshPl.platformaInit: need restart? ${t.needRestart}`),t.shouldUseMinio=t.existedSettings.minioIsUsed,t.shouldUseMinio?this.logger.info("SshPl.platformaInit: minio is used"):this.logger.info("SshPl.platformaInit: minio is not used"),t.needRestart?(await(r==null?void 0:r("Stopping services...")),await this.stop(),!0):(await(r==null?void 0:r("Server setup completed.")),!1)}async downloadBinariesAndUploadToTheServer(t,e,r,i,o){const s=[];try{const a=await this.downloadAndUntar(t,r,i,"pl",`pl-${e.version}`);s.push(a);const c=await this.downloadAndUntar(t,r,i,"supervisord",Mt);s.push(c);const l=zt(r,i.arch);if(o){const d=await this.downloadAndUntar(t,r,i,"minio",jt);s.push(d),await this.sshClient.chmod(l,488)}return{history:s,minioRelPath:o?l:void 0,downloadedPl:U(r,i.arch)}}catch(a){const c=`SshPl.downloadBinariesAndUploadToServer: ${a}, state: ${JSON.stringify(s)}`;throw this.logger.error(c),a}}async downloadAndUntar(t,e,r,i,o){const s={};s.binBasePath=x(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 Ct(this.logger,t,i,o,r.arch,r.platform);break}catch(h){if(await p.sleep(300),d==c)throw new Error(`downloadAndUntar: ${c} attempts, last error: ${h}`)}s.downloadResult=p.notEmpty(a),s.localArchivePath=u.resolve(s.downloadResult.archivePath),s.remoteDir=u.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(t,e){const r=Q(t,e.arch),i=X(t,e.arch),o=U(t,e.arch);return!await this.sshClient.checkFileExists(o)||!await this.sshClient.checkFileExists(i)||!await this.sshClient.checkFileExists(r)}async checkIsAliveWithInterval(t,e=1e3,r=15,i=!0){const o=r*e;let s=0,a=await this.isAlive();for(;i?!E(a,t):E(a,t);){if(await p.sleep(e),s+=e,s>o)throw new Error(`isAliveWithInterval: The process did not ${i?"started":"stopped"} after ${o} ms. Live status: ${JSON.stringify(a)}`);a=await this.isAlive()}}async readExistedConfig(t){const e=await this.sshClient.readFile(T(t));return nt(e)}async fetchPorts(t,e){return{grpc:{local:await v.getFreePort(),remote:await this.getFreePortForPlatformaOnServer(t,e)},monitoring:{local:await v.getFreePort(),remote:await this.getFreePortForPlatformaOnServer(t,e)},debug:{local:await v.getFreePort(),remote:await this.getFreePortForPlatformaOnServer(t,e)},http:{local:await v.getFreePort(),remote:await this.getFreePortForPlatformaOnServer(t,e)},minioPort:{local:await v.getFreePort(),remote:await this.getFreePortForPlatformaOnServer(t,e)},minioConsolePort:{local:await v.getFreePort(),remote:await this.getFreePortForPlatformaOnServer(t,e)}}}async getLocalFreePort(){return new Promise(t=>{const e=H.createServer();e.listen(0,()=>{const r=e.address().port;e.close(i=>t(r))})})}async getFreePortForPlatformaOnServer(t,e){const r=Gt(t,e.arch),{stdout:i,stderr:o}=await this.sshClient.exec(`${r}`);if(o)throw new Error(`getFreePortForPlatformaOnServer: stderr is not empty: ${o}, stdout: ${i}`);return+i}async getArch(){const{stdout:t,stderr:e}=await this.sshClient.exec("uname -s && uname -m");if(e)throw new Error(`getArch: stderr is not empty: ${e}, stdout: ${t}`);const r=t.split(`
64
- `);return{platform:r[0],arch:r[1]}}async getUserHomeDirectory(){const{stdout:t,stderr:e}=await this.sshClient.exec("echo $HOME");if(e){const r=`/home/${this.username}`;return console.warn(`getUserHomeDirectory: stderr is not empty: ${e}, stdout: ${t}, will get a default home: ${r}`),r}return t.trim()}}const Qt={useGlobalAccess:!1,plBinary:{type:"Download",version:A()}};async function te(n,t){try{const{stdout:e,stderr:r}=await t.exec("ldd --version | head -n 1");if(r)throw new Error(`Failed to check glibc version: ${r}`);return st(e)}catch(e){throw n.error(`glibc version check failed: ${e}`),e}}function st(n){const t=n.match(/\d+\.\d+/);if(!t)throw new Error(`Could not parse glibc version from: ${n}`);return parseFloat(t[0])}exports.ConnectionInfo=rt;exports.LocalConfigYaml=V;exports.LocalPl=W;exports.PortPair=$;exports.SshClient=O;exports.SshPl=B;exports.SshPlPorts=et;exports.getDefaultPlVersion=A;exports.localPlatformaInit=Nt;exports.mergeDefaultOps=K;exports.newConnectionInfo=it;exports.parseConnectionInfo=nt;exports.parseGlibcVersion=st;exports.plProcessOps=Y;exports.stringifyConnectionInfo=ot;
1
+ export { LocalConfigYaml, LocalPl, localPlatformaInit, mergeDefaultOps, plProcessOps } from './local/pl.js';
2
+ export { getDefaultPlVersion } from './common/pl_version.js';
3
+ export { SshClient } from './ssh/ssh.js';
4
+ export { SshPl, parseGlibcVersion } from './ssh/pl.js';
5
+ export { ConnectionInfo, PortPair, SshPlPorts, newConnectionInfo, parseConnectionInfo, stringifyConnectionInfo } from './ssh/connection_info.js';
65
6
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/local/process.ts","../src/common/os_and_arch.ts","../src/common/pl_binary_download.ts","../src/common/pl_version.ts","../src/common/pl_binary.ts","../src/local/pid.ts","../src/local/trace.ts","../src/local/pl.ts","../src/ssh/ssh.ts","../src/ssh/pl_paths.ts","../src/ssh/supervisord.ts","../src/ssh/connection_info.ts","../src/ssh/pl.ts"],"sourcesContent":["import type { SpawnOptions, ChildProcess } from 'node:child_process';\nimport { spawn } from 'node:child_process';\nimport type { MiLogger } from '@milaboratories/ts-helpers';\nimport { sleep } from '@milaboratories/ts-helpers';\n\nexport type ProcessOptions = {\n cmd: string;\n args: string[];\n opts: SpawnOptions;\n};\n\nexport function processRun(logger: MiLogger, opts: ProcessOptions): ChildProcess {\n logger.info(`Running:\ncmd: ${JSON.stringify([opts.cmd, ...opts.args])}\nwd: ${opts.opts.cwd}`);\n\n logger.info(' spawning child process');\n return spawn(opts.cmd, opts.args, opts.opts);\n}\n\n// eslint-disable-next-line @typescript-eslint/require-await\nexport async function isProcessAlive(pid: number) {\n try {\n process.kill(pid, 0);\n return true;\n } catch (_e) {\n return false;\n }\n}\n\nexport function processStop(pid: number) {\n return process.kill(pid, 'SIGINT');\n}\n\nexport async function processWaitStopped(pid: number, maxMs: number) {\n const sleepMs = 100;\n let total = 0;\n while (await isProcessAlive(pid)) {\n await sleep(sleepMs);\n total += sleepMs;\n if (total > maxMs) {\n throw new Error(`The process did not stopped after ${maxMs} ms.`);\n }\n }\n}\n","export const OSes = ['linux', 'macos', 'windows'] as const;\nexport type OSType = (typeof OSes)[number];\n\n/** @param osName - should be the thing returned from either {@link os.platform())} or `uname -s` */\nexport function newOs(osName: string): OSType {\n switch (osName.toLowerCase()) {\n case 'darwin':\n return 'macos';\n case 'linux':\n return 'linux';\n case 'win32':\n return 'windows';\n default:\n throw new Error(\n `operating system '${osName}' is not currently supported by Platforma ecosystem. The list of OSes supported: `\n + JSON.stringify(OSes),\n );\n }\n}\n\nexport const Arches = ['amd64', 'arm64'] as const;\nexport type ArchType = (typeof Arches)[number];\n\n/** @param arch - should be the thing returned from either {@link os.arch())} or `uname -m` */\nexport function newArch(arch: string): ArchType {\n switch (arch) {\n case 'aarch64':\n case 'aarch64_be':\n case 'arm64':\n return 'arm64';\n\n case 'x86_64':\n case 'x64':\n return 'amd64';\n\n default:\n throw new Error(\n `processor architecture '${arch}' is not currently supported by Platforma ecosystem. The list of architectures supported: `\n + JSON.stringify(Arches),\n );\n }\n}\n","import fs from 'node:fs';\nimport fsp from 'node:fs/promises';\nimport upath from 'upath';\nimport { request } from 'undici';\nimport { Writable, Readable } from 'node:stream';\nimport { text } from 'node:stream/consumers';\nimport * as tar from 'tar';\nimport type { MiLogger } from '@milaboratories/ts-helpers';\nimport { assertNever, fileExists } from '@milaboratories/ts-helpers';\nimport decompress from 'decompress';\nimport type { ArchType, OSType } from './os_and_arch';\nimport { newOs, newArch } from './os_and_arch';\n\nconst cdn = 'https://cdn.platforma.bio/software';\n// We'll download things from Global Access if downloading from CDN has failed\n// (it might be that it's blocked from the company's network.)\nconst gaCdn = 'https://cdn-ga.pl-open.science/software';\n\nexport type DownloadBinaryResult = {\n archiveUrl: string;\n alternativeArchiveGAUrl: string;\n wasDownloadedFrom?: string;\n archivePath: string;\n archiveType: ArchiveType;\n targetFolder: string;\n baseName: string;\n};\n\nexport async function downloadBinaryNoExtract(\n logger: MiLogger,\n baseDir: string,\n softwareName: string,\n tgzName: string,\n arch: string,\n platform: string,\n): Promise<DownloadBinaryResult> {\n const opts = getPathsForDownload(softwareName, tgzName, baseDir, newArch(arch), newOs(platform));\n const { archiveUrl, alternativeArchiveGAUrl, archivePath } = opts;\n\n try {\n await downloadArchive(logger, archiveUrl, archivePath);\n opts.wasDownloadedFrom = archiveUrl;\n } catch (_e) {\n await downloadArchive(logger, alternativeArchiveGAUrl, archivePath);\n opts.wasDownloadedFrom = alternativeArchiveGAUrl;\n }\n\n return opts;\n}\n\nexport async function downloadBinary(\n logger: MiLogger,\n baseDir: string,\n softwareName: string,\n archiveName: string,\n arch: string,\n platform: string,\n): Promise<DownloadBinaryResult> {\n const opts = getPathsForDownload(softwareName, archiveName, baseDir, newArch(arch), newOs(platform));\n const { archiveUrl, alternativeArchiveGAUrl, archivePath, archiveType, targetFolder } = opts;\n\n try {\n await downloadArchive(logger, archiveUrl, archivePath);\n opts.wasDownloadedFrom = archiveUrl;\n } catch (_e) {\n await downloadArchive(logger, alternativeArchiveGAUrl, archivePath);\n opts.wasDownloadedFrom = alternativeArchiveGAUrl;\n }\n\n await extractArchive(logger, archivePath, archiveType, targetFolder);\n\n return opts;\n}\n\nfunction getPathsForDownload(\n softwareName: string,\n archiveName: string,\n baseDir: string,\n arch: ArchType,\n os: OSType,\n): DownloadBinaryResult {\n const baseName = `${archiveName}-${arch}`;\n const archiveType = osToArchiveType[os];\n\n const archiveFileName = `${baseName}.${archiveType}`;\n const archiveUrl = `${cdn}/${softwareName}/${os}/${archiveFileName}`;\n const alternativeArchiveGAUrl = `${gaCdn}/${softwareName}/${os}/${archiveFileName}`;\n const archivePath = upath.join(baseDir, archiveFileName);\n // folder where binary distribution of pl will be unpacked\n const targetFolder = upath.join(baseDir, baseName);\n\n return {\n archiveUrl,\n alternativeArchiveGAUrl,\n archivePath,\n archiveType,\n targetFolder,\n baseName,\n };\n}\n\nexport type DownloadInfo = {\n dstArchive?: string;\n fileExisted?: boolean;\n dirnameCreated?: boolean;\n statusCode?: number;\n errorMsg?: string;\n tmpPath?: string;\n wroteTmp?: boolean;\n tmpExisted?: boolean;\n renamed?: boolean;\n newExisted?: boolean;\n};\n\nexport async function downloadArchive(\n logger: MiLogger, archiveUrl: string, dstArchiveFile: string,\n): Promise<DownloadInfo> {\n const state: DownloadInfo = {};\n state.dstArchive = dstArchiveFile;\n\n try {\n state.fileExisted = await fileExists(dstArchiveFile);\n if (state.fileExisted) {\n logger.info(`Platforma Backend archive download skipped: '${dstArchiveFile}' already exists`);\n return state;\n }\n\n await fsp.mkdir(upath.dirname(dstArchiveFile), { recursive: true });\n state.dirnameCreated = true;\n\n logger.info(`Downloading archive:\\n URL: ${archiveUrl}\\n Save to: ${dstArchiveFile}`);\n\n const { body, statusCode } = await request(archiveUrl);\n state.statusCode = statusCode;\n if (statusCode != 200) {\n // completely draining the stream to prevent leaving open connections\n const textBody = await text(body);\n state.errorMsg = `failed to download archive: ${statusCode}, response: ${textBody.slice(0, 1000)}`;\n logger.error(state.errorMsg);\n throw new Error(state.errorMsg);\n }\n\n // to prevent incomplete downloads we first write in a temp file\n state.tmpPath = dstArchiveFile + '.tmp';\n // eslint-disable-next-line n/no-unsupported-features/node-builtins\n await Readable.toWeb(body).pipeTo(Writable.toWeb(fs.createWriteStream(state.tmpPath)));\n state.wroteTmp = true;\n state.tmpExisted = await fileExists(state.tmpPath);\n\n // and then atomically rename it\n await fsp.rename(state.tmpPath, dstArchiveFile);\n state.renamed = true;\n state.newExisted = await fileExists(dstArchiveFile);\n\n return state;\n } catch (e: unknown) {\n const msg = `downloadArchive: ${JSON.stringify(e)}, state: ${JSON.stringify(state)}`;\n logger.error(msg);\n throw new Error(msg);\n }\n}\n\n/** Used to prevent mid-way interrupted unarchived folders to be used */\nconst MarkerFileName = '.ok';\n\nexport async function extractArchive(\n logger: MiLogger,\n archivePath: string,\n archiveType: ArchiveType,\n dstFolder: string,\n) {\n logger.info('extracting archive...');\n logger.info(` archive path: '${archivePath}'`);\n logger.info(` target dir: '${dstFolder}'`);\n\n if (!(await fileExists(archivePath))) {\n const msg = `Platforma Backend binary archive not found at '${archivePath}'`;\n logger.error(msg);\n throw new Error(msg);\n }\n\n const markerFilePath = upath.join(dstFolder, MarkerFileName);\n\n if (await fileExists(markerFilePath)) {\n logger.info(`Platforma Backend binaries unpack skipped: '${dstFolder}' exists`);\n return;\n }\n\n if (await fileExists(dstFolder)) {\n logger.info(`Removing previous incompletely unpacked folder: '${dstFolder}'`);\n await fsp.rm(dstFolder, { recursive: true });\n }\n\n logger.info(` creating target dir '${dstFolder}'`);\n await fsp.mkdir(dstFolder, { recursive: true });\n\n logger.info(\n `Unpacking Platforma Backend archive:\\n Archive: ${archivePath}\\n Target dir: ${dstFolder}`,\n );\n\n switch (archiveType) {\n case 'tgz':\n await tar.x({\n file: archivePath,\n cwd: dstFolder,\n gzip: true,\n });\n break;\n\n case 'zip':\n await decompress(archivePath, dstFolder);\n break;\n\n default:\n assertNever(archiveType);\n }\n\n // writing marker file, to be able in the future detect that we completely unarchived the tar file\n await fsp.writeFile(markerFilePath, 'ok');\n\n logger.info(` ... unpack done.`);\n}\n\nexport type ArchiveType = 'tgz' | 'zip';\n\nconst osToArchiveType: Record<OSType, ArchiveType> = {\n linux: 'tgz',\n macos: 'tgz',\n windows: 'zip',\n};\n","declare const PL_VERSION: string;\n\nexport function getDefaultPlVersion(): string {\n return PL_VERSION;\n}\n","import type { MiLogger } from '@milaboratories/ts-helpers';\nimport { assertNever } from '@milaboratories/ts-helpers';\nimport { downloadBinary } from './pl_binary_download';\nimport { getDefaultPlVersion } from './pl_version';\nimport os from 'node:os';\nimport upath from 'upath';\nimport type { OSType } from './os_and_arch';\nimport { newOs } from './os_and_arch';\n\n/** Shows how the binary should be got. */\nexport type PlBinarySource = PlBinarySourceDownload | PlBinarySourceLocal;\n\nexport type PlBinarySourceDownload = {\n readonly type: 'Download';\n readonly version: string;\n};\n\nexport type PlBinarySourceLocal = {\n readonly type: 'Local';\n readonly path: string;\n};\n\nexport function newDefaultPlBinarySource(): PlBinarySourceDownload {\n return { type: 'Download', version: getDefaultPlVersion() };\n}\n\nexport async function resolveLocalPlBinaryPath(\n logger: MiLogger,\n downloadDir: string,\n src: PlBinarySource,\n): Promise<string> {\n switch (src.type) {\n case 'Download':\n // eslint-disable-next-line no-case-declarations\n const ops = await downloadBinary(logger, downloadDir, 'pl', `pl-${src.version}`, os.arch(), os.platform());\n return upath.join(ops.baseName, 'binaries', osToBinaryName[newOs(os.platform())]);\n\n case 'Local':\n return src.path;\n\n default:\n assertNever(src);\n }\n}\n\nexport const osToBinaryName: Record<OSType, string> = {\n linux: 'platforma',\n macos: 'platforma',\n windows: 'platforma.exe',\n};\n","import { fileExists } from '@milaboratories/ts-helpers';\nimport fs from 'node:fs/promises';\nimport upath from 'upath';\n\nexport function filePid(dir: string) {\n return upath.join(dir, 'pl_pid');\n}\n\nexport async function readPid(filePath: string): Promise<number | undefined> {\n if (!(await fileExists(filePath))) {\n return undefined;\n }\n\n const text = await fs.readFile(filePath);\n\n return Number(text.toString());\n}\n\nexport async function writePid(filePath: string, pid: number) {\n await fs.writeFile(filePath, JSON.stringify(pid));\n}\n","import type { MiLogger } from '@milaboratories/ts-helpers';\n\n/** Records all inputs and outputs of one's choice, so if the error happened\n * one can check how it was by just printing this structure. */\nexport type Trace = Record<string, any>;\n\nexport function newTrace(): Trace {\n return {};\n}\n\nexport function trace(t: Trace, k: string, v: any) {\n t[k] = v;\n return v;\n}\n\n/** Creates a trace and runs a function with it. The function can record all its\n * logs or traces using `trace` fn. */\nexport async function withTrace<T>(\n logger: MiLogger,\n fn: (trace: (k: string, v: any) => typeof v, t: Trace) => Promise<T>,\n): Promise<T> {\n const t = newTrace();\n try {\n const result = await fn((k: string, v: any) => trace(t, k, v), t);\n return result;\n } catch (e: any) {\n logger.error(`error ${e} while doing traced operation, state: ${JSON.stringify(t)}`);\n throw e;\n }\n}\n","import type {\n ProcessOptions } from './process';\nimport {\n isProcessAlive,\n processStop,\n processWaitStopped,\n processRun,\n} from './process';\nimport type { PlBinarySource } from '../common/pl_binary';\nimport { newDefaultPlBinarySource, resolveLocalPlBinaryPath } from '../common/pl_binary';\nimport type { MiLogger } from '@milaboratories/ts-helpers';\nimport { notEmpty } from '@milaboratories/ts-helpers';\nimport type { ChildProcess, SpawnOptions } from 'node:child_process';\nimport { filePid, readPid, writePid } from './pid';\nimport type { Trace } from './trace';\nimport { withTrace } from './trace';\nimport upath from 'upath';\nimport fsp from 'node:fs/promises';\nimport type { Required } from 'utility-types';\nimport * as os from 'node:os';\n\nexport const LocalConfigYaml = 'config-local.yaml';\n\n/**\n * Represents a local running pl-core,\n * and has methods to start, check if it's running, stop and wait for stopping it.\n * Also, a hook on pl-core closed can be provided.\n */\nexport class LocalPl {\n private instance?: ChildProcess;\n public pid?: number;\n private nRuns: number = 0;\n private lastRunHistory: Trace = {};\n private wasStopped = false;\n\n constructor(\n private readonly logger: MiLogger,\n private readonly workingDir: string,\n private readonly startOptions: ProcessOptions,\n private readonly initialStartHistory: Trace,\n private readonly onClose?: (pl: LocalPl) => Promise<void>,\n private readonly onError?: (pl: LocalPl) => Promise<void>,\n private readonly onCloseAndError?: (pl: LocalPl) => Promise<void>,\n private readonly onCloseAndErrorNoStop?: (pl: LocalPl) => Promise<void>,\n ) {}\n\n async start() {\n await withTrace(this.logger, async (trace, t) => {\n this.wasStopped = false;\n const instance = processRun(this.logger, this.startOptions);\n instance.on('error', (e: any) => {\n this.logger.error(\n `error '${e}', while running platforma, started opts: ${JSON.stringify(this.debugInfo())}`,\n );\n\n // keep in mind there are no awaits here, it will be asynchronous\n if (this.onError !== undefined) void this.onError(this);\n if (this.onCloseAndError !== undefined) void this.onCloseAndError(this);\n if (this.onCloseAndErrorNoStop !== undefined && !this.wasStopped)\n void this.onCloseAndErrorNoStop(this);\n });\n instance.on('close', () => {\n this.logger.warn(`platforma was closed, started opts: ${JSON.stringify(this.debugInfo())}`);\n\n // keep in mind there are no awaits here, it will be asynchronous\n if (this.onClose !== undefined) void this.onClose(this);\n if (this.onCloseAndError !== undefined) void this.onCloseAndError(this);\n if (this.onCloseAndErrorNoStop !== undefined && !this.wasStopped)\n void this.onCloseAndErrorNoStop(this);\n });\n\n trace('started', true);\n\n const pidFile = trace('pidFile', filePid(this.workingDir));\n trace('pid', notEmpty(instance.pid));\n trace('pidWritten', await writePid(pidFile, notEmpty(instance.pid)));\n\n this.nRuns++;\n this.instance = instance;\n this.pid = instance.pid;\n this.lastRunHistory = t;\n });\n }\n\n stop() {\n // TODO use this.instance to stop the process\n this.wasStopped = true;\n processStop(notEmpty(this.pid));\n }\n\n async waitStopped() {\n await processWaitStopped(notEmpty(this.pid), 15000);\n }\n\n stopped() {\n return this.wasStopped;\n }\n\n async isAlive(): Promise<boolean> {\n return await isProcessAlive(notEmpty(this.pid));\n }\n\n debugInfo() {\n return {\n lastRunHistory: this.lastRunHistory,\n nRuns: this.nRuns,\n pid: this.pid,\n workingDir: this.workingDir,\n initialStartHistory: this.initialStartHistory,\n wasStopped: this.wasStopped,\n };\n }\n}\n\n/** Options to start a local pl-core. */\nexport type LocalPlOptions = {\n /** From what directory start a process. */\n readonly workingDir: string;\n /** A string representation of yaml config. */\n readonly config: string;\n /** How to get a binary, download it or get an existing one (default: download latest version) */\n readonly plBinary?: PlBinarySource;\n /** Additional options for a process, environments, stdout, stderr etc. */\n readonly spawnOptions?: SpawnOptions;\n /**\n * If the previous pl-core was started from the same directory,\n * we can check if it's still running and then stop it before starting a new one.\n * (default: true)\n */\n readonly closeOld?: boolean;\n\n readonly onClose?: (pl: LocalPl) => Promise<void>;\n readonly onError?: (pl: LocalPl) => Promise<void>;\n readonly onCloseAndError?: (pl: LocalPl) => Promise<void>;\n readonly onCloseAndErrorNoStop?: (pl: LocalPl) => Promise<void>;\n};\n\nexport type LocalPlOptionsFull = Required<LocalPlOptions, 'plBinary' | 'spawnOptions' | 'closeOld'>;\n\n/**\n * Starts pl-core, if the option was provided downloads a binary, reads license environments etc.\n */\nexport async function localPlatformaInit(logger: MiLogger, _ops: LocalPlOptions): Promise<LocalPl> {\n // filling-in default values\n\n // Backend could consume a lot of CPU power,\n // we want to keep at least a couple for UI and other apps to work.\n const numCpu = Math.max(os.cpus().length - 2, 1);\n const ops = mergeDefaultOps(_ops, numCpu);\n\n return await withTrace(logger, async (trace, t) => {\n trace('startOptions', { ...ops, config: 'too wordy' });\n\n const workDir = upath.resolve(ops.workingDir);\n\n if (ops.closeOld) {\n trace('closeOld', await localPlatformaReadPidAndStop(logger, workDir));\n }\n\n const configPath = upath.join(workDir, LocalConfigYaml);\n\n logger.info(`writing configuration '${configPath}'...`);\n await fsp.writeFile(configPath, ops.config);\n\n const plBinPath = upath.join(workDir, 'binaries');\n const baseBinaryPath = await resolveLocalPlBinaryPath(logger, plBinPath, ops.plBinary);\n const binaryPath = trace('binaryPath', upath.join('binaries', baseBinaryPath));\n\n const processOpts = plProcessOps(binaryPath, configPath, ops, workDir, process.env);\n trace('processOpts', {\n cmd: processOpts.cmd,\n args: processOpts.args,\n cwd: processOpts.opts.cwd,\n });\n\n const pl = new LocalPl(\n logger,\n ops.workingDir,\n processOpts,\n t,\n ops.onClose,\n ops.onError,\n ops.onCloseAndError,\n ops.onCloseAndErrorNoStop,\n );\n await pl.start();\n\n return pl;\n });\n}\n\n/** Reads a pid of the old pl-core if it was started in the same working directory,\n * and closes it. */\nasync function localPlatformaReadPidAndStop(\n logger: MiLogger,\n workingDir: string,\n): Promise<Record<string, any>> {\n return await withTrace(logger, async (trace, t) => {\n const file = trace('pidFilePath', filePid(workingDir));\n\n const oldPid = trace('pid', await readPid(file));\n const alive = trace('wasAlive', await isProcessAlive(oldPid));\n\n if (oldPid !== undefined && alive) {\n trace('stopped', processStop(oldPid));\n trace('waitStopped', await processWaitStopped(oldPid, 10_000));\n }\n\n return t;\n });\n}\n\n/** Gets default options for the whole init process\n * and overrides them with the provided options. */\nexport function mergeDefaultOps(ops: LocalPlOptions, numCpu: number): LocalPlOptionsFull {\n const result: {\n plBinary: PlBinarySource;\n spawnOptions: SpawnOptions;\n closeOld: boolean;\n } = {\n plBinary: newDefaultPlBinarySource(),\n spawnOptions: {\n env: {\n GOMAXPROCS: String(numCpu),\n },\n },\n closeOld: true,\n };\n\n if (ops.spawnOptions?.env) {\n result.spawnOptions.env = { ...result.spawnOptions.env, ...ops.spawnOptions.env };\n }\n\n if (ops.spawnOptions) {\n const withoutEnv = { ...ops.spawnOptions };\n delete withoutEnv['env'];\n result.spawnOptions = { ...result.spawnOptions, ...withoutEnv };\n }\n\n const withoutSpawnOps = { ...ops };\n delete withoutSpawnOps['spawnOptions'];\n\n return { ...result, ...withoutSpawnOps };\n}\n\n/** Gets default options for a platforma local binary\n * and overrides them with the provided options. */\nexport function plProcessOps(\n binaryPath: any,\n configPath: string,\n ops: LocalPlOptionsFull,\n workDir: string,\n defaultEnv: Record<string, string | undefined>,\n): ProcessOptions {\n const result: ProcessOptions = {\n cmd: binaryPath,\n args: ['--config', configPath],\n opts: {\n env: { ...defaultEnv },\n cwd: workDir,\n stdio: ['pipe', 'ignore', 'inherit'],\n windowsHide: true, // hide a terminal on Windows\n },\n };\n\n if (ops.spawnOptions?.env) {\n result.opts.env = { ...result.opts.env, ...ops.spawnOptions.env };\n }\n\n const withoutEnv = { ...ops.spawnOptions };\n delete withoutEnv['env'];\n result.opts = { ...result.opts, ...withoutEnv };\n\n return result;\n}\n","/* eslint-disable @typescript-eslint/no-misused-promises */\n/* eslint-disable @typescript-eslint/no-base-to-string */\nimport type { ConnectConfig, ClientChannel, SFTPWrapper } from 'ssh2';\nimport ssh, { Client } from 'ssh2';\nimport net from 'node:net';\nimport dns from 'node:dns';\nimport fs from 'node:fs';\nimport { readFile } from 'node:fs/promises';\nimport upath from 'upath';\nimport { RetryablePromise, type MiLogger } from '@milaboratories/ts-helpers';\nimport { randomBytes } from 'node:crypto';\n\nconst defaultConfig: ConnectConfig = {\n keepaliveInterval: 60000,\n keepaliveCountMax: 10,\n};\n\nexport type SshAuthMethods = 'publickey' | 'password';\nexport type SshAuthMethodsResult = SshAuthMethods[];\nexport type SshDirContent = {\n files: string[];\n directories: string[];\n};\n\nexport class SshClient {\n private config?: ConnectConfig;\n public homeDir?: string;\n private forwardedServers: net.Server[] = [];\n\n constructor(\n private readonly logger: MiLogger,\n private readonly client: Client,\n ) {}\n\n /**\n * Initializes the SshClient and establishes a connection using the provided configuration.\n * @param config - The connection configuration object for the SSH client.\n * @returns A new instance of SshClient with an active connection.\n */\n public static async init(logger: MiLogger, config: ConnectConfig): Promise<SshClient> {\n const withDefaults = {\n ...defaultConfig,\n ...config,\n };\n\n const client = new SshClient(logger, new Client());\n await client.connect(withDefaults);\n\n return client;\n }\n\n public getForwardedServers() {\n return this.forwardedServers;\n }\n\n public getFullHostName() {\n return `${this.config?.host}:${this.config?.port}`;\n }\n\n public getUserName() {\n return this.config?.username;\n }\n\n /**\n * Connects to the SSH server using the specified configuration.\n * @param config - The connection configuration object for the SSH client.\n * @returns A promise that resolves when the connection is established or rejects on error.\n */\n public async connect(config: ConnectConfig) {\n this.config = config;\n return await connect(this.client, config, () => {}, () => {});\n }\n\n /**\n * Executes a command on the SSH server.\n * @param command - The command to execute on the remote server.\n * @returns A promise resolving with the command's stdout and stderr outputs.\n */\n public async exec(command: string): Promise<SshExecResult> {\n return new Promise((resolve, reject) => {\n this.client.exec(command, (err: any, stream: ClientChannel) => {\n if (err) {\n return reject(new Error(`ssh.exec: ${command}: ${err}`));\n }\n\n let stdout = '';\n let stderr = '';\n\n stream.on('close', (code: number) => {\n if (code === 0) {\n resolve({ stdout, stderr });\n } else {\n reject(new Error(`Command ${command} exited with code ${code}, stdout: ${stdout}, stderr: ${stderr}`));\n }\n }).on('data', (data: ArrayBuffer) => {\n stdout += data.toString();\n }).stderr.on('data', (data: ArrayBuffer) => {\n stderr += data.toString();\n });\n });\n });\n }\n\n /**\n * Retrieves the supported authentication methods for a given host and port.\n * @param host - The hostname or IP address of the server.\n * @param port - The port number to connect to on the server.\n * @returns 'publickey' | 'password'[] A promise resolving with a list of supported authentication methods.\n */\n public static async getAuthTypes(host: string, port: number): Promise<SshAuthMethodsResult> {\n return new Promise((resolve) => {\n let stdout = '';\n const conn = new Client();\n\n conn.on('ready', () => {\n conn.end();\n const types = this.extractAuthMethods(stdout);\n resolve(types.length === 0 ? ['publickey', 'password'] : types as SshAuthMethodsResult);\n });\n\n conn.on('error', () => {\n conn.end();\n resolve(['publickey', 'password']);\n });\n\n conn.connect({\n host,\n port,\n username: new Date().getTime().toString(),\n debug: (err) => {\n stdout += `${err}\\n`;\n },\n });\n });\n }\n\n /**\n * Extracts authentication methods from debug logs.\n * @param log - The debug log output containing authentication information.\n * @returns An array of extracted authentication methods.\n */\n private static extractAuthMethods(log: string): string[] {\n const match = log.match(/Inbound: Received USERAUTH_FAILURE \\((.+)\\)/);\n return match && match[1] ? match[1].split(',').map((method) => method.trim()) : [];\n }\n\n /**\n * Sets up port forwarding between a remote port on the SSH server and a local port.\n * A new connection is used for this operation instead of an existing one.\n * @param ports - An object specifying the remote and local port configuration.\n * @param config - Optional connection configuration for the SSH client.\n * @returns { server: net.Server } A promise resolving with the created server instance.\n */\n public async forwardPort(ports: { remotePort: number; localPort: number; localHost?: string }, config?: ConnectConfig): Promise<{ server: net.Server }> {\n const log = `ssh.forward:${ports.localPort}:${ports.remotePort}.id_${randomBytes(1).toString('hex')}`;\n config = config ?? this.config;\n\n // we make this thing persistent so that if the connection\n // drops (it happened in the past because of lots of errors and forwardOut opened channels),\n // we'll recreate it here.\n const persistentClient = new RetryablePromise((p: RetryablePromise<Client>) => {\n return new Promise<Client>((resolve, reject) => {\n const client = new Client();\n\n client.on('ready', () => {\n this.logger.info(`${log}.client.ready`);\n resolve(client);\n });\n\n client.on('error', (err) => {\n this.logger.info(`${log}.client.error: ${err}`);\n p.reset();\n reject(err);\n });\n\n client.on('close', () => {\n this.logger.info(`${log}.client.closed`);\n p.reset();\n });\n\n client.connect(config!);\n });\n });\n\n await persistentClient.ensure(); // warm up a connection\n\n return new Promise((resolve, reject) => {\n const server = net.createServer({ pauseOnConnect: true }, async (localSocket) => {\n const sockLog = `${log}.sock_${randomBytes(1).toString('hex')}`;\n // this.logger.info(`${sockLog}.localSocket: start connection`);\n let conn: Client;\n try {\n conn = await persistentClient.ensure();\n } catch (e: unknown) {\n this.logger.info(`${sockLog}.persistentClient.catch: ${e}`);\n localSocket.end();\n return;\n }\n\n // Remove TCP buffering.\n // Although it means less throughput (bad), it also less latency (good).\n // It could help when we have\n // small messages like in our grpc transactions.\n // And it also could help when we have tcp forwarding to not buffer messages in the middle.\n (conn as any).setNoDelay(true);\n localSocket.setNoDelay(true);\n\n let stream: ClientChannel;\n try {\n stream = await forwardOut(this.logger, conn, '127.0.0.1', 0, '127.0.0.1', ports.remotePort);\n } catch (e: unknown) {\n this.logger.error(`${sockLog}.forwardOut.err: ${e}`);\n localSocket.end();\n return;\n }\n\n localSocket.pipe(stream);\n stream.pipe(localSocket);\n localSocket.resume();\n // this.logger.info(`${sockLog}.forwardOut: connected`);\n\n stream.on('error', (err: unknown) => {\n this.logger.error(`${sockLog}.stream.error: ${err}`);\n localSocket.end();\n stream.end();\n });\n stream.on('close', () => {\n // this.logger.info(`${sockLog}.stream.close: closed`);\n localSocket.end();\n stream.end();\n });\n localSocket.on('close', () => {\n this.logger.info(`${sockLog}.localSocket: closed`);\n localSocket.end();\n stream.end();\n });\n });\n\n server.listen(ports.localPort, '127.0.0.1', () => {\n this.logger.info(`${log}.server: started listening`);\n this.forwardedServers.push(server);\n resolve({ server });\n });\n\n server.on('error', (err) => {\n server.close();\n reject(new Error(`${log}.server: error: ${JSON.stringify(err)}`));\n });\n\n server.on('close', () => {\n this.logger.info(`${log}.server: closed ${JSON.stringify(ports)}`);\n this.forwardedServers = this.forwardedServers.filter((s) => s !== server);\n });\n });\n }\n\n public closeForwardedPorts(): void {\n this.logger.info('[SSH] Closing all forwarded ports...');\n this.forwardedServers.forEach((server) => {\n const rawAddress = server.address();\n if (rawAddress && typeof rawAddress !== 'string') {\n const address: net.AddressInfo = rawAddress;\n this.logger.info(`[SSH] Closing port forward for server ${address.address}:${address.port}`);\n }\n\n server.close();\n });\n this.forwardedServers = [];\n }\n\n /**\n * Checks if a specified host is available by performing a DNS lookup.\n * @param hostname - The hostname or IP address to check.\n * @returns A promise resolving with `true` if the host is reachable, otherwise `false`.\n */\n public static async checkHostAvailability(hostname: string): Promise<boolean> {\n return new Promise((resolve) => {\n dns.lookup(hostname, (err) => {\n resolve(!err);\n });\n });\n }\n\n /**\n * Determines whether a private key requires a passphrase for use.\n * @param privateKey - The private key content to check.\n * @returns A promise resolving with `true` if a passphrase is required, otherwise `false`.\n */\n public static async isPassphraseRequiredForKey(privateKey: string): Promise<boolean> {\n return new Promise((resolve, reject) => {\n try {\n const keyOrError = ssh.utils.parseKey(privateKey);\n if (keyOrError instanceof Error) {\n resolve(true);\n }\n return resolve(false);\n } catch (err: unknown) {\n console.log('Error parsing privateKey');\n reject(new Error(`ssh.isPassphraseRequiredForKey: err ${err}`));\n }\n });\n }\n\n /**\n * Uploads a local file to a remote server via SFTP.\n * This function creates new SFTP connection\n * @param localPath - The local file path.\n * @param remotePath - The remote file path on the server.\n * @returns A promise resolving with `true` if the file was successfully uploaded.\n */\n public async uploadFile(localPath: string, remotePath: string): Promise<boolean> {\n return await this.withSftp(async (sftp) => {\n return new Promise((resolve, reject) => {\n sftp.fastPut(localPath, remotePath, (err) => {\n if (err) {\n const newErr = new Error(\n `ssh.uploadFile: err: ${err}, localPath: ${localPath}, remotePath: ${remotePath}`);\n return reject(newErr);\n }\n resolve(true);\n });\n });\n });\n }\n\n public async withSftp<R>(callback: (sftp: SFTPWrapper) => Promise<R>): Promise<R> {\n return new Promise((resolve, reject) => {\n this.client.sftp((err, sftp) => {\n if (err) {\n return reject(new Error(`ssh.withSftp: sftp err: ${err}`));\n }\n\n callback(sftp)\n .then(resolve)\n .catch((err) => {\n reject(new Error(`ssh.withSftp.callback: err ${err}`));\n })\n .finally(() => {\n sftp?.end();\n });\n });\n });\n }\n\n public async writeFileOnTheServer(remotePath: string, data: string | Buffer, mode: number = 0o660) {\n return this.withSftp(async (sftp) => {\n return this.writeFile(sftp, remotePath, data, mode);\n });\n }\n\n public async getForderStructure(sftp: SFTPWrapper, remotePath: string, data: SshDirContent = { files: [], directories: [] }): Promise<SshDirContent> {\n return new Promise((resolve, reject) => {\n sftp.readdir(remotePath, async (err, items) => {\n if (err) {\n return reject(err);\n }\n\n for (const item of items) {\n const itemPath = `${remotePath}/${item.filename}`;\n if (item.attrs.isDirectory()) {\n data.directories.push(itemPath);\n try {\n await this.getForderStructure(sftp, itemPath, data);\n } catch (error) {\n return reject(error instanceof Error ? error : new Error(String(error)));\n }\n } else {\n data.files.push(itemPath);\n }\n }\n resolve(data);\n });\n });\n }\n\n public rmdir(sftp: SFTPWrapper, path: string) {\n return new Promise((resolve, reject) => {\n sftp.rmdir(path, (err) => err ? reject(err) : resolve(true));\n });\n }\n\n public unlink(sftp: SFTPWrapper, path: string) {\n return new Promise((resolve, reject) => {\n sftp.unlink(path, (err) => err ? reject(err) : resolve(true));\n });\n }\n\n public async deleteFolder(path: string) {\n return this.withSftp(async (sftp) => {\n try {\n const list = await this.getForderStructure(sftp, path);\n this.logger.info(`ssh.deleteFolder list of files and directories`);\n this.logger.info(`ssh.deleteFolder list of files: ${list.files}`);\n this.logger.info(`ssh.deleteFolder list of directories: ${list.directories}`);\n\n for (const filePath of list.files) {\n this.logger.info(`ssh.deleteFolder unlink file ${filePath}`);\n await this.unlink(sftp, filePath);\n }\n\n list.directories.sort((a, b) => b.length - a.length);\n\n for (const directoryPath of list.directories) {\n this.logger.info(`ssh.deleteFolder rmdir ${directoryPath}`);\n await this.rmdir(sftp, directoryPath);\n }\n\n await this.rmdir(sftp, path);\n return true;\n } catch (e: unknown) {\n this.logger.error(e);\n const message = e instanceof Error ? e.message : '';\n throw new Error(`ssh.deleteFolder: path: ${path}, message: ${message}`);\n }\n });\n }\n\n public async readFile(remotePath: string): Promise<string> {\n return this.withSftp(async (sftp) => {\n return new Promise((resolve, reject) => {\n sftp.readFile(remotePath, (err, buffer) => {\n if (err) {\n return reject(new Error(`ssh.readFile: ${err}`));\n }\n resolve(buffer.toString());\n });\n });\n });\n }\n\n async chmod(path: string, mode: number) {\n return this.withSftp(async (sftp) => {\n return new Promise((resolve, reject) => {\n sftp.chmod(path, mode, (err) => {\n if (err) {\n return reject(new Error(`ssh.chmod: ${err}, path: ${path}, mode: ${mode}`));\n }\n return resolve(undefined);\n });\n });\n });\n }\n\n async checkFileExists(remotePath: string) {\n return this.withSftp(async (sftp) => {\n return new Promise((resolve, reject) => {\n sftp.stat(remotePath, (err: Error | undefined, stats) => {\n if (err) {\n if ((err as unknown as { code?: number })?.code === 2) {\n return resolve(false);\n }\n return reject(new Error(`ssh.checkFileExists: err ${err}`));\n }\n resolve(stats.isFile());\n });\n });\n });\n }\n\n async checkPathExists(remotePath: string): Promise<{ exists: boolean; isFile: boolean; isDirectory: boolean }> {\n return this.withSftp(async (sftp) => {\n return new Promise((resolve, reject) => {\n sftp.stat(remotePath, (err, stats) => {\n if (err) {\n if ((err as Error & { code: number }).code === 2) {\n return resolve({ exists: false, isFile: false, isDirectory: false });\n }\n return reject(new Error(`ssh.checkPathExists: ${err}`));\n }\n resolve({\n exists: true,\n isFile: stats.isFile(),\n isDirectory: stats.isDirectory(),\n });\n });\n });\n });\n }\n\n private async writeFile(sftp: SFTPWrapper, remotePath: string, data: string | Buffer, mode: number = 0o660): Promise<boolean> {\n return new Promise((resolve, reject) => {\n sftp.writeFile(remotePath, data, { mode }, (err) => {\n if (err) {\n return reject(new Error(`ssh.writeFile: err ${err}, remotePath: ${remotePath}`));\n }\n resolve(true);\n });\n });\n }\n\n public uploadFileUsingExistingSftp(sftp: SFTPWrapper, localPath: string, remotePath: string, mode: number = 0o660) {\n return new Promise((resolve, reject) => {\n void readFile(localPath).then(async (result: Buffer) => {\n return this.writeFile(sftp, remotePath, result, mode)\n .then(() => {\n resolve(undefined);\n })\n .catch((err) => {\n const msg = `uploadFileUsingExistingSftp: ${err}`;\n this.logger.error(msg);\n reject(new Error(msg));\n });\n });\n });\n }\n\n private async __uploadDirectory(sftp: SFTPWrapper, localDir: string, remoteDir: string, mode: number = 0o660): Promise<void> {\n return new Promise((resolve, reject) => {\n fs.readdir(localDir, async (err, files) => {\n if (err) {\n return reject(new Error(`ssh.__uploadDir: err ${err}, localDir: ${localDir}, remoteDir: ${remoteDir}`));\n }\n\n try {\n await this.__createRemoteDirectory(sftp, remoteDir);\n for (const file of files) {\n const localPath = upath.join(localDir, file);\n const remotePath = `${remoteDir}/${file}`;\n\n if (fs.lstatSync(localPath).isDirectory()) {\n await this.__uploadDirectory(sftp, localPath, remotePath, mode);\n } else {\n await this.uploadFileUsingExistingSftp(sftp, localPath, remotePath, mode);\n }\n }\n\n resolve();\n } catch (err) {\n const msg = `ssh.__uploadDir: catched err ${err}`;\n this.logger.error(msg);\n reject(new Error(msg));\n }\n });\n });\n }\n\n /**\n * Uploads a local directory and its contents (including subdirectories) to the remote server via SFTP.\n * @param localDir - The path to the local directory to upload.\n * @param remoteDir - The path to the remote directory on the server.\n * @returns A promise that resolves when the directory and its contents are uploaded.\n */\n public async uploadDirectory(localDir: string, remoteDir: string, mode: number = 0o660): Promise<void> {\n return new Promise((resolve, reject) => {\n void this.withSftp(async (sftp: SFTPWrapper) => {\n try {\n await this.__uploadDirectory(sftp, localDir, remoteDir, mode);\n resolve();\n } catch (e: unknown) {\n reject(new Error(`ssh.uploadDirectory: ${e}`));\n }\n });\n });\n }\n\n /**\n * Ensures that a remote directory and all its parent directories exist.\n * @param sftp - The SFTP wrapper.\n * @param remotePath - The path to the remote directory.\n * @returns A promise that resolves when the directory is created.\n */\n private __createRemoteDirectory(sftp: SFTPWrapper, remotePath: string): Promise<void> {\n return new Promise((resolve, reject) => {\n const directories = remotePath.split('/');\n let currentPath = '';\n\n const createNext = (index: number) => {\n if (index >= directories.length) {\n return resolve();\n }\n\n currentPath += `${directories[index]}/`;\n\n sftp.stat(currentPath, (err) => {\n if (err) {\n sftp.mkdir(currentPath, (err) => {\n if (err) {\n return reject(new Error(`ssh.__createRemDir: err ${err}, remotePath: ${remotePath}`));\n }\n createNext(index + 1);\n });\n } else {\n createNext(index + 1);\n }\n });\n };\n\n createNext(0);\n });\n }\n\n /**\n * Ensures that a remote directory and all its parent directories exist.\n * @param sftp - The SFTP wrapper.\n * @param remotePath - The path to the remote directory.\n * @returns A promise that resolves when the directory is created.\n */\n public ensureRemoteDirCreated(remotePath: string, mode: number = 0o755): Promise<void> {\n return this.withSftp(async (sftp) => {\n const directories = remotePath.split('/');\n let currentPath = '';\n\n for (const directory of directories) {\n currentPath += `${directory}/`;\n\n try {\n await new Promise<void>((resolve, reject) => {\n sftp.stat(currentPath, (err) => {\n if (!err) return resolve();\n\n sftp.mkdir(currentPath, { mode }, (err) => {\n if (err) {\n return reject(new Error(`ssh.createRemoteDir: err ${err}, remotePath: ${remotePath}`));\n }\n resolve();\n });\n });\n });\n } catch (error) {\n console.error(`Failed to create directory: ${currentPath}`, error);\n throw error;\n }\n }\n });\n }\n\n /**\n * Downloads a file from the remote server to a local path via SFTP.\n * @param remotePath - The remote file path on the server.\n * @param localPath - The local file path to save the file.\n * @returns A promise resolving with `true` if the file was successfully downloaded.\n */\n public async downloadFile(remotePath: string, localPath: string): Promise<boolean> {\n return this.withSftp(async (sftp) => {\n return new Promise((resolve, reject) => {\n sftp.fastGet(remotePath, localPath, (err) => {\n if (err) {\n return reject(new Error(`ssh.downloadFile: err ${err}, remotePath: ${remotePath}, localPath: ${localPath}`));\n }\n resolve(true);\n });\n });\n });\n }\n\n /**\n * Closes the SSH client connection and forwarded ports.\n */\n public close(): void {\n this.closeForwardedPorts();\n this.client.end();\n }\n}\n\nexport type SshExecResult = { stdout: string; stderr: string };\n\nasync function connect(\n client: Client,\n config: ConnectConfig,\n onError: (e: unknown) => void,\n onClose: () => void,\n): Promise<Client> {\n return new Promise((resolve, reject) => {\n client.on('ready', () => {\n resolve(client);\n });\n\n client.on('error', (err: unknown) => {\n onError(err);\n reject(new Error(`ssh.connect: ${err}`));\n });\n\n client.on('close', () => {\n onClose();\n });\n\n client.connect(config);\n\n // Remove TCP buffering.\n // Although it means less throughput (bad), it also means less latency (good).\n // It could help when we have\n // small messages like in our grpc transactions.\n // And it also could help when we have tcp forwarding to not buffer messages in the middle.\n (client as any).setNoDelay(true);\n });\n}\n\nasync function forwardOut(logger: MiLogger, conn: Client, localHost: string, localPort: number, remoteHost: string, remotePort: number): Promise<ClientChannel> {\n return new Promise((resolve, reject) => {\n conn.forwardOut(localHost, localPort, remoteHost, remotePort, (err, stream) => {\n if (err) {\n logger.error(`forwardOut.error: ${err}`);\n return reject(err);\n }\n\n return resolve(stream);\n });\n });\n}\n","/** Just a lot of hardcoded paths of our current ssh deployment. */\n\nimport upath from 'upath';\nimport { newArch } from '../common/os_and_arch';\nimport { getDefaultPlVersion } from '../common/pl_version';\n\nexport const minioDirName = 'minio-2024-12-18T13-15-44Z';\nexport const supervisordDirName = 'supervisord-0.7.3';\nexport const supervisordSubDirName = 'supervisord_0.7.3_Linux_64-bit';\n\nexport function workDir(remoteHome: string) {\n return upath.join(remoteHome, '.platforma_ssh');\n}\n\nexport function binariesDir(remoteHome: string) {\n return upath.join(workDir(remoteHome), 'binaries');\n}\n\nexport function platformaBaseDir(remoteHome: string, arch: string) {\n return upath.join(binariesDir(remoteHome), `pl-${getDefaultPlVersion()}-${newArch(arch)}`);\n}\n\nexport function platformaDir(remoteHome: string, arch: string) {\n return upath.join(platformaBaseDir(remoteHome, arch), 'binaries');\n}\n\nexport function platformaBin(remoteHome: string, arch: string): string {\n return upath.join(platformaDir(remoteHome, arch), 'platforma');\n}\n\nexport function platformaConf(remoteHome: string): string {\n return upath.join(workDir(remoteHome), 'config.yaml');\n}\n\nexport function platformaFreePortBin(remoteHome: string, arch: string): string {\n return upath.join(platformaDir(remoteHome, arch), 'free-port');\n}\n\nexport function minioDir(remoteHome: string, arch: string) {\n return upath.join(binariesDir(remoteHome), `minio-2024-12-18T13-15-44Z-${newArch(arch)}`);\n}\n\nexport function minioBin(remoteHome: string, arch: string) {\n return upath.join(minioDir(remoteHome, arch), 'minio');\n}\n\nexport function supervisorBinDir(remoteHome: string, arch: string) {\n return upath.join(binariesDir(remoteHome), `supervisord-0.7.3-${newArch(arch)}`, supervisordSubDirName);\n}\n\nexport function supervisorBin(remoteHome: string, arch: string): string {\n return upath.join(supervisorBinDir(remoteHome, arch), 'supervisord');\n}\n\nexport function supervisorConf(remoteHome: string) {\n return upath.join(workDir(remoteHome), 'supervisor.conf');\n}\n\nexport function connectionInfo(remoteHome: string) {\n return upath.join(workDir(remoteHome), `connection.txt`);\n}\n\nexport function platformaCliLogs(remoteHome: string) {\n return upath.join(workDir(remoteHome), 'platforma_cli_logs.log');\n}\n","/** Provides helper functions to work with supervisord */\n\nimport type { MiLogger } from '@milaboratories/ts-helpers';\nimport * as plpath from './pl_paths';\nimport type { SshClient, SshExecResult } from './ssh';\nimport { randomBytes } from 'node:crypto';\n\nexport async function supervisorCtlStart(\n sshClient: SshClient,\n remoteHome: string, arch: string,\n) {\n const result = await supervisorExec(sshClient, remoteHome, arch, '--daemon');\n\n if (result.stderr) {\n throw new Error(`Can not run ssh Platforma ${result.stderr}`);\n }\n}\n\nexport async function supervisorStop(\n sshClient: SshClient,\n remoteHome: string, arch: string,\n) {\n const result = await supervisorExec(sshClient, remoteHome, arch, 'ctl shutdown');\n\n if (result.stderr) {\n throw new Error(`Can not stop ssh Platforma ${result.stderr}`);\n }\n}\n\n/** Provides a simple true/false response got from supervisord status\n * along with a debug info that could be showed in error logs (raw response from the command, parsed response etc). */\nexport type SupervisorStatus = {\n platforma?: boolean;\n minio?: boolean;\n rawResult?: SshExecResult;\n execError?: string;\n};\n\nexport function isAllAlive(status: SupervisorStatus, shouldUseMinio: boolean) {\n if (shouldUseMinio) {\n return status.platforma && status.minio;\n }\n\n return status.platforma;\n}\n\nexport function isSupervisordRunning(status: SupervisorStatus) {\n return status.execError === undefined;\n}\n\nexport async function supervisorStatus(\n logger: MiLogger,\n sshClient: SshClient,\n remoteHome: string, arch: string,\n): Promise<SupervisorStatus> {\n let result: SshExecResult;\n try {\n result = await supervisorExec(sshClient, remoteHome, arch, 'ctl status');\n } catch (e: unknown) {\n return { execError: String(e) };\n }\n\n if (result.stderr) {\n logger.info(`supervisord ctl status: stderr occurred: ${result.stderr}, stdout: ${result.stdout}`);\n\n return { rawResult: result };\n }\n\n const platforma = isProgramRunning(result.stdout, 'platforma');\n const minio = isProgramRunning(result.stdout, 'minio');\n const status: SupervisorStatus = {\n rawResult: result,\n platforma,\n minio,\n };\n\n if (!status.minio) {\n logger.warn('Minio is not running on the server');\n }\n\n if (!status.platforma) {\n logger.warn('Platforma is not running on the server');\n }\n\n return status;\n}\n\n/** Generates the config for supervisord.\n * docs: https://github.com/ochinchina/supervisord?tab=readme-ov-file#supervised-program-settings\n */\nexport function generateSupervisordConfig(\n supervisorRemotePort: number,\n remoteWorkDir: string,\n platformaConfigPath: string,\n plPath: string,\n) {\n const password = randomBytes(16).toString('hex');\n const freePort = supervisorRemotePort;\n\n return `\n[supervisord]\nlogfile=${remoteWorkDir}/supervisord.log\nloglevel=info\npidfile=${remoteWorkDir}/supervisord.pid\n\n[inet_http_server]\nport=127.0.0.1:${freePort}\nusername=default-user\npassword=${password}\n\n[supervisorctl]\nserverurl=http://127.0.0.1:${freePort}\nusername=default-user\npassword=${password}\n\n[program:platforma]\nautostart=true\ncommand=${plPath} --config ${platformaConfigPath}\ndirectory=${remoteWorkDir}\nautorestart=true\nstdout_logfile=${remoteWorkDir}/platforma_cli_logs.log\nstdout_logfile_maxbytes=10000\nstdout_logfile_backups=10\nredirect_stderr=true\n`;\n}\n\n/** @deprecated, we use minio only on old deployments that existed before we remove minio,\n * for new servers use generation of the config above. */\nexport function generateSupervisordConfigWithMinio(\n minioStorageDir: string,\n minioEnvs: Record<string, string>,\n supervisorRemotePort: number,\n remoteWorkDir: string,\n platformaConfigPath: string,\n\n minioPath: string,\n plPath: string,\n) {\n const minioEnvStr = Object.entries(minioEnvs).map(([key, value]) => `${key}=\"${value}\"`).join(',');\n const password = randomBytes(16).toString('hex');\n const freePort = supervisorRemotePort;\n\n return `\n[supervisord]\nlogfile=${remoteWorkDir}/supervisord.log\nloglevel=info\npidfile=${remoteWorkDir}/supervisord.pid\n\n[inet_http_server]\nport=127.0.0.1:${freePort}\nusername=default-user\npassword=${password}\n\n[supervisorctl]\nserverurl=http://127.0.0.1:${freePort}\nusername=default-user\npassword=${password}\n\n[program:platforma]\nautostart=true\ndepends_on=minio\ncommand=${plPath} --config ${platformaConfigPath}\ndirectory=${remoteWorkDir}\nautorestart=true\n\n[program:minio]\nautostart=true\nenvironment=${minioEnvStr}\ncommand=${minioPath} server ${minioStorageDir}\ndirectory=${remoteWorkDir}\nautorestart=true\n`;\n}\n\nexport async function supervisorExec(\n sshClient: SshClient,\n remoteHome: string, arch: string,\n command: string,\n) {\n const supervisorCmd = plpath.supervisorBin(remoteHome, arch);\n const supervisorConf = plpath.supervisorConf(remoteHome);\n\n const cmd = `${supervisorCmd} --configuration ${supervisorConf} ${command}`;\n return await sshClient.exec(cmd);\n}\n\nfunction isProgramRunning(output: string, programName: string) {\n // eslint-disable-next-line no-control-regex\n const stripAnsi = (str: string) => str.replace(/\\x1B\\[[0-9;]*m/g, '');\n\n const cleanedOutput = stripAnsi(output);\n\n return cleanedOutput.split('\\n').some((line) => {\n const [name, status] = line.trim().split(/\\s{2,}/); // Split string by 2 spaces.\n\n return name === programName && status === 'Running';\n });\n}\n","/** We store all info about the connection on the server,\n * so that another client could read the file and connect from another machine. */\nimport { z } from 'zod';\n\n//\n// Types\n//\n\nexport const PortPair = z.object({\n local: z.number(),\n remote: z.number(),\n});\n/** The pair of ports for forwarding. */\nexport type PortPair = z.infer<typeof PortPair>;\n\nexport const SshPlPorts = z.object({\n grpc: PortPair,\n http: PortPair.optional(),\n monitoring: PortPair,\n debug: PortPair,\n /** @deprecated */\n minioPort: PortPair,\n /** @deprecated */\n minioConsolePort: PortPair,\n});\n/** All info about ports that are forwarded. */\nexport type SshPlPorts = z.infer<typeof SshPlPorts>;\n\nexport const ConnectionInfo = z.object({\n plUser: z.string(),\n plPassword: z.string(),\n ports: SshPlPorts,\n\n // It's false by default because it was added later,\n // and in some deployments there won't be useGlobalAccess flag in the file.\n useGlobalAccess: z.boolean().default(false),\n\n // We added the field afterwards, the pl backend was this version.\n plVersion: z.string().default('1.18.3'),\n\n // It's true by default because it was added later and previous installation use minio.\n minioIsUsed: z.boolean().default(true),\n});\n/** The content of the file that holds all the info about the connection on the remote server. */\nexport type ConnectionInfo = z.infer<typeof ConnectionInfo>;\n\n//\n// Funcs\n//\n\nexport function newConnectionInfo(\n plUser: string,\n plPassword: string,\n ports: SshPlPorts,\n useGlobalAccess: boolean,\n plVersion: string,\n minioIsUsed: boolean,\n): ConnectionInfo {\n return {\n plUser,\n plPassword,\n ports,\n useGlobalAccess,\n plVersion,\n minioIsUsed: minioIsUsed,\n };\n}\n\nexport function parseConnectionInfo(content: string): ConnectionInfo {\n return ConnectionInfo.parse(JSON.parse(content));\n}\n\nexport function stringifyConnectionInfo(conn: ConnectionInfo): string {\n return JSON.stringify(conn, undefined, 2);\n}\n","import type * as ssh from 'ssh2';\nimport { SshClient } from './ssh';\nimport type { MiLogger } from '@milaboratories/ts-helpers';\nimport { sleep, notEmpty } from '@milaboratories/ts-helpers';\nimport type { DownloadBinaryResult } from '../common/pl_binary_download';\nimport { downloadBinaryNoExtract } from '../common/pl_binary_download';\nimport upath from 'upath';\nimport * as plpath from './pl_paths';\nimport { getDefaultPlVersion } from '../common/pl_version';\n\nimport net from 'node:net';\nimport type { PlConfig, PlLicenseMode, SshPlConfigGenerationResult } from '@milaboratories/pl-config';\nimport { getFreePort, generateSshPlConfigs } from '@milaboratories/pl-config';\nimport type { SupervisorStatus } from './supervisord';\nimport { supervisorStatus, supervisorStop as supervisorCtlShutdown, generateSupervisordConfigWithMinio, supervisorCtlStart, isSupervisordRunning, generateSupervisordConfig, isAllAlive } from './supervisord';\nimport type { ConnectionInfo, SshPlPorts } from './connection_info';\nimport { newConnectionInfo, parseConnectionInfo, stringifyConnectionInfo } from './connection_info';\nimport type { PlBinarySourceDownload } from '../common/pl_binary';\n\nconst minRequiredGlibcVersion = 2.28;\n\nexport class SshPl {\n private initState: PlatformaInitState = { step: 'init' };\n constructor(\n public readonly logger: MiLogger,\n public readonly sshClient: SshClient,\n private readonly username: string,\n ) { }\n\n public info() {\n return {\n username: this.username,\n initState: this.initState,\n };\n }\n\n public static async init(logger: MiLogger, config: ssh.ConnectConfig): Promise<SshPl> {\n try {\n const sshClient = await SshClient.init(logger, config);\n return new SshPl(logger, sshClient, notEmpty(config.username));\n } catch (e: unknown) {\n logger.error(`Connection error in SshClient.init: ${e}`);\n throw e;\n }\n }\n\n public cleanUp() {\n this.sshClient.close();\n }\n\n /** Provides an info if the platforma and minio are running along with the debug info. */\n public async isAlive(): Promise<SupervisorStatus> {\n const arch = await this.getArch();\n const remoteHome = await this.getUserHomeDirectory();\n return await supervisorStatus(this.logger, this.sshClient, remoteHome, arch.arch);\n }\n\n /** Starts all the services on the server.\n * Idempotent semantic: we could call it several times. */\n public async start(shouldUseMinio: boolean) {\n const arch = await this.getArch();\n const remoteHome = await this.getUserHomeDirectory();\n\n try {\n if (!isAllAlive(await this.isAlive(), shouldUseMinio)) {\n await supervisorCtlStart(this.sshClient, remoteHome, arch.arch);\n\n // We are waiting for Platforma to run to ensure that it has started.\n return await this.checkIsAliveWithInterval(shouldUseMinio);\n }\n } catch (e: unknown) {\n let msg = `SshPl.start: ${e}`;\n\n let logs = '';\n try {\n logs = await this.sshClient.readFile(plpath.platformaCliLogs(remoteHome));\n msg += `, platforma cli logs: ${logs}`;\n } catch (e: unknown) {\n msg += `, Can not read platforma cli logs: ${e}`;\n }\n\n this.logger.error(msg);\n throw new Error(msg);\n }\n }\n\n /** Stops all the services on the server.\n * Idempotent semantic: we could call it several times. */\n public async stop() {\n const arch = await this.getArch();\n const remoteHome = await this.getUserHomeDirectory();\n\n try {\n const alive = await this.isAlive();\n if (isSupervisordRunning(alive)) {\n await supervisorCtlShutdown(this.sshClient, remoteHome, arch.arch);\n // Check if Minio is running by looking at the alive status\n const shouldUseMinio = alive.minio === true;\n return await this.checkIsAliveWithInterval(shouldUseMinio, 1000, 15, false);\n }\n } catch (e: unknown) {\n const msg = `PlSsh.stop: ${e}`;\n this.logger.error(msg);\n throw new Error(msg);\n }\n }\n\n /** Stops the services, deletes a directory with the state and closes SSH connection. */\n public async reset(): Promise<boolean> {\n await this.stopAndClean();\n this.cleanUp();\n return true;\n }\n\n /** Stops platforma and deletes its state. */\n public async stopAndClean(): Promise<void> {\n const remoteHome = await this.getUserHomeDirectory();\n\n this.logger.info(`pl.reset: Stop Platforma on the server`);\n await this.stop();\n\n this.logger.info(`pl.reset: Deleting Platforma workDir ${plpath.workDir(remoteHome)} on the server`);\n await this.sshClient.deleteFolder(plpath.workDir(remoteHome));\n }\n\n /** Downloads binaries and untar them on the server,\n * generates all the configs, creates necessary dirs,\n * and finally starts all the services. */\n public async platformaInit(options: SshPlConfig): Promise<ConnectionInfo> {\n const state: PlatformaInitState = { localWorkdir: options.localWorkdir, step: 'init' };\n\n const { onProgress } = options;\n\n // merge options with default ops.\n const ops: SshPlConfig = {\n ...defaultSshPlConfig,\n ...options,\n };\n state.plBinaryOps = ops.plBinary;\n\n try {\n await this.doStepDetectArch(state, onProgress);\n await this.doStepDetectHome(state, onProgress);\n\n const needRestartPlatforma = await this.doStepReadExistedConfig(state, ops, onProgress);\n if (!needRestartPlatforma) {\n await onProgress?.('Platforma is already running. Skipping initialization.');\n return state.existedSettings!;\n }\n await this.doStepStopExistedPlatforma(state, onProgress);\n\n await onProgress?.('Installation platforma...');\n\n await this.doStepDownloadBinaries(state, onProgress, ops);\n await this.doStepFetchPorts(state);\n await this.doStepGenerateNewConfig(state, onProgress, ops);\n await this.doStepCreateFoldersAndSaveFiles(state, onProgress);\n await this.doStepConfigureSupervisord(state, onProgress);\n await this.doStepSaveNewConnectionInfo(state, onProgress, ops);\n await this.doStepStartPlatforma(state, onProgress);\n\n return state.connectionInfo!;\n } catch (e: unknown) {\n const msg = `SshPl.platformaInit: ${e}, state: ${JSON.stringify(this.removeSensitiveData(state))}`;\n this.logger.error(msg);\n\n throw new Error(msg);\n }\n }\n\n private async doStepStopExistedPlatforma(state: PlatformaInitState, onProgress: ((...args: any) => Promise<any>) | undefined) {\n state.step = 'stopExistedPlatforma';\n if (!isAllAlive(state.alive!, state.shouldUseMinio ?? false)) {\n return;\n }\n\n await onProgress?.('Stopping services...');\n await this.stop();\n }\n\n private removeSensitiveData(state: PlatformaInitState): PlatformaInitState {\n const stateCopy = { ...state };\n stateCopy.generatedConfig = { ...stateCopy.generatedConfig, filesToCreate: { skipped: 'sanitized' } } as SshPlConfigGenerationResult;\n return stateCopy;\n }\n\n private async doStepStartPlatforma(state: PlatformaInitState, onProgress: ((...args: any) => Promise<any>) | undefined) {\n state.step = 'startPlatforma';\n await onProgress?.('Starting Platforma on the server...');\n await this.start(state.shouldUseMinio ?? false);\n state.started = true;\n this.initState = state;\n\n await onProgress?.('Platforma has been started successfully.');\n }\n\n private async doStepSaveNewConnectionInfo(state: PlatformaInitState, onProgress: ((...args: any) => Promise<any>) | undefined, ops: SshPlConfig) {\n state.step = 'saveNewConnectionInfo';\n const config = state.generatedConfig!;\n await onProgress?.('Saving connection information...');\n state.connectionInfo = newConnectionInfo(\n config.plUser,\n config.plPassword,\n state.ports!,\n notEmpty(ops.useGlobalAccess),\n ops.plBinary!.version,\n state.shouldUseMinio ?? false,\n );\n await this.sshClient.writeFileOnTheServer(\n plpath.connectionInfo(state.remoteHome!),\n stringifyConnectionInfo(state.connectionInfo),\n );\n await onProgress?.('Connection information saved.');\n }\n\n private async doStepConfigureSupervisord(state: PlatformaInitState, onProgress: ((...args: any) => Promise<any>) | undefined) {\n await onProgress?.('Writing supervisord configuration...');\n state.step = 'configureSupervisord';\n\n const config = state.generatedConfig!;\n\n let supervisorConfig: string;\n if (state.shouldUseMinio) {\n supervisorConfig = generateSupervisordConfigWithMinio(\n config.minioConfig.storageDir,\n config.minioConfig.envs,\n await this.getFreePortForPlatformaOnServer(state.remoteHome!, state.arch!),\n config.workingDir,\n config.plConfig.configPath,\n state.binPaths!.minioRelPath!,\n state.binPaths!.downloadedPl,\n );\n } else {\n supervisorConfig = generateSupervisordConfig(\n await this.getFreePortForPlatformaOnServer(state.remoteHome!, state.arch!),\n config.workingDir,\n config.plConfig.configPath,\n state.binPaths!.downloadedPl,\n );\n }\n\n const writeResult = await this.sshClient.writeFileOnTheServer(plpath.supervisorConf(state.remoteHome!), supervisorConfig);\n if (!writeResult) {\n throw new Error(`Can not write supervisord config on the server ${plpath.workDir(state.remoteHome!)}`);\n }\n await onProgress?.('Supervisord configuration written.');\n }\n\n private async doStepCreateFoldersAndSaveFiles(state: PlatformaInitState, onProgress: ((...args: any) => Promise<any>) | undefined) {\n state.step = 'createFoldersAndSaveFiles';\n const config = state.generatedConfig!;\n await onProgress?.('Generating folder structure...');\n for (const [filePath, content] of Object.entries(config.filesToCreate)) {\n await this.sshClient.writeFileOnTheServer(filePath, content);\n this.logger.info(`Created file ${filePath}`);\n }\n\n for (const dir of config.dirsToCreate) {\n await this.sshClient.ensureRemoteDirCreated(dir);\n this.logger.info(`Created directory ${dir}`);\n }\n await onProgress?.('Folder structure created.');\n }\n\n private async doStepGenerateNewConfig(state: PlatformaInitState, onProgress: ((...args: any) => Promise<any>) | undefined, ops: SshPlConfig) {\n state.step = 'generateNewConfig';\n\n await onProgress?.('Generating new config...');\n const config = await generateSshPlConfigs({\n logger: this.logger,\n workingDir: plpath.workDir(state.remoteHome!),\n portsMode: {\n type: 'customWithMinio',\n ports: {\n debug: state.ports!.debug.remote,\n grpc: state.ports!.grpc.remote,\n http: state.ports!.http!.remote,\n minio: state.ports!.minioPort.remote,\n minioConsole: state.ports!.minioConsolePort.remote,\n monitoring: state.ports!.monitoring.remote,\n\n httpLocal: state.ports!.http!.local,\n grpcLocal: state.ports!.grpc.local,\n minioLocal: state.ports!.minioPort.local,\n },\n },\n licenseMode: ops.license,\n useGlobalAccess: notEmpty(ops.useGlobalAccess),\n plConfigPostprocessing: ops.plConfigPostprocessing,\n useMinio: state.shouldUseMinio ?? false,\n });\n state.generatedConfig = { ...config };\n await onProgress?.('New config generated');\n }\n\n private async doStepFetchPorts(state: PlatformaInitState) {\n state.step = 'fetchPorts';\n state.ports = await this.fetchPorts(state.remoteHome!, state.arch!);\n\n if (!state.ports.debug.remote\n || !state.ports.grpc.remote\n || !state.ports.minioPort.remote\n || !state.ports.minioConsolePort.remote\n || !state.ports.monitoring.remote\n || !state.ports.http?.remote) {\n throw new Error(`SshPl.platformaInit: remote ports are not defined`);\n }\n }\n\n private async doStepDownloadBinaries(state: PlatformaInitState, onProgress: ((...args: any) => Promise<any>) | undefined, ops: SshPlConfig) {\n state.step = 'downloadBinaries';\n await onProgress?.('Downloading and uploading required binaries...');\n\n const glibcVersion = await getGlibcVersion(this.logger, this.sshClient);\n if (glibcVersion < minRequiredGlibcVersion)\n throw new Error(`glibc version ${glibcVersion} is too old. Version ${minRequiredGlibcVersion} or higher is required for Platforma.`);\n\n const downloadRes = await this.downloadBinariesAndUploadToTheServer(\n ops.localWorkdir, ops.plBinary!, state.remoteHome!, state.arch!, state.shouldUseMinio ?? false,\n );\n await onProgress?.('All required binaries have been downloaded and uploaded.');\n\n state.binPaths = { ...downloadRes, history: undefined };\n state.downloadedBinaries = downloadRes.history;\n }\n\n private async doStepDetectArch(state: PlatformaInitState, onProgress: ((...args: any) => Promise<any>) | undefined) {\n state.step = 'detectArch';\n await onProgress?.('Detecting server architecture...');\n state.arch = await this.getArch();\n await onProgress?.('Server architecture detected.');\n }\n\n private async doStepDetectHome(state: PlatformaInitState, onProgress: ((...args: any) => Promise<any>) | undefined) {\n state.step = 'detectHome';\n await onProgress?.('Fetching user home directory...');\n state.remoteHome = await this.getUserHomeDirectory();\n await onProgress?.('User home directory retrieved.');\n }\n\n private async doStepReadExistedConfig(\n state: PlatformaInitState,\n ops: SshPlConfig,\n onProgress: ((...args: any) => Promise<any>) | undefined,\n ): Promise<boolean> {\n state.step = 'checkAlive';\n await onProgress?.('Checking platform status...');\n state.alive = await this.isAlive();\n\n if (!state.alive?.platforma) {\n return true;\n }\n\n await onProgress?.('All required services are running.');\n\n state.existedSettings = await this.readExistedConfig(state.remoteHome!);\n if (!state.existedSettings) {\n throw new Error(`SshPl.platformaInit: platforma is alive but existed settings are not found`);\n }\n\n const sameGA = state.existedSettings.useGlobalAccess == ops.useGlobalAccess;\n const samePlVersion = state.existedSettings.plVersion == ops.plBinary!.version;\n state.needRestart = !(sameGA && samePlVersion);\n this.logger.info(`SshPl.platformaInit: need restart? ${state.needRestart}`);\n\n state.shouldUseMinio = state.existedSettings.minioIsUsed;\n if (state.shouldUseMinio) {\n this.logger.info(`SshPl.platformaInit: minio is used`);\n } else {\n this.logger.info(`SshPl.platformaInit: minio is not used`);\n }\n\n if (!state.needRestart) {\n await onProgress?.('Server setup completed.');\n return false;\n }\n\n await onProgress?.('Stopping services...');\n await this.stop();\n\n return true;\n }\n\n public async downloadBinariesAndUploadToTheServer(\n localWorkdir: string,\n plBinary: PlBinarySourceDownload,\n remoteHome: string,\n arch: Arch,\n shouldUseMinio: boolean,\n ) {\n const state: DownloadAndUntarState[] = [];\n try {\n const pl = await this.downloadAndUntar(\n localWorkdir, remoteHome, arch,\n 'pl', `pl-${plBinary.version}`,\n );\n state.push(pl);\n\n const supervisor = await this.downloadAndUntar(\n localWorkdir, remoteHome, arch,\n 'supervisord', plpath.supervisordDirName,\n );\n state.push(supervisor);\n\n const minioPath = plpath.minioBin(remoteHome, arch.arch);\n if (shouldUseMinio) {\n const minio = await this.downloadAndUntar(\n localWorkdir, remoteHome, arch,\n 'minio', plpath.minioDirName,\n );\n state.push(minio);\n await this.sshClient.chmod(minioPath, 0o750);\n }\n\n return {\n history: state,\n minioRelPath: shouldUseMinio ? minioPath : undefined,\n downloadedPl: plpath.platformaBin(remoteHome, arch.arch),\n };\n } catch (e: unknown) {\n const msg = `SshPl.downloadBinariesAndUploadToServer: ${e}, state: ${JSON.stringify(state)}`;\n this.logger.error(msg);\n throw e;\n }\n }\n\n /** We have to extract pl in the remote server,\n * because Windows doesn't support symlinks\n * that are found in Linux pl binaries tgz archive.\n * For this reason, we extract all to the remote server.\n * It requires `tar` to be installed on the server\n * (it's not installed for Rocky Linux for example). */\n public async downloadAndUntar(\n localWorkdir: string,\n remoteHome: string,\n arch: Arch,\n softwareName: string,\n tgzName: string,\n ): Promise<DownloadAndUntarState> {\n const state: DownloadAndUntarState = {};\n state.binBasePath = plpath.binariesDir(remoteHome);\n await this.sshClient.ensureRemoteDirCreated(state.binBasePath);\n state.binBasePathCreated = true;\n\n let downloadBinaryResult: DownloadBinaryResult | null = null;\n const attempts = 5;\n for (let i = 1; i <= attempts; i++) {\n try {\n downloadBinaryResult = await downloadBinaryNoExtract(\n this.logger,\n localWorkdir,\n softwareName,\n tgzName,\n arch.arch, arch.platform,\n );\n break;\n } catch (e: unknown) {\n await sleep(300);\n if (i == attempts) {\n throw new Error(`downloadAndUntar: ${attempts} attempts, last error: ${e}`);\n }\n }\n }\n state.downloadResult = notEmpty(downloadBinaryResult);\n\n state.localArchivePath = upath.resolve(state.downloadResult.archivePath);\n state.remoteDir = upath.join(state.binBasePath, state.downloadResult.baseName);\n state.remoteArchivePath = state.remoteDir + '.tgz';\n\n await this.sshClient.ensureRemoteDirCreated(state.remoteDir);\n await this.sshClient.uploadFile(state.localArchivePath, state.remoteArchivePath);\n state.uploadDone = true;\n\n try {\n await this.sshClient.exec('hash tar');\n } catch (_) {\n throw new Error(`tar is not installed on the server. Please install it before running Platforma.`);\n }\n\n // TODO: Create a proper archive to avoid xattr warnings\n const untarResult = await this.sshClient.exec(\n `tar --warning=no-all -xvf ${state.remoteArchivePath} --directory=${state.remoteDir}`,\n );\n\n if (untarResult.stderr)\n throw new Error(`downloadAndUntar: untar: stderr occurred: ${untarResult.stderr}, stdout: ${untarResult.stdout}`);\n\n state.untarDone = true;\n\n return state;\n }\n\n public async needDownload(remoteHome: string, arch: Arch) {\n const checkPathSupervisor = plpath.supervisorBin(remoteHome, arch.arch);\n const checkPathMinio = plpath.minioDir(remoteHome, arch.arch);\n const checkPathPlatforma = plpath.platformaBin(remoteHome, arch.arch);\n\n if (!await this.sshClient.checkFileExists(checkPathPlatforma)\n || !await this.sshClient.checkFileExists(checkPathMinio)\n || !await this.sshClient.checkFileExists(checkPathSupervisor)) {\n return true;\n }\n\n return false;\n }\n\n public async checkIsAliveWithInterval(shouldUseMinio: boolean, interval: number = 1000, count = 15, shouldStart = true): Promise<void> {\n const maxMs = count * interval;\n\n let total = 0;\n let alive = await this.isAlive();\n while (shouldStart ? !isAllAlive(alive, shouldUseMinio) : isAllAlive(alive, shouldUseMinio)) {\n await sleep(interval);\n total += interval;\n if (total > maxMs) {\n throw new Error(`isAliveWithInterval: The process did not ${shouldStart ? 'started' : 'stopped'} after ${maxMs} ms. Live status: ${JSON.stringify(alive)}`);\n }\n alive = await this.isAlive();\n }\n }\n\n public async readExistedConfig(remoteHome: string): Promise<ConnectionInfo> {\n const connectionInfo = await this.sshClient.readFile(plpath.connectionInfo(remoteHome));\n return parseConnectionInfo(connectionInfo);\n }\n\n public async fetchPorts(remoteHome: string, arch: Arch): Promise<SshPlPorts> {\n const ports: SshPlPorts = {\n grpc: {\n local: await getFreePort(),\n remote: await this.getFreePortForPlatformaOnServer(remoteHome, arch),\n },\n monitoring: {\n local: await getFreePort(),\n remote: await this.getFreePortForPlatformaOnServer(remoteHome, arch),\n },\n debug: {\n local: await getFreePort(),\n remote: await this.getFreePortForPlatformaOnServer(remoteHome, arch),\n },\n http: {\n local: await getFreePort(),\n remote: await this.getFreePortForPlatformaOnServer(remoteHome, arch),\n },\n minioPort: {\n local: await getFreePort(),\n remote: await this.getFreePortForPlatformaOnServer(remoteHome, arch),\n },\n minioConsolePort: {\n local: await getFreePort(),\n remote: await this.getFreePortForPlatformaOnServer(remoteHome, arch),\n },\n };\n\n return ports;\n }\n\n public async getLocalFreePort(): Promise<number> {\n return new Promise((res) => {\n const srv = net.createServer();\n srv.listen(0, () => {\n const port = (srv.address() as net.AddressInfo).port;\n srv.close((_) => res(port));\n });\n });\n }\n\n public async getFreePortForPlatformaOnServer(remoteHome: string, arch: Arch): Promise<number> {\n const freePortBin = plpath.platformaFreePortBin(remoteHome, arch.arch);\n\n const { stdout, stderr } = await this.sshClient.exec(`${freePortBin}`);\n if (stderr) {\n throw new Error(`getFreePortForPlatformaOnServer: stderr is not empty: ${stderr}, stdout: ${stdout}`);\n }\n\n return +stdout;\n }\n\n public async getArch(): Promise<Arch> {\n const { stdout, stderr } = await this.sshClient.exec('uname -s && uname -m');\n if (stderr)\n throw new Error(`getArch: stderr is not empty: ${stderr}, stdout: ${stdout}`);\n\n const arr = stdout.split('\\n');\n\n return {\n platform: arr[0],\n arch: arr[1],\n };\n }\n\n public async getUserHomeDirectory() {\n const { stdout, stderr } = await this.sshClient.exec('echo $HOME');\n\n if (stderr) {\n const home = `/home/${this.username}`;\n console.warn(`getUserHomeDirectory: stderr is not empty: ${stderr}, stdout: ${stdout}, will get a default home: ${home}`);\n\n return home;\n }\n\n return stdout.trim();\n }\n}\n\ntype Arch = { platform: string; arch: string };\n\nexport type SshPlConfig = {\n localWorkdir: string;\n license: PlLicenseMode;\n useGlobalAccess?: boolean;\n plBinary?: PlBinarySourceDownload;\n\n onProgress?: (...args: any) => Promise<any>;\n plConfigPostprocessing?: (config: PlConfig) => PlConfig;\n};\n\nconst defaultSshPlConfig: Pick<\n SshPlConfig,\n | 'useGlobalAccess'\n | 'plBinary'\n> = {\n useGlobalAccess: false,\n plBinary: {\n type: 'Download',\n version: getDefaultPlVersion(),\n },\n};\n\ntype BinPaths = {\n history?: DownloadAndUntarState[];\n minioRelPath?: string;\n downloadedPl: string;\n};\n\ntype DownloadAndUntarState = {\n binBasePath?: string;\n binBasePathCreated?: boolean;\n downloadResult?: DownloadBinaryResult;\n attempts?: number;\n\n localArchivePath?: string;\n remoteDir?: string;\n remoteArchivePath?: string;\n uploadDone?: boolean;\n untarDone?: boolean;\n};\n\ntype PlatformaInitStep =\n 'init'\n | 'detectArch'\n | 'detectHome'\n | 'checkAlive'\n | 'stopExistedPlatforma'\n | 'downloadBinaries'\n | 'fetchPorts'\n | 'generateNewConfig'\n | 'createFoldersAndSaveFiles'\n | 'configureSupervisord'\n | 'saveNewConnectionInfo'\n | 'startPlatforma';\n\ntype PlatformaInitState = {\n step: PlatformaInitStep;\n localWorkdir?: string;\n plBinaryOps?: PlBinarySourceDownload;\n arch?: Arch;\n remoteHome?: string;\n alive?: SupervisorStatus;\n existedSettings?: ConnectionInfo;\n needRestart?: boolean;\n shouldUseMinio?: boolean;\n downloadedBinaries?: DownloadAndUntarState[];\n binPaths?: BinPaths;\n ports?: SshPlPorts;\n generatedConfig?: SshPlConfigGenerationResult;\n connectionInfo?: ConnectionInfo;\n started?: boolean;\n};\n\n/**\n * Gets the glibc version on the remote system\n * @returns The glibc version as a number\n * @throws Error if version cannot be determined\n */\nasync function getGlibcVersion(logger: MiLogger, sshClient: SshClient): Promise <number> {\n try {\n const { stdout, stderr } = await sshClient.exec('ldd --version | head -n 1');\n if (stderr) {\n throw new Error(`Failed to check glibc version: ${stderr}`);\n }\n return parseGlibcVersion(stdout);\n } catch (e: unknown) {\n logger.error(`glibc version check failed: ${e}`);\n throw e;\n }\n}\n\nexport function parseGlibcVersion(output: string): number {\n const versionMatch = output.match(/\\d+\\.\\d+/);\n if (!versionMatch) {\n throw new Error(`Could not parse glibc version from: ${output}`);\n }\n\n return parseFloat(versionMatch[0]);\n}\n"],"names":["processRun","logger","opts","spawn","isProcessAlive","pid","processStop","processWaitStopped","maxMs","total","sleep","OSes","newOs","osName","Arches","newArch","arch","cdn","gaCdn","downloadBinaryNoExtract","baseDir","softwareName","tgzName","platform","getPathsForDownload","archiveUrl","alternativeArchiveGAUrl","archivePath","downloadArchive","downloadBinary","archiveName","archiveType","targetFolder","extractArchive","os","baseName","osToArchiveType","archiveFileName","upath","dstArchiveFile","state","fileExists","fsp","body","statusCode","request","textBody","text","Readable","Writable","fs","e","msg","MarkerFileName","dstFolder","markerFilePath","tar","decompress","assertNever","getDefaultPlVersion","newDefaultPlBinarySource","resolveLocalPlBinaryPath","downloadDir","src","ops","osToBinaryName","filePid","dir","readPid","filePath","writePid","newTrace","trace","t","k","v","withTrace","fn","LocalConfigYaml","LocalPl","workingDir","startOptions","initialStartHistory","onClose","onError","onCloseAndError","onCloseAndErrorNoStop","__publicField","instance","pidFile","notEmpty","localPlatformaInit","_ops","numCpu","mergeDefaultOps","workDir","localPlatformaReadPidAndStop","configPath","plBinPath","baseBinaryPath","binaryPath","processOpts","plProcessOps","pl","file","oldPid","alive","result","_a","withoutEnv","withoutSpawnOps","defaultEnv","defaultConfig","SshClient","client","config","withDefaults","Client","_b","connect","command","resolve","reject","err","stream","stdout","stderr","code","data","host","port","conn","types","log","match","method","ports","randomBytes","persistentClient","RetryablePromise","p","server","net","localSocket","sockLog","forwardOut","s","rawAddress","address","hostname","dns","privateKey","ssh","localPath","remotePath","sftp","newErr","callback","mode","items","item","itemPath","error","path","list","a","b","directoryPath","message","buffer","stats","readFile","localDir","remoteDir","files","directories","currentPath","createNext","index","directory","localHost","localPort","remoteHost","remotePort","minioDirName","supervisordDirName","supervisordSubDirName","remoteHome","binariesDir","platformaBaseDir","platformaDir","platformaBin","platformaFreePortBin","minioDir","minioBin","supervisorBinDir","supervisorBin","supervisorConf","connectionInfo","platformaCliLogs","supervisorCtlStart","sshClient","supervisorExec","supervisorStop","isAllAlive","status","shouldUseMinio","isSupervisordRunning","supervisorStatus","platforma","isProgramRunning","minio","generateSupervisordConfig","supervisorRemotePort","remoteWorkDir","platformaConfigPath","plPath","password","freePort","generateSupervisordConfigWithMinio","minioStorageDir","minioEnvs","minioPath","minioEnvStr","key","value","supervisorCmd","plpath.supervisorBin","plpath.supervisorConf","cmd","output","programName","str","line","name","PortPair","z","SshPlPorts","ConnectionInfo","newConnectionInfo","plUser","plPassword","useGlobalAccess","plVersion","minioIsUsed","parseConnectionInfo","content","stringifyConnectionInfo","minRequiredGlibcVersion","SshPl","username","logs","plpath.platformaCliLogs","supervisorCtlShutdown","plpath.workDir","options","onProgress","defaultSshPlConfig","stateCopy","plpath.connectionInfo","supervisorConfig","generateSshPlConfigs","glibcVersion","getGlibcVersion","downloadRes","sameGA","samePlVersion","localWorkdir","plBinary","supervisor","plpath.supervisordDirName","plpath.minioBin","plpath.minioDirName","plpath.platformaBin","plpath.binariesDir","downloadBinaryResult","attempts","i","untarResult","checkPathSupervisor","checkPathMinio","plpath.minioDir","checkPathPlatforma","interval","count","shouldStart","getFreePort","res","srv","_","freePortBin","plpath.platformaFreePortBin","arr","home","parseGlibcVersion","versionMatch"],"mappings":"m9BAWgB,SAAAA,GAAWC,EAAkBC,EAAoC,CAC/E,OAAAD,EAAO,KAAK;AAAA,OACP,KAAK,UAAU,CAACC,EAAK,IAAK,GAAGA,EAAK,IAAI,CAAC,CAAC;AAAA,MACzCA,EAAK,KAAK,GAAG,EAAE,EAEnBD,EAAO,KAAK,0BAA0B,EAC/BE,GAAAA,MAAMD,EAAK,IAAKA,EAAK,KAAMA,EAAK,IAAI,CAC7C,CAGA,eAAsBE,EAAeC,EAAa,CAC5C,GAAA,CACM,eAAA,KAAKA,EAAK,CAAC,EACZ,QACI,CACJ,MAAA,EAAA,CAEX,CAEO,SAASC,EAAYD,EAAa,CAChC,OAAA,QAAQ,KAAKA,EAAK,QAAQ,CACnC,CAEsB,eAAAE,EAAmBF,EAAaG,EAAe,CAEnE,IAAIC,EAAQ,EACL,KAAA,MAAML,EAAeC,CAAG,GAG7B,GAFA,MAAMK,EAAAA,MAAM,GAAO,EACVD,GAAA,IACLA,EAAQD,EACV,MAAM,IAAI,MAAM,qCAAqCA,CAAK,MAAM,CAGtE,CC5CO,MAAMG,GAAO,CAAC,QAAS,QAAS,SAAS,EAIzC,SAASC,EAAMC,EAAwB,CACpC,OAAAA,EAAO,YAAe,EAAA,CAC5B,IAAK,SACI,MAAA,QACT,IAAK,QACI,MAAA,QACT,IAAK,QACI,MAAA,UACT,QACE,MAAM,IAAI,MACR,qBAAqBA,CAAM,oFACzB,KAAK,UAAUF,EAAI,CACvB,CAAA,CAEN,CAEa,MAAAG,GAAS,CAAC,QAAS,OAAO,EAIhC,SAASC,EAAQC,EAAwB,CAC9C,OAAQA,EAAM,CACZ,IAAK,UACL,IAAK,aACL,IAAK,QACI,MAAA,QAET,IAAK,SACL,IAAK,MACI,MAAA,QAET,QACE,MAAM,IAAI,MACR,2BAA2BA,CAAI,6FAC7B,KAAK,UAAUF,EAAM,CACzB,CAAA,CAEN,CC5BA,MAAMG,GAAM,qCAGNC,GAAQ,0CAYd,eAAsBC,GACpBlB,EACAmB,EACAC,EACAC,EACAN,EACAO,EAC+B,CACzB,MAAArB,EAAOsB,EAAoBH,EAAcC,EAASF,EAASL,EAAQC,CAAI,EAAGJ,EAAMW,CAAQ,CAAC,EACzF,CAAE,WAAAE,EAAY,wBAAAC,EAAyB,YAAAC,CAAgB,EAAAzB,EAEzD,GAAA,CACI,MAAA0B,EAAgB3B,EAAQwB,EAAYE,CAAW,EACrDzB,EAAK,kBAAoBuB,OACd,CACL,MAAAG,EAAgB3B,EAAQyB,EAAyBC,CAAW,EAClEzB,EAAK,kBAAoBwB,CAAA,CAGpB,OAAAxB,CACT,CAEA,eAAsB2B,GACpB5B,EACAmB,EACAC,EACAS,EACAd,EACAO,EAC+B,CACzB,MAAArB,EAAOsB,EAAoBH,EAAcS,EAAaV,EAASL,EAAQC,CAAI,EAAGJ,EAAMW,CAAQ,CAAC,EAC7F,CAAE,WAAAE,EAAY,wBAAAC,EAAyB,YAAAC,EAAa,YAAAI,EAAa,aAAAC,GAAiB9B,EAEpF,GAAA,CACI,MAAA0B,EAAgB3B,EAAQwB,EAAYE,CAAW,EACrDzB,EAAK,kBAAoBuB,OACd,CACL,MAAAG,EAAgB3B,EAAQyB,EAAyBC,CAAW,EAClEzB,EAAK,kBAAoBwB,CAAA,CAG3B,aAAMO,GAAehC,EAAQ0B,EAAaI,EAAaC,CAAY,EAE5D9B,CACT,CAEA,SAASsB,EACPH,EACAS,EACAV,EACAJ,EACAkB,EACsB,CACtB,MAAMC,EAAW,GAAGL,CAAW,IAAId,CAAI,GACjCe,EAAcK,GAAgBF,CAAE,EAEhCG,EAAkB,GAAGF,CAAQ,IAAIJ,CAAW,GAC5CN,EAAa,GAAGR,EAAG,IAAII,CAAY,IAAIa,CAAE,IAAIG,CAAe,GAC5DX,EAA0B,GAAGR,EAAK,IAAIG,CAAY,IAAIa,CAAE,IAAIG,CAAe,GAC3EV,EAAcW,EAAM,KAAKlB,EAASiB,CAAe,EAEjDL,EAAeM,EAAM,KAAKlB,EAASe,CAAQ,EAE1C,MAAA,CACL,WAAAV,EACA,wBAAAC,EACA,YAAAC,EACA,YAAAI,EACA,aAAAC,EACA,SAAAG,CACF,CACF,CAesB,eAAAP,EACpB3B,EAAkBwB,EAAoBc,EACf,CACvB,MAAMC,EAAsB,CAAC,EAC7BA,EAAM,WAAaD,EAEf,GAAA,CAEF,GADMC,EAAA,YAAc,MAAMC,EAAA,WAAWF,CAAc,EAC/CC,EAAM,YACD,OAAAvC,EAAA,KAAK,gDAAgDsC,CAAc,kBAAkB,EACrFC,EAGH,MAAAE,EAAI,MAAMJ,EAAM,QAAQC,CAAc,EAAG,CAAE,UAAW,GAAM,EAClEC,EAAM,eAAiB,GAEvBvC,EAAO,KAAK;AAAA,SAAgCwB,CAAU;AAAA,YAAec,CAAc,EAAE,EAErF,KAAM,CAAE,KAAAI,EAAM,WAAAC,CAAe,EAAA,MAAMC,GAAAA,QAAQpB,CAAU,EAErD,GADAe,EAAM,WAAaI,EACfA,GAAc,IAAK,CAEf,MAAAE,EAAW,MAAMC,GAAA,KAAKJ,CAAI,EAC1B,MAAAH,EAAA,SAAW,+BAA+BI,CAAU,eAAeE,EAAS,MAAM,EAAG,GAAI,CAAC,GACzF7C,EAAA,MAAMuC,EAAM,QAAQ,EACrB,IAAI,MAAMA,EAAM,QAAQ,CAAA,CAIhC,OAAAA,EAAM,QAAUD,EAAiB,OAEjC,MAAMS,WAAS,MAAML,CAAI,EAAE,OAAOM,EAAAA,SAAS,MAAMC,EAAG,kBAAkBV,EAAM,OAAO,CAAC,CAAC,EACrFA,EAAM,SAAW,GACjBA,EAAM,WAAa,MAAMC,aAAWD,EAAM,OAAO,EAGjD,MAAME,EAAI,OAAOF,EAAM,QAASD,CAAc,EAC9CC,EAAM,QAAU,GACVA,EAAA,WAAa,MAAMC,EAAA,WAAWF,CAAc,EAE3CC,QACAW,EAAY,CACb,MAAAC,EAAM,oBAAoB,KAAK,UAAUD,CAAC,CAAC,YAAY,KAAK,UAAUX,CAAK,CAAC,GAClF,MAAAvC,EAAO,MAAMmD,CAAG,EACV,IAAI,MAAMA,CAAG,CAAA,CAEvB,CAGA,MAAMC,GAAiB,MAEvB,eAAsBpB,GACpBhC,EACA0B,EACAI,EACAuB,EACA,CAKA,GAJArD,EAAO,KAAK,uBAAuB,EAC5BA,EAAA,KAAK,oBAAoB0B,CAAW,GAAG,EACvC1B,EAAA,KAAK,kBAAkBqD,CAAS,GAAG,EAEtC,CAAE,MAAMb,aAAWd,CAAW,EAAI,CAC9B,MAAAyB,EAAM,kDAAkDzB,CAAW,IACzE,MAAA1B,EAAO,MAAMmD,CAAG,EACV,IAAI,MAAMA,CAAG,CAAA,CAGrB,MAAMG,EAAiBjB,EAAM,KAAKgB,EAAWD,EAAc,EAEvD,GAAA,MAAMZ,EAAAA,WAAWc,CAAc,EAAG,CAC7BtD,EAAA,KAAK,+CAA+CqD,CAAS,UAAU,EAC9E,MAAA,CAeF,OAZI,MAAMb,EAAAA,WAAWa,CAAS,IACrBrD,EAAA,KAAK,oDAAoDqD,CAAS,GAAG,EAC5E,MAAMZ,EAAI,GAAGY,EAAW,CAAE,UAAW,GAAM,GAGtCrD,EAAA,KAAK,0BAA0BqD,CAAS,GAAG,EAClD,MAAMZ,EAAI,MAAMY,EAAW,CAAE,UAAW,GAAM,EAEvCrD,EAAA,KACL;AAAA,eAAsD0B,CAAW;AAAA,gBAAmB2B,CAAS,EAC/F,EAEQvB,EAAa,CACnB,IAAK,MACH,MAAMyB,GAAI,EAAE,CACV,KAAM7B,EACN,IAAK2B,EACL,KAAM,EAAA,CACP,EACD,MAEF,IAAK,MACG,MAAAG,GAAW9B,EAAa2B,CAAS,EACvC,MAEF,QACEI,EAAAA,YAAY3B,CAAW,CAAA,CAIrB,MAAAW,EAAI,UAAUa,EAAgB,IAAI,EAExCtD,EAAO,KAAK,oBAAoB,CAClC,CAIA,MAAMmC,GAA+C,CACnD,MAAO,MACP,MAAO,MACP,QAAS,KACX,ECnOO,SAASuB,GAA8B,CACrC,MAAA,QACT,CCkBO,SAASC,IAAmD,CACjE,MAAO,CAAE,KAAM,WAAY,QAASD,GAAsB,CAC5D,CAEsB,eAAAE,GACpB5D,EACA6D,EACAC,EACiB,CACjB,OAAQA,EAAI,KAAM,CAChB,IAAK,WAEH,MAAMC,EAAM,MAAMnC,GAAe5B,EAAQ6D,EAAa,KAAM,MAAMC,EAAI,OAAO,GAAI7B,EAAG,KAAA,EAAQA,EAAG,UAAU,EAClG,OAAAI,EAAM,KAAK0B,EAAI,SAAU,WAAYC,GAAerD,EAAMsB,EAAG,SAAA,CAAU,CAAC,CAAC,EAElF,IAAK,QACH,OAAO6B,EAAI,KAEb,QACEL,EAAAA,YAAYK,CAAG,CAAA,CAErB,CAEO,MAAME,GAAyC,CACpD,MAAO,YACP,MAAO,YACP,QAAS,eACX,EC7CO,SAASC,EAAQC,EAAa,CAC5B,OAAA7B,EAAM,KAAK6B,EAAK,QAAQ,CACjC,CAEA,eAAsBC,GAAQC,EAA+C,CAC3E,GAAI,CAAE,MAAM5B,aAAW4B,CAAQ,EACtB,OAGT,MAAMtB,EAAO,MAAMG,EAAG,SAASmB,CAAQ,EAEhC,OAAA,OAAOtB,EAAK,UAAU,CAC/B,CAEsB,eAAAuB,GAASD,EAAkBhE,EAAa,CAC5D,MAAM6C,EAAG,UAAUmB,EAAU,KAAK,UAAUhE,CAAG,CAAC,CAClD,CCdO,SAASkE,IAAkB,CAChC,MAAO,CAAC,CACV,CAEgB,SAAAC,GAAMC,EAAUC,EAAWC,EAAQ,CACjD,OAAAF,EAAEC,CAAC,EAAIC,EACAA,CACT,CAIsB,eAAAC,EACpB3E,EACA4E,EACY,CACZ,MAAMJ,EAAIF,GAAS,EACf,GAAA,CAEK,OADQ,MAAMM,EAAG,CAACH,EAAWC,IAAWH,GAAMC,EAAGC,EAAGC,CAAC,EAAGF,CAAC,QAEzDtB,EAAQ,CACR,MAAAlD,EAAA,MAAM,SAASkD,CAAC,yCAAyC,KAAK,UAAUsB,CAAC,CAAC,EAAE,EAC7EtB,CAAA,CAEV,CCRO,MAAM2B,EAAkB,oBAOxB,MAAMC,CAAQ,CAOnB,YACmB9E,EACA+E,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EACjB,CAfMC,EAAA,iBACDA,EAAA,YACCA,EAAA,aAAgB,GAChBA,EAAA,sBAAwB,CAAC,GACzBA,EAAA,kBAAa,IAGF,KAAA,OAAAtF,EACA,KAAA,WAAA+E,EACA,KAAA,aAAAC,EACA,KAAA,oBAAAC,EACA,KAAA,QAAAC,EACA,KAAA,QAAAC,EACA,KAAA,gBAAAC,EACA,KAAA,sBAAAC,CAAA,CAGnB,MAAM,OAAQ,CACZ,MAAMV,EAAU,KAAK,OAAQ,MAAOJ,EAAOC,IAAM,CAC/C,KAAK,WAAa,GAClB,MAAMe,EAAWxF,GAAW,KAAK,OAAQ,KAAK,YAAY,EACjDwF,EAAA,GAAG,QAAUrC,GAAW,CAC/B,KAAK,OAAO,MACV,UAAUA,CAAC,6CAA6C,KAAK,UAAU,KAAK,UAAA,CAAW,CAAC,EAC1F,EAGI,KAAK,UAAY,QAAgB,KAAK,QAAQ,IAAI,EAClD,KAAK,kBAAoB,QAAgB,KAAK,gBAAgB,IAAI,EAClE,KAAK,wBAA0B,QAAa,CAAC,KAAK,YAC/C,KAAK,sBAAsB,IAAI,CAAA,CACvC,EACQqC,EAAA,GAAG,QAAS,IAAM,CACpB,KAAA,OAAO,KAAK,uCAAuC,KAAK,UAAU,KAAK,WAAW,CAAC,EAAE,EAGtF,KAAK,UAAY,QAAgB,KAAK,QAAQ,IAAI,EAClD,KAAK,kBAAoB,QAAgB,KAAK,gBAAgB,IAAI,EAClE,KAAK,wBAA0B,QAAa,CAAC,KAAK,YAC/C,KAAK,sBAAsB,IAAI,CAAA,CACvC,EAEDhB,EAAM,UAAW,EAAI,EAErB,MAAMiB,EAAUjB,EAAM,UAAWN,EAAQ,KAAK,UAAU,CAAC,EACzDM,EAAM,MAAOkB,EAAAA,SAASF,EAAS,GAAG,CAAC,EAC7BhB,EAAA,aAAc,MAAMF,GAASmB,EAASC,EAAAA,SAASF,EAAS,GAAG,CAAC,CAAC,EAE9D,KAAA,QACL,KAAK,SAAWA,EAChB,KAAK,IAAMA,EAAS,IACpB,KAAK,eAAiBf,CAAA,CACvB,CAAA,CAGH,MAAO,CAEL,KAAK,WAAa,GACNnE,EAAAoF,EAAA,SAAS,KAAK,GAAG,CAAC,CAAA,CAGhC,MAAM,aAAc,CAClB,MAAMnF,EAAmBmF,EAAA,SAAS,KAAK,GAAG,EAAG,IAAK,CAAA,CAGpD,SAAU,CACR,OAAO,KAAK,UAAA,CAGd,MAAM,SAA4B,CAChC,OAAO,MAAMtF,EAAesF,EAAAA,SAAS,KAAK,GAAG,CAAC,CAAA,CAGhD,WAAY,CACH,MAAA,CACL,eAAgB,KAAK,eACrB,MAAO,KAAK,MACZ,IAAK,KAAK,IACV,WAAY,KAAK,WACjB,oBAAqB,KAAK,oBAC1B,WAAY,KAAK,UACnB,CAAA,CAEJ,CA8BsB,eAAAC,GAAmB1F,EAAkB2F,EAAwC,CAK3F,MAAAC,EAAS,KAAK,IAAI3D,GAAG,OAAO,OAAS,EAAG,CAAC,EACzC8B,EAAM8B,EAAgBF,EAAMC,CAAM,EAExC,OAAO,MAAMjB,EAAU3E,EAAQ,MAAOuE,EAAOC,IAAM,CACjDD,EAAM,eAAgB,CAAE,GAAGR,EAAK,OAAQ,YAAa,EAErD,MAAM+B,EAAUzD,EAAM,QAAQ0B,EAAI,UAAU,EAExCA,EAAI,UACNQ,EAAM,WAAY,MAAMwB,GAA6B/F,EAAQ8F,CAAO,CAAC,EAGvE,MAAME,EAAa3D,EAAM,KAAKyD,EAASjB,CAAe,EAE/C7E,EAAA,KAAK,0BAA0BgG,CAAU,MAAM,EACtD,MAAMvD,EAAI,UAAUuD,EAAYjC,EAAI,MAAM,EAE1C,MAAMkC,EAAY5D,EAAM,KAAKyD,EAAS,UAAU,EAC1CI,EAAiB,MAAMtC,GAAyB5D,EAAQiG,EAAWlC,EAAI,QAAQ,EAC/EoC,EAAa5B,EAAM,aAAclC,EAAM,KAAK,WAAY6D,CAAc,CAAC,EAEvEE,EAAcC,EAAaF,EAAYH,EAAYjC,EAAK+B,EAAS,QAAQ,GAAG,EAClFvB,EAAM,cAAe,CACnB,IAAK6B,EAAY,IACjB,KAAMA,EAAY,KAClB,IAAKA,EAAY,KAAK,GAAA,CACvB,EAED,MAAME,EAAK,IAAIxB,EACb9E,EACA+D,EAAI,WACJqC,EACA5B,EACAT,EAAI,QACJA,EAAI,QACJA,EAAI,gBACJA,EAAI,qBACN,EACA,aAAMuC,EAAG,MAAM,EAERA,CAAA,CACR,CACH,CAIA,eAAeP,GACb/F,EACA+E,EAC8B,CAC9B,OAAO,MAAMJ,EAAU3E,EAAQ,MAAOuE,EAAOC,IAAM,CACjD,MAAM+B,EAAOhC,EAAM,cAAeN,EAAQc,CAAU,CAAC,EAE/CyB,EAASjC,EAAM,MAAO,MAAMJ,GAAQoC,CAAI,CAAC,EACzCE,EAAQlC,EAAM,WAAY,MAAMpE,EAAeqG,CAAM,CAAC,EAExD,OAAAA,IAAW,QAAaC,IACpBlC,EAAA,UAAWlE,EAAYmG,CAAM,CAAC,EACpCjC,EAAM,cAAe,MAAMjE,EAAmBkG,EAAQ,GAAM,CAAC,GAGxDhC,CAAA,CACR,CACH,CAIgB,SAAAqB,EAAgB9B,EAAqB6B,EAAoC,OACvF,MAAMc,EAIF,CACF,SAAU/C,GAAyB,EACnC,aAAc,CACZ,IAAK,CACH,WAAY,OAAOiC,CAAM,CAAA,CAE7B,EACA,SAAU,EACZ,EAMA,IAJIe,EAAA5C,EAAI,eAAJ,MAAA4C,EAAkB,MACbD,EAAA,aAAa,IAAM,CAAE,GAAGA,EAAO,aAAa,IAAK,GAAG3C,EAAI,aAAa,GAAI,GAG9EA,EAAI,aAAc,CACpB,MAAM6C,EAAa,CAAE,GAAG7C,EAAI,YAAa,EACzC,OAAO6C,EAAW,IAClBF,EAAO,aAAe,CAAE,GAAGA,EAAO,aAAc,GAAGE,CAAW,CAAA,CAG1D,MAAAC,EAAkB,CAAE,GAAG9C,CAAI,EACjC,cAAO8C,EAAgB,aAEhB,CAAE,GAAGH,EAAQ,GAAGG,CAAgB,CACzC,CAIO,SAASR,EACdF,EACAH,EACAjC,EACA+B,EACAgB,EACgB,OAChB,MAAMJ,EAAyB,CAC7B,IAAKP,EACL,KAAM,CAAC,WAAYH,CAAU,EAC7B,KAAM,CACJ,IAAK,CAAE,GAAGc,CAAW,EACrB,IAAKhB,EACL,MAAO,CAAC,OAAQ,SAAU,SAAS,EACnC,YAAa,EAAA,CAEjB,GAEIa,EAAA5C,EAAI,eAAJ,MAAA4C,EAAkB,MACbD,EAAA,KAAK,IAAM,CAAE,GAAGA,EAAO,KAAK,IAAK,GAAG3C,EAAI,aAAa,GAAI,GAGlE,MAAM6C,EAAa,CAAE,GAAG7C,EAAI,YAAa,EACzC,cAAO6C,EAAW,IAClBF,EAAO,KAAO,CAAE,GAAGA,EAAO,KAAM,GAAGE,CAAW,EAEvCF,CACT,CCtQA,MAAMK,GAA+B,CACnC,kBAAmB,IACnB,kBAAmB,EACrB,EASO,MAAMC,CAAU,CAKrB,YACmBhH,EACAiH,EACjB,CAPM3B,EAAA,eACDA,EAAA,gBACCA,EAAA,wBAAiC,CAAC,GAGvB,KAAA,OAAAtF,EACA,KAAA,OAAAiH,CAAA,CAQnB,aAAoB,KAAKjH,EAAkBkH,EAA2C,CACpF,MAAMC,EAAe,CACnB,GAAGJ,GACH,GAAGG,CACL,EAEMD,EAAS,IAAID,EAAUhH,EAAQ,IAAIoH,QAAQ,EAC3C,aAAAH,EAAO,QAAQE,CAAY,EAE1BF,CAAA,CAGF,qBAAsB,CAC3B,OAAO,KAAK,gBAAA,CAGP,iBAAkB,SACvB,MAAO,IAAGN,EAAA,KAAK,SAAL,YAAAA,EAAa,IAAI,KAAIU,EAAA,KAAK,SAAL,YAAAA,EAAa,IAAI,EAAA,CAG3C,aAAc,OACnB,OAAOV,EAAA,KAAK,SAAL,YAAAA,EAAa,QAAA,CAQtB,MAAa,QAAQO,EAAuB,CAC1C,YAAK,OAASA,EACP,MAAMI,GAAQ,KAAK,OAAQJ,CAA0B,CAAA,CAQ9D,MAAa,KAAKK,EAAyC,CACzD,OAAO,IAAI,QAAQ,CAACC,EAASC,IAAW,CACtC,KAAK,OAAO,KAAKF,EAAS,CAACG,EAAUC,IAA0B,CAC7D,GAAID,EACK,OAAAD,EAAO,IAAI,MAAM,aAAaF,CAAO,KAAKG,CAAG,EAAE,CAAC,EAGzD,IAAIE,EAAS,GACTC,EAAS,GAENF,EAAA,GAAG,QAAUG,GAAiB,CAC/BA,IAAS,EACHN,EAAA,CAAE,OAAAI,EAAQ,OAAAC,EAAQ,EAEnBJ,EAAA,IAAI,MAAM,WAAWF,CAAO,qBAAqBO,CAAI,aAAaF,CAAM,aAAaC,CAAM,EAAE,CAAC,CAExG,CAAA,EAAE,GAAG,OAASE,GAAsB,CACnCH,GAAUG,EAAK,SAAS,CACzB,CAAA,EAAE,OAAO,GAAG,OAASA,GAAsB,CAC1CF,GAAUE,EAAK,SAAS,CAAA,CACzB,CAAA,CACF,CAAA,CACF,CAAA,CASH,aAAoB,aAAaC,EAAcC,EAA6C,CACnF,OAAA,IAAI,QAAST,GAAY,CAC9B,IAAII,EAAS,GACP,MAAAM,EAAO,IAAId,SAEZc,EAAA,GAAG,QAAS,IAAM,CACrBA,EAAK,IAAI,EACH,MAAAC,EAAQ,KAAK,mBAAmBP,CAAM,EAC5CJ,EAAQW,EAAM,SAAW,EAAI,CAAC,YAAa,UAAU,EAAIA,CAA6B,CAAA,CACvF,EAEID,EAAA,GAAG,QAAS,IAAM,CACrBA,EAAK,IAAI,EACDV,EAAA,CAAC,YAAa,UAAU,CAAC,CAAA,CAClC,EAEDU,EAAK,QAAQ,CACX,KAAAF,EACA,KAAAC,EACA,SAAc,IAAA,KAAO,EAAA,QAAA,EAAU,SAAS,EACxC,MAAQP,GAAQ,CACdE,GAAU,GAAGF,CAAG;AAAA,CAAA,CAClB,CACD,CAAA,CACF,CAAA,CAQH,OAAe,mBAAmBU,EAAuB,CACjD,MAAAC,EAAQD,EAAI,MAAM,6CAA6C,EACrE,OAAOC,GAASA,EAAM,CAAC,EAAIA,EAAM,CAAC,EAAE,MAAM,GAAG,EAAE,IAAKC,GAAWA,EAAO,KAAM,CAAA,EAAI,CAAC,CAAA,CAUnF,MAAa,YAAYC,EAAsErB,EAAyD,CACtJ,MAAMkB,EAAM,eAAeG,EAAM,SAAS,IAAIA,EAAM,UAAU,OAAOC,EAAY,YAAA,CAAC,EAAE,SAAS,KAAK,CAAC,GACnGtB,EAASA,GAAU,KAAK,OAKxB,MAAMuB,EAAmB,IAAIC,mBAAkBC,GACtC,IAAI,QAAgB,CAACnB,EAASC,IAAW,CACxC,MAAAR,EAAS,IAAIG,SAEZH,EAAA,GAAG,QAAS,IAAM,CACvB,KAAK,OAAO,KAAK,GAAGmB,CAAG,eAAe,EACtCZ,EAAQP,CAAM,CAAA,CACf,EAEMA,EAAA,GAAG,QAAUS,GAAQ,CAC1B,KAAK,OAAO,KAAK,GAAGU,CAAG,kBAAkBV,CAAG,EAAE,EAC9CiB,EAAE,MAAM,EACRlB,EAAOC,CAAG,CAAA,CACX,EAEMT,EAAA,GAAG,QAAS,IAAM,CACvB,KAAK,OAAO,KAAK,GAAGmB,CAAG,gBAAgB,EACvCO,EAAE,MAAM,CAAA,CACT,EAED1B,EAAO,QAAQC,CAAO,CAAA,CACvB,CACF,EAED,aAAMuB,EAAiB,OAAO,EAEvB,IAAI,QAAQ,CAACjB,EAASC,IAAW,CAChC,MAAAmB,EAASC,EAAI,aAAa,CAAE,eAAgB,EAAK,EAAG,MAAOC,GAAgB,CACzE,MAAAC,EAAU,GAAGX,CAAG,SAASI,cAAY,CAAC,EAAE,SAAS,KAAK,CAAC,GAEzD,IAAAN,EACA,GAAA,CACKA,EAAA,MAAMO,EAAiB,OAAO,QAC9BvF,EAAY,CACnB,KAAK,OAAO,KAAK,GAAG6F,CAAO,4BAA4B7F,CAAC,EAAE,EAC1D4F,EAAY,IAAI,EAChB,MAAA,CAQDZ,EAAa,WAAW,EAAI,EAC7BY,EAAY,WAAW,EAAI,EAEvB,IAAAnB,EACA,GAAA,CACOA,EAAA,MAAMqB,GAAW,KAAK,OAAQd,EAAM,YAAa,EAAG,YAAaK,EAAM,UAAU,QACnFrF,EAAY,CACnB,KAAK,OAAO,MAAM,GAAG6F,CAAO,oBAAoB7F,CAAC,EAAE,EACnD4F,EAAY,IAAI,EAChB,MAAA,CAGFA,EAAY,KAAKnB,CAAM,EACvBA,EAAO,KAAKmB,CAAW,EACvBA,EAAY,OAAO,EAGZnB,EAAA,GAAG,QAAUD,GAAiB,CACnC,KAAK,OAAO,MAAM,GAAGqB,CAAO,kBAAkBrB,CAAG,EAAE,EACnDoB,EAAY,IAAI,EAChBnB,EAAO,IAAI,CAAA,CACZ,EACMA,EAAA,GAAG,QAAS,IAAM,CAEvBmB,EAAY,IAAI,EAChBnB,EAAO,IAAI,CAAA,CACZ,EACWmB,EAAA,GAAG,QAAS,IAAM,CAC5B,KAAK,OAAO,KAAK,GAAGC,CAAO,sBAAsB,EACjDD,EAAY,IAAI,EAChBnB,EAAO,IAAI,CAAA,CACZ,CAAA,CACF,EAEDiB,EAAO,OAAOL,EAAM,UAAW,YAAa,IAAM,CAChD,KAAK,OAAO,KAAK,GAAGH,CAAG,4BAA4B,EAC9C,KAAA,iBAAiB,KAAKQ,CAAM,EACzBpB,EAAA,CAAE,OAAAoB,EAAQ,CAAA,CACnB,EAEMA,EAAA,GAAG,QAAUlB,GAAQ,CAC1BkB,EAAO,MAAM,EACNnB,EAAA,IAAI,MAAM,GAAGW,CAAG,mBAAmB,KAAK,UAAUV,CAAG,CAAC,EAAE,CAAC,CAAA,CACjE,EAEMkB,EAAA,GAAG,QAAS,IAAM,CAClB,KAAA,OAAO,KAAK,GAAGR,CAAG,mBAAmB,KAAK,UAAUG,CAAK,CAAC,EAAE,EACjE,KAAK,iBAAmB,KAAK,iBAAiB,OAAQU,GAAMA,IAAML,CAAM,CAAA,CACzE,CAAA,CACF,CAAA,CAGI,qBAA4B,CAC5B,KAAA,OAAO,KAAK,sCAAsC,EAClD,KAAA,iBAAiB,QAASA,GAAW,CAClC,MAAAM,EAAaN,EAAO,QAAQ,EAC9B,GAAAM,GAAc,OAAOA,GAAe,SAAU,CAChD,MAAMC,EAA2BD,EAC5B,KAAA,OAAO,KAAK,yCAAyCC,EAAQ,OAAO,IAAIA,EAAQ,IAAI,EAAE,CAAA,CAG7FP,EAAO,MAAM,CAAA,CACd,EACD,KAAK,iBAAmB,CAAC,CAAA,CAQ3B,aAAoB,sBAAsBQ,EAAoC,CACrE,OAAA,IAAI,QAAS5B,GAAY,CAC1B6B,GAAA,OAAOD,EAAW1B,GAAQ,CAC5BF,EAAQ,CAACE,CAAG,CAAA,CACb,CAAA,CACF,CAAA,CAQH,aAAoB,2BAA2B4B,EAAsC,CACnF,OAAO,IAAI,QAAQ,CAAC9B,EAASC,IAAW,CAClC,GAAA,CAEF,OADmB8B,EAAI,MAAM,SAASD,CAAU,YACtB,OACxB9B,EAAQ,EAAI,EAEPA,EAAQ,EAAK,QACbE,EAAc,CACrB,QAAQ,IAAI,0BAA0B,EACtCD,EAAO,IAAI,MAAM,uCAAuCC,CAAG,EAAE,CAAC,CAAA,CAChE,CACD,CAAA,CAUH,MAAa,WAAW8B,EAAmBC,EAAsC,CAC/E,OAAO,MAAM,KAAK,SAAS,MAAOC,GACzB,IAAI,QAAQ,CAAClC,EAASC,IAAW,CACtCiC,EAAK,QAAQF,EAAWC,EAAa/B,GAAQ,CAC3C,GAAIA,EAAK,CACP,MAAMiC,EAAS,IAAI,MACjB,wBAAwBjC,CAAG,gBAAgB8B,CAAS,iBAAiBC,CAAU,EAAE,EACnF,OAAOhC,EAAOkC,CAAM,CAAA,CAEtBnC,EAAQ,EAAI,CAAA,CACb,CAAA,CACF,CACF,CAAA,CAGH,MAAa,SAAYoC,EAAyD,CAChF,OAAO,IAAI,QAAQ,CAACpC,EAASC,IAAW,CACtC,KAAK,OAAO,KAAK,CAACC,EAAKgC,IAAS,CAC9B,GAAIhC,EACF,OAAOD,EAAO,IAAI,MAAM,2BAA2BC,CAAG,EAAE,CAAC,EAG3DkC,EAASF,CAAI,EACV,KAAKlC,CAAO,EACZ,MAAOE,GAAQ,CACdD,EAAO,IAAI,MAAM,8BAA8BC,CAAG,EAAE,CAAC,CAAA,CACtD,EACA,QAAQ,IAAM,CACbgC,GAAA,MAAAA,EAAM,KAAI,CACX,CAAA,CACJ,CAAA,CACF,CAAA,CAGH,MAAa,qBAAqBD,EAAoB1B,EAAuB8B,EAAe,IAAO,CAC1F,OAAA,KAAK,SAAS,MAAOH,GACnB,KAAK,UAAUA,EAAMD,EAAY1B,EAAM8B,CAAI,CACnD,CAAA,CAGH,MAAa,mBAAmBH,EAAmBD,EAAoB1B,EAAsB,CAAE,MAAO,CAAI,EAAA,YAAa,CAAA,GAA8B,CACnJ,OAAO,IAAI,QAAQ,CAACP,EAASC,IAAW,CACtCiC,EAAK,QAAQD,EAAY,MAAO/B,EAAKoC,IAAU,CAC7C,GAAIpC,EACF,OAAOD,EAAOC,CAAG,EAGnB,UAAWqC,KAAQD,EAAO,CACxB,MAAME,EAAW,GAAGP,CAAU,IAAIM,EAAK,QAAQ,GAC3C,GAAAA,EAAK,MAAM,cAAe,CACvBhC,EAAA,YAAY,KAAKiC,CAAQ,EAC1B,GAAA,CACF,MAAM,KAAK,mBAAmBN,EAAMM,EAAUjC,CAAI,QAC3CkC,EAAO,CACP,OAAAxC,EAAOwC,aAAiB,MAAQA,EAAQ,IAAI,MAAM,OAAOA,CAAK,CAAC,CAAC,CAAA,CACzE,MAEKlC,EAAA,MAAM,KAAKiC,CAAQ,CAC1B,CAEFxC,EAAQO,CAAI,CAAA,CACb,CAAA,CACF,CAAA,CAGI,MAAM2B,EAAmBQ,EAAc,CAC5C,OAAO,IAAI,QAAQ,CAAC1C,EAASC,IAAW,CACjCiC,EAAA,MAAMQ,EAAOxC,GAAQA,EAAMD,EAAOC,CAAG,EAAIF,EAAQ,EAAI,CAAC,CAAA,CAC5D,CAAA,CAGI,OAAOkC,EAAmBQ,EAAc,CAC7C,OAAO,IAAI,QAAQ,CAAC1C,EAASC,IAAW,CACjCiC,EAAA,OAAOQ,EAAOxC,GAAQA,EAAMD,EAAOC,CAAG,EAAIF,EAAQ,EAAI,CAAC,CAAA,CAC7D,CAAA,CAGH,MAAa,aAAa0C,EAAc,CAC/B,OAAA,KAAK,SAAS,MAAOR,GAAS,CAC/B,GAAA,CACF,MAAMS,EAAO,MAAM,KAAK,mBAAmBT,EAAMQ,CAAI,EAChD,KAAA,OAAO,KAAK,gDAAgD,EACjE,KAAK,OAAO,KAAK,mCAAmCC,EAAK,KAAK,EAAE,EAChE,KAAK,OAAO,KAAK,yCAAyCA,EAAK,WAAW,EAAE,EAEjE,UAAA/F,KAAY+F,EAAK,MAC1B,KAAK,OAAO,KAAK,gCAAgC/F,CAAQ,EAAE,EACrD,MAAA,KAAK,OAAOsF,EAAMtF,CAAQ,EAG7B+F,EAAA,YAAY,KAAK,CAACC,EAAGC,IAAMA,EAAE,OAASD,EAAE,MAAM,EAExC,UAAAE,KAAiBH,EAAK,YAC/B,KAAK,OAAO,KAAK,2BAA2BG,CAAa,EAAE,EACrD,MAAA,KAAK,MAAMZ,EAAMY,CAAa,EAGhC,aAAA,KAAK,MAAMZ,EAAMQ,CAAI,EACpB,SACAhH,EAAY,CACd,KAAA,OAAO,MAAMA,CAAC,EACnB,MAAMqH,EAAUrH,aAAa,MAAQA,EAAE,QAAU,GACjD,MAAM,IAAI,MAAM,2BAA2BgH,CAAI,cAAcK,CAAO,EAAE,CAAA,CACxE,CACD,CAAA,CAGH,MAAa,SAASd,EAAqC,CAClD,OAAA,KAAK,SAAS,MAAOC,GACnB,IAAI,QAAQ,CAAClC,EAASC,IAAW,CACtCiC,EAAK,SAASD,EAAY,CAAC/B,EAAK8C,IAAW,CACzC,GAAI9C,EACF,OAAOD,EAAO,IAAI,MAAM,iBAAiBC,CAAG,EAAE,CAAC,EAEzCF,EAAAgD,EAAO,UAAU,CAAA,CAC1B,CAAA,CACF,CACF,CAAA,CAGH,MAAM,MAAMN,EAAcL,EAAc,CAC/B,OAAA,KAAK,SAAS,MAAOH,GACnB,IAAI,QAAQ,CAAClC,EAASC,IAAW,CACtCiC,EAAK,MAAMQ,EAAML,EAAOnC,GAClBA,EACKD,EAAO,IAAI,MAAM,cAAcC,CAAG,WAAWwC,CAAI,WAAWL,CAAI,EAAE,CAAC,EAErErC,EAAQ,MAAS,CACzB,CAAA,CACF,CACF,CAAA,CAGH,MAAM,gBAAgBiC,EAAoB,CACjC,OAAA,KAAK,SAAS,MAAOC,GACnB,IAAI,QAAQ,CAAClC,EAASC,IAAW,CACtCiC,EAAK,KAAKD,EAAY,CAAC/B,EAAwB+C,IAAU,CACvD,GAAI/C,EACG,OAAAA,GAAA,YAAAA,EAAsC,QAAS,EAC3CF,EAAQ,EAAK,EAEfC,EAAO,IAAI,MAAM,4BAA4BC,CAAG,EAAE,CAAC,EAEpDF,EAAAiD,EAAM,QAAQ,CAAA,CACvB,CAAA,CACF,CACF,CAAA,CAGH,MAAM,gBAAgBhB,EAAyF,CACtG,OAAA,KAAK,SAAS,MAAOC,GACnB,IAAI,QAAQ,CAAClC,EAASC,IAAW,CACtCiC,EAAK,KAAKD,EAAY,CAAC/B,EAAK+C,IAAU,CACpC,GAAI/C,EACG,OAAAA,EAAiC,OAAS,EACtCF,EAAQ,CAAE,OAAQ,GAAO,OAAQ,GAAO,YAAa,GAAO,EAE9DC,EAAO,IAAI,MAAM,wBAAwBC,CAAG,EAAE,CAAC,EAEhDF,EAAA,CACN,OAAQ,GACR,OAAQiD,EAAM,OAAO,EACrB,YAAaA,EAAM,YAAY,CAAA,CAChC,CAAA,CACF,CAAA,CACF,CACF,CAAA,CAGH,MAAc,UAAUf,EAAmBD,EAAoB1B,EAAuB8B,EAAe,IAAyB,CAC5H,OAAO,IAAI,QAAQ,CAACrC,EAASC,IAAW,CACtCiC,EAAK,UAAUD,EAAY1B,EAAM,CAAE,KAAA8B,CAAK,EAAInC,GAAQ,CAClD,GAAIA,EACK,OAAAD,EAAO,IAAI,MAAM,sBAAsBC,CAAG,iBAAiB+B,CAAU,EAAE,CAAC,EAEjFjC,EAAQ,EAAI,CAAA,CACb,CAAA,CACF,CAAA,CAGI,4BAA4BkC,EAAmBF,EAAmBC,EAAoBI,EAAe,IAAO,CACjH,OAAO,IAAI,QAAQ,CAACrC,EAASC,IAAW,CACjCiD,EAAS,SAAAlB,CAAS,EAAE,KAAK,MAAO9C,GAC5B,KAAK,UAAUgD,EAAMD,EAAY/C,EAAQmD,CAAI,EACjD,KAAK,IAAM,CACVrC,EAAQ,MAAS,CAAA,CAClB,EACA,MAAOE,GAAQ,CACR,MAAAvE,EAAM,gCAAgCuE,CAAG,GAC1C,KAAA,OAAO,MAAMvE,CAAG,EACdsE,EAAA,IAAI,MAAMtE,CAAG,CAAC,CAAA,CACtB,CACJ,CAAA,CACF,CAAA,CAGH,MAAc,kBAAkBuG,EAAmBiB,EAAkBC,EAAmBf,EAAe,IAAsB,CAC3H,OAAO,IAAI,QAAQ,CAACrC,EAASC,IAAW,CACtCxE,EAAG,QAAQ0H,EAAU,MAAOjD,EAAKmD,IAAU,CACzC,GAAInD,EACK,OAAAD,EAAO,IAAI,MAAM,wBAAwBC,CAAG,eAAeiD,CAAQ,gBAAgBC,CAAS,EAAE,CAAC,EAGpG,GAAA,CACI,MAAA,KAAK,wBAAwBlB,EAAMkB,CAAS,EAClD,UAAWrE,KAAQsE,EAAO,CACxB,MAAMrB,EAAYnH,EAAM,KAAKsI,EAAUpE,CAAI,EACrCkD,EAAa,GAAGmB,CAAS,IAAIrE,CAAI,GAEnCtD,EAAG,UAAUuG,CAAS,EAAE,cAC1B,MAAM,KAAK,kBAAkBE,EAAMF,EAAWC,EAAYI,CAAI,EAE9D,MAAM,KAAK,4BAA4BH,EAAMF,EAAWC,EAAYI,CAAI,CAC1E,CAGMrC,EAAA,QACDE,EAAK,CACN,MAAAvE,EAAM,gCAAgCuE,CAAG,GAC1C,KAAA,OAAO,MAAMvE,CAAG,EACdsE,EAAA,IAAI,MAAMtE,CAAG,CAAC,CAAA,CACvB,CACD,CAAA,CACF,CAAA,CASH,MAAa,gBAAgBwH,EAAkBC,EAAmBf,EAAe,IAAsB,CACrG,OAAO,IAAI,QAAQ,CAACrC,EAASC,IAAW,CACjC,KAAK,SAAS,MAAOiC,GAAsB,CAC1C,GAAA,CACF,MAAM,KAAK,kBAAkBA,EAAMiB,EAAUC,EAAWf,CAAI,EACpDrC,EAAA,QACDtE,EAAY,CACnBuE,EAAO,IAAI,MAAM,wBAAwBvE,CAAC,EAAE,CAAC,CAAA,CAC/C,CACD,CAAA,CACF,CAAA,CASK,wBAAwBwG,EAAmBD,EAAmC,CACpF,OAAO,IAAI,QAAQ,CAACjC,EAASC,IAAW,CAChC,MAAAqD,EAAcrB,EAAW,MAAM,GAAG,EACxC,IAAIsB,EAAc,GAEZ,MAAAC,EAAcC,GAAkB,CAChC,GAAAA,GAASH,EAAY,OACvB,OAAOtD,EAAQ,EAGFuD,GAAA,GAAGD,EAAYG,CAAK,CAAC,IAE/BvB,EAAA,KAAKqB,EAAcrD,GAAQ,CAC1BA,EACGgC,EAAA,MAAMqB,EAAcrD,GAAQ,CAC/B,GAAIA,EACK,OAAAD,EAAO,IAAI,MAAM,2BAA2BC,CAAG,iBAAiB+B,CAAU,EAAE,CAAC,EAEtFuB,EAAWC,EAAQ,CAAC,CAAA,CACrB,EAEDD,EAAWC,EAAQ,CAAC,CACtB,CACD,CACH,EAEAD,EAAW,CAAC,CAAA,CACb,CAAA,CASI,uBAAuBvB,EAAoBI,EAAe,IAAsB,CAC9E,OAAA,KAAK,SAAS,MAAOH,GAAS,CAC7B,MAAAoB,EAAcrB,EAAW,MAAM,GAAG,EACxC,IAAIsB,EAAc,GAElB,UAAWG,KAAaJ,EAAa,CACnCC,GAAe,GAAGG,CAAS,IAEvB,GAAA,CACF,MAAM,IAAI,QAAc,CAAC1D,EAASC,IAAW,CACtCiC,EAAA,KAAKqB,EAAcrD,GAAQ,CAC1B,GAAA,CAACA,EAAK,OAAOF,EAAQ,EAEzBkC,EAAK,MAAMqB,EAAa,CAAE,KAAAlB,CAAK,EAAInC,GAAQ,CACzC,GAAIA,EACK,OAAAD,EAAO,IAAI,MAAM,4BAA4BC,CAAG,iBAAiB+B,CAAU,EAAE,CAAC,EAE/EjC,EAAA,CAAA,CACT,CAAA,CACF,CAAA,CACF,QACMyC,EAAO,CACd,cAAQ,MAAM,+BAA+Bc,CAAW,GAAId,CAAK,EAC3DA,CAAA,CACR,CACF,CACD,CAAA,CASH,MAAa,aAAaR,EAAoBD,EAAqC,CAC1E,OAAA,KAAK,SAAS,MAAOE,GACnB,IAAI,QAAQ,CAAClC,EAASC,IAAW,CACtCiC,EAAK,QAAQD,EAAYD,EAAY9B,GAAQ,CAC3C,GAAIA,EACK,OAAAD,EAAO,IAAI,MAAM,yBAAyBC,CAAG,iBAAiB+B,CAAU,gBAAgBD,CAAS,EAAE,CAAC,EAE7GhC,EAAQ,EAAI,CAAA,CACb,CAAA,CACF,CACF,CAAA,CAMI,OAAc,CACnB,KAAK,oBAAoB,EACzB,KAAK,OAAO,IAAI,CAAA,CAEpB,CAIA,eAAeF,GACbL,EACAC,EACA/B,EACAD,EACiB,CACjB,OAAO,IAAI,QAAQ,CAACsC,EAASC,IAAW,CAC/BR,EAAA,GAAG,QAAS,IAAM,CACvBO,EAAQP,CAAM,CAAA,CACf,EAEMA,EAAA,GAAG,QAAUS,GAAiB,CAEnCD,EAAO,IAAI,MAAM,gBAAgBC,CAAG,EAAE,CAAC,CAAA,CACxC,EAEMT,EAAA,GAAG,QAAS,IAAM,CACf,CACT,EAEDA,EAAO,QAAQC,CAAM,EAOpBD,EAAe,WAAW,EAAI,CAAA,CAChC,CACH,CAEA,eAAe+B,GAAWhJ,EAAkBkI,EAAciD,EAAmBC,EAAmBC,EAAoBC,EAA4C,CAC9J,OAAO,IAAI,QAAQ,CAAC9D,EAASC,IAAW,CACtCS,EAAK,WAAWiD,EAAWC,EAAWC,EAAYC,EAAY,CAAC5D,EAAKC,IAC9DD,GACK1H,EAAA,MAAM,qBAAqB0H,CAAG,EAAE,EAChCD,EAAOC,CAAG,GAGZF,EAAQG,CAAM,CACtB,CAAA,CACF,CACH,CCprBO,MAAM4D,GAAe,6BACfC,GAAqB,oBACrBC,GAAwB,iCAE9B,SAAS3F,EAAQ4F,EAAoB,CACnC,OAAArJ,EAAM,KAAKqJ,EAAY,gBAAgB,CAChD,CAEO,SAASC,EAAYD,EAAoB,CAC9C,OAAOrJ,EAAM,KAAKyD,EAAQ4F,CAAU,EAAG,UAAU,CACnD,CAEgB,SAAAE,GAAiBF,EAAoB3K,EAAc,CACjE,OAAOsB,EAAM,KAAKsJ,EAAYD,CAAU,EAAG,MAAMhI,EAAoB,CAAC,IAAI5C,EAAQC,CAAI,CAAC,EAAE,CAC3F,CAEgB,SAAA8K,EAAaH,EAAoB3K,EAAc,CAC7D,OAAOsB,EAAM,KAAKuJ,GAAiBF,EAAY3K,CAAI,EAAG,UAAU,CAClE,CAEgB,SAAA+K,EAAaJ,EAAoB3K,EAAsB,CACrE,OAAOsB,EAAM,KAAKwJ,EAAaH,EAAY3K,CAAI,EAAG,WAAW,CAC/D,CAMgB,SAAAgL,GAAqBL,EAAoB3K,EAAsB,CAC7E,OAAOsB,EAAM,KAAKwJ,EAAaH,EAAY3K,CAAI,EAAG,WAAW,CAC/D,CAEgB,SAAAiL,EAASN,EAAoB3K,EAAc,CAClD,OAAAsB,EAAM,KAAKsJ,EAAYD,CAAU,EAAG,8BAA8B5K,EAAQC,CAAI,CAAC,EAAE,CAC1F,CAEgB,SAAAkL,GAASP,EAAoB3K,EAAc,CACzD,OAAOsB,EAAM,KAAK2J,EAASN,EAAY3K,CAAI,EAAG,OAAO,CACvD,CAEgB,SAAAmL,GAAiBR,EAAoB3K,EAAc,CAC1D,OAAAsB,EAAM,KAAKsJ,EAAYD,CAAU,EAAG,qBAAqB5K,EAAQC,CAAI,CAAC,GAAI0K,EAAqB,CACxG,CAEgB,SAAAU,EAAcT,EAAoB3K,EAAsB,CACtE,OAAOsB,EAAM,KAAK6J,GAAiBR,EAAY3K,CAAI,EAAG,aAAa,CACrE,CAEO,SAASqL,GAAeV,EAAoB,CACjD,OAAOrJ,EAAM,KAAKyD,EAAQ4F,CAAU,EAAG,iBAAiB,CAC1D,CAEO,SAASW,EAAeX,EAAoB,CACjD,OAAOrJ,EAAM,KAAKyD,EAAQ4F,CAAU,EAAG,gBAAgB,CACzD,CAEO,SAASY,GAAiBZ,EAAoB,CACnD,OAAOrJ,EAAM,KAAKyD,EAAQ4F,CAAU,EAAG,wBAAwB,CACjE,CCzDsB,eAAAa,GACpBC,EACAd,EAAoB3K,EACpB,CACA,MAAM2F,EAAS,MAAM+F,EAAeD,EAAWd,EAAY3K,EAAM,UAAU,EAE3E,GAAI2F,EAAO,OACT,MAAM,IAAI,MAAM,6BAA6BA,EAAO,MAAM,EAAE,CAEhE,CAEsB,eAAAgG,GACpBF,EACAd,EAAoB3K,EACpB,CACA,MAAM2F,EAAS,MAAM+F,EAAeD,EAAWd,EAAY3K,EAAM,cAAc,EAE/E,GAAI2F,EAAO,OACT,MAAM,IAAI,MAAM,8BAA8BA,EAAO,MAAM,EAAE,CAEjE,CAWgB,SAAAiG,EAAWC,EAA0BC,EAAyB,CAC5E,OAAIA,EACKD,EAAO,WAAaA,EAAO,MAG7BA,EAAO,SAChB,CAEO,SAASE,GAAqBF,EAA0B,CAC7D,OAAOA,EAAO,YAAc,MAC9B,CAEA,eAAsBG,GACpB/M,EACAwM,EACAd,EAAoB3K,EACO,CACvB,IAAA2F,EACA,GAAA,CACFA,EAAS,MAAM+F,EAAeD,EAAWd,EAAY3K,EAAM,YAAY,QAChEmC,EAAY,CACnB,MAAO,CAAE,UAAW,OAAOA,CAAC,CAAE,CAAA,CAGhC,GAAIwD,EAAO,OACT,OAAA1G,EAAO,KAAK,4CAA4C0G,EAAO,MAAM,aAAaA,EAAO,MAAM,EAAE,EAE1F,CAAE,UAAWA,CAAO,EAG7B,MAAMsG,EAAYC,EAAiBvG,EAAO,OAAQ,WAAW,EACvDwG,EAAQD,EAAiBvG,EAAO,OAAQ,OAAO,EAC/CkG,EAA2B,CAC/B,UAAWlG,EACX,UAAAsG,EACA,MAAAE,CACF,EAEI,OAACN,EAAO,OACV5M,EAAO,KAAK,oCAAoC,EAG7C4M,EAAO,WACV5M,EAAO,KAAK,wCAAwC,EAG/C4M,CACT,CAKO,SAASO,GACdC,EACAC,EACAC,EACAC,EACA,CACA,MAAMC,EAAWhF,EAAA,YAAY,EAAE,EAAE,SAAS,KAAK,EACzCiF,EAAWL,EAEV,MAAA;AAAA;AAAA,UAECC,CAAa;AAAA;AAAA,UAEbA,CAAa;AAAA;AAAA;AAAA,iBAGNI,CAAQ;AAAA;AAAA,WAEdD,CAAQ;AAAA;AAAA;AAAA,6BAGUC,CAAQ;AAAA;AAAA,WAE1BD,CAAQ;AAAA;AAAA;AAAA;AAAA,UAITD,CAAM,aAAaD,CAAmB;AAAA,YACpCD,CAAa;AAAA;AAAA,iBAERA,CAAa;AAAA;AAAA;AAAA;AAAA,CAK9B,CAIO,SAASK,GACdC,EACAC,EACAR,EACAC,EACAC,EAEAO,EACAN,EACA,CACA,MAAMO,EAAc,OAAO,QAAQF,CAAS,EAAE,IAAI,CAAC,CAACG,EAAKC,CAAK,IAAM,GAAGD,CAAG,KAAKC,CAAK,GAAG,EAAE,KAAK,GAAG,EAC3FR,EAAWhF,EAAA,YAAY,EAAE,EAAE,SAAS,KAAK,EACzCiF,EAAWL,EAEV,MAAA;AAAA;AAAA,UAECC,CAAa;AAAA;AAAA,UAEbA,CAAa;AAAA;AAAA;AAAA,iBAGNI,CAAQ;AAAA;AAAA,WAEdD,CAAQ;AAAA;AAAA;AAAA,6BAGUC,CAAQ;AAAA;AAAA,WAE1BD,CAAQ;AAAA;AAAA;AAAA;AAAA;AAAA,UAKTD,CAAM,aAAaD,CAAmB;AAAA,YACpCD,CAAa;AAAA;AAAA;AAAA;AAAA;AAAA,cAKXS,CAAW;AAAA,UACfD,CAAS,WAAWF,CAAe;AAAA,YACjCN,CAAa;AAAA;AAAA,CAGzB,CAEA,eAAsBZ,EACpBD,EACAd,EAAoB3K,EACpBwG,EACA,CACA,MAAM0G,EAAgBC,EAAqBxC,EAAY3K,CAAI,EACrDqL,EAAiB+B,GAAsBzC,CAAU,EAEjD0C,EAAM,GAAGH,CAAa,oBAAoB7B,CAAc,IAAI7E,CAAO,GAClE,OAAA,MAAMiF,EAAU,KAAK4B,CAAG,CACjC,CAEA,SAASnB,EAAiBoB,EAAgBC,EAAqB,CAM7D,OAJmBC,GAAgBA,EAAI,QAAQ,kBAAmB,EAAE,GAEpCF,CAAM,EAEjB,MAAM;AAAA,CAAI,EAAE,KAAMG,GAAS,CACxC,KAAA,CAACC,EAAM7B,CAAM,EAAI4B,EAAK,KAAK,EAAE,MAAM,QAAQ,EAE1C,OAAAC,IAASH,GAAe1B,IAAW,SAAA,CAC3C,CACH,CC9La,MAAA8B,EAAWC,IAAE,OAAO,CAC/B,MAAOA,IAAE,OAAO,EAChB,OAAQA,IAAE,OAAO,CACnB,CAAC,EAIYC,GAAaD,IAAE,OAAO,CACjC,KAAMD,EACN,KAAMA,EAAS,SAAS,EACxB,WAAYA,EACZ,MAAOA,EAEP,UAAWA,EAEX,iBAAkBA,CACpB,CAAC,EAIYG,GAAiBF,IAAE,OAAO,CACrC,OAAQA,IAAE,OAAO,EACjB,WAAYA,IAAE,OAAO,EACrB,MAAOC,GAIP,gBAAiBD,EAAAA,EAAE,UAAU,QAAQ,EAAK,EAG1C,UAAWA,EAAAA,EAAE,SAAS,QAAQ,QAAQ,EAGtC,YAAaA,EAAA,EAAE,QAAQ,EAAE,QAAQ,EAAI,CACvC,CAAC,EAQM,SAASG,GACdC,EACAC,EACAzG,EACA0G,EACAC,EACAC,EACgB,CACT,MAAA,CACL,OAAAJ,EACA,WAAAC,EACA,MAAAzG,EACA,gBAAA0G,EACA,UAAAC,EACA,YAAAC,CACF,CACF,CAEO,SAASC,GAAoBC,EAAiC,CACnE,OAAOR,GAAe,MAAM,KAAK,MAAMQ,CAAO,CAAC,CACjD,CAEO,SAASC,GAAwBpH,EAA8B,CACpE,OAAO,KAAK,UAAUA,EAAM,OAAW,CAAC,CAC1C,CCvDA,MAAMqH,EAA0B,KAEzB,MAAMC,CAAM,CAEjB,YACkBxP,EACAwM,EACCiD,EACjB,CALMnK,EAAA,iBAAgC,CAAE,KAAM,MAAO,GAErC,KAAA,OAAAtF,EACA,KAAA,UAAAwM,EACC,KAAA,SAAAiD,CAAA,CAGZ,MAAO,CACL,MAAA,CACL,SAAU,KAAK,SACf,UAAW,KAAK,SAClB,CAAA,CAGF,aAAoB,KAAKzP,EAAkBkH,EAA2C,CAChF,GAAA,CACF,MAAMsF,EAAY,MAAMxF,EAAU,KAAKhH,EAAQkH,CAAM,EACrD,OAAO,IAAIsI,EAAMxP,EAAQwM,EAAW/G,EAAAA,SAASyB,EAAO,QAAQ,CAAC,QACtDhE,EAAY,CACZ,MAAAlD,EAAA,MAAM,uCAAuCkD,CAAC,EAAE,EACjDA,CAAA,CACR,CAGK,SAAU,CACf,KAAK,UAAU,MAAM,CAAA,CAIvB,MAAa,SAAqC,CAC1C,MAAAnC,EAAO,MAAM,KAAK,QAAQ,EAC1B2K,EAAa,MAAM,KAAK,qBAAqB,EAC5C,OAAA,MAAMqB,GAAiB,KAAK,OAAQ,KAAK,UAAWrB,EAAY3K,EAAK,IAAI,CAAA,CAKlF,MAAa,MAAM8L,EAAyB,CACpC,MAAA9L,EAAO,MAAM,KAAK,QAAQ,EAC1B2K,EAAa,MAAM,KAAK,qBAAqB,EAE/C,GAAA,CACF,GAAI,CAACiB,EAAW,MAAM,KAAK,QAAQ,EAAGE,CAAc,EAClD,aAAMN,GAAmB,KAAK,UAAWb,EAAY3K,EAAK,IAAI,EAGvD,MAAM,KAAK,yBAAyB8L,CAAc,QAEpD3J,EAAY,CACf,IAAAC,EAAM,gBAAgBD,CAAC,GAEvBwM,EAAO,GACP,GAAA,CACFA,EAAO,MAAM,KAAK,UAAU,SAASC,GAAwBjE,CAAU,CAAC,EACxEvI,GAAO,yBAAyBuM,CAAI,SAC7BxM,EAAY,CACnBC,GAAO,sCAAsCD,CAAC,EAAA,CAG3C,WAAA,OAAO,MAAMC,CAAG,EACf,IAAI,MAAMA,CAAG,CAAA,CACrB,CAKF,MAAa,MAAO,CACZ,MAAApC,EAAO,MAAM,KAAK,QAAQ,EAC1B2K,EAAa,MAAM,KAAK,qBAAqB,EAE/C,GAAA,CACI,MAAAjF,EAAQ,MAAM,KAAK,QAAQ,EAC7B,GAAAqG,GAAqBrG,CAAK,EAAG,CAC/B,MAAMmJ,GAAsB,KAAK,UAAWlE,EAAY3K,EAAK,IAAI,EAE3D,MAAA8L,EAAiBpG,EAAM,QAAU,GACvC,OAAO,MAAM,KAAK,yBAAyBoG,EAAgB,IAAM,GAAI,EAAK,CAAA,QAErE3J,EAAY,CACb,MAAAC,EAAM,eAAeD,CAAC,GACvB,WAAA,OAAO,MAAMC,CAAG,EACf,IAAI,MAAMA,CAAG,CAAA,CACrB,CAIF,MAAa,OAA0B,CACrC,aAAM,KAAK,aAAa,EACxB,KAAK,QAAQ,EACN,EAAA,CAIT,MAAa,cAA8B,CACnC,MAAAuI,EAAa,MAAM,KAAK,qBAAqB,EAE9C,KAAA,OAAO,KAAK,wCAAwC,EACzD,MAAM,KAAK,KAAK,EAEhB,KAAK,OAAO,KAAK,wCAAwCmE,EAAenE,CAAU,CAAC,gBAAgB,EACnG,MAAM,KAAK,UAAU,aAAamE,EAAenE,CAAU,CAAC,CAAA,CAM9D,MAAa,cAAcoE,EAA+C,CACxE,MAAMvN,EAA4B,CAAE,aAAcuN,EAAQ,aAAc,KAAM,MAAO,EAE/E,CAAE,WAAAC,GAAeD,EAGjB/L,EAAmB,CACvB,GAAGiM,GACH,GAAGF,CACL,EACAvN,EAAM,YAAcwB,EAAI,SAEpB,GAAA,CAKF,OAJM,MAAA,KAAK,iBAAiBxB,EAAOwN,CAAU,EACvC,MAAA,KAAK,iBAAiBxN,EAAOwN,CAAU,EAEhB,MAAM,KAAK,wBAAwBxN,EAAOwB,EAAKgM,CAAU,GAKhF,MAAA,KAAK,2BAA2BxN,EAAOwN,CAAU,EAEvD,MAAMA,GAAA,YAAAA,EAAa,8BAEnB,MAAM,KAAK,uBAAuBxN,EAAOwN,EAAYhM,CAAG,EAClD,MAAA,KAAK,iBAAiBxB,CAAK,EACjC,MAAM,KAAK,wBAAwBA,EAAOwN,EAAYhM,CAAG,EACnD,MAAA,KAAK,gCAAgCxB,EAAOwN,CAAU,EACtD,MAAA,KAAK,2BAA2BxN,EAAOwN,CAAU,EACvD,MAAM,KAAK,4BAA4BxN,EAAOwN,EAAYhM,CAAG,EACvD,MAAA,KAAK,qBAAqBxB,EAAOwN,CAAU,EAE1CxN,EAAM,iBAfX,MAAMwN,GAAA,YAAAA,EAAa,2DACZxN,EAAM,uBAeRW,EAAY,CACb,MAAAC,EAAM,wBAAwBD,CAAC,YAAY,KAAK,UAAU,KAAK,oBAAoBX,CAAK,CAAC,CAAC,GAC3F,WAAA,OAAO,MAAMY,CAAG,EAEf,IAAI,MAAMA,CAAG,CAAA,CACrB,CAGF,MAAc,2BAA2BZ,EAA2BwN,EAA0D,CAC5HxN,EAAM,KAAO,uBACRoK,EAAWpK,EAAM,MAAQA,EAAM,gBAAkB,EAAK,IAI3D,MAAMwN,GAAA,YAAAA,EAAa,yBACnB,MAAM,KAAK,KAAK,EAAA,CAGV,oBAAoBxN,EAA+C,CACnE,MAAA0N,EAAY,CAAE,GAAG1N,CAAM,EACnB,OAAA0N,EAAA,gBAAkB,CAAE,GAAGA,EAAU,gBAAiB,cAAe,CAAE,QAAS,YAAc,EAC7FA,CAAA,CAGT,MAAc,qBAAqB1N,EAA2BwN,EAA0D,CACtHxN,EAAM,KAAO,iBACb,MAAMwN,GAAA,YAAAA,EAAa,wCACnB,MAAM,KAAK,MAAMxN,EAAM,gBAAkB,EAAK,EAC9CA,EAAM,QAAU,GAChB,KAAK,UAAYA,EAEjB,MAAMwN,GAAA,YAAAA,EAAa,4CAA0C,CAG/D,MAAc,4BAA4BxN,EAA2BwN,EAA0DhM,EAAkB,CAC/IxB,EAAM,KAAO,wBACb,MAAM2E,EAAS3E,EAAM,gBACrB,MAAMwN,GAAA,YAAAA,EAAa,qCACnBxN,EAAM,eAAiBuM,GACrB5H,EAAO,OACPA,EAAO,WACP3E,EAAM,MACNkD,EAAA,SAAS1B,EAAI,eAAe,EAC5BA,EAAI,SAAU,QACdxB,EAAM,gBAAkB,EAC1B,EACA,MAAM,KAAK,UAAU,qBACnB2N,EAAsB3N,EAAM,UAAW,EACvC+M,GAAwB/M,EAAM,cAAc,CAC9C,EACA,MAAMwN,GAAA,YAAAA,EAAa,iCAA+B,CAGpD,MAAc,2BAA2BxN,EAA2BwN,EAA0D,CAC5H,MAAMA,GAAA,YAAAA,EAAa,yCACnBxN,EAAM,KAAO,uBAEb,MAAM2E,EAAS3E,EAAM,gBAEjB,IAAA4N,EAqBJ,GApBI5N,EAAM,eACW4N,EAAAzC,GACjBxG,EAAO,YAAY,WACnBA,EAAO,YAAY,KACnB,MAAM,KAAK,gCAAgC3E,EAAM,WAAaA,EAAM,IAAK,EACzE2E,EAAO,WACPA,EAAO,SAAS,WAChB3E,EAAM,SAAU,aAChBA,EAAM,SAAU,YAClB,EAEmB4N,EAAAhD,GACjB,MAAM,KAAK,gCAAgC5K,EAAM,WAAaA,EAAM,IAAK,EACzE2E,EAAO,WACPA,EAAO,SAAS,WAChB3E,EAAM,SAAU,YAClB,EAIE,CADgB,MAAM,KAAK,UAAU,qBAAqB4L,GAAsB5L,EAAM,UAAW,EAAG4N,CAAgB,EAEhH,MAAA,IAAI,MAAM,kDAAkDN,EAAetN,EAAM,UAAW,CAAC,EAAE,EAEvG,MAAMwN,GAAA,YAAAA,EAAa,sCAAoC,CAGzD,MAAc,gCAAgCxN,EAA2BwN,EAA0D,CACjIxN,EAAM,KAAO,4BACb,MAAM2E,EAAS3E,EAAM,gBACrB,MAAMwN,GAAA,YAAAA,EAAa,mCACR,SAAA,CAAC3L,EAAUiL,CAAO,IAAK,OAAO,QAAQnI,EAAO,aAAa,EACnE,MAAM,KAAK,UAAU,qBAAqB9C,EAAUiL,CAAO,EAC3D,KAAK,OAAO,KAAK,gBAAgBjL,CAAQ,EAAE,EAGlC,UAAAF,KAAOgD,EAAO,aACjB,MAAA,KAAK,UAAU,uBAAuBhD,CAAG,EAC/C,KAAK,OAAO,KAAK,qBAAqBA,CAAG,EAAE,EAE7C,MAAM6L,GAAA,YAAAA,EAAa,6BAA2B,CAGhD,MAAc,wBAAwBxN,EAA2BwN,EAA0DhM,EAAkB,CAC3IxB,EAAM,KAAO,oBAEb,MAAMwN,GAAA,YAAAA,EAAa,6BACb,MAAA7I,EAAS,MAAMkJ,uBAAqB,CACxC,OAAQ,KAAK,OACb,WAAYP,EAAetN,EAAM,UAAW,EAC5C,UAAW,CACT,KAAM,kBACN,MAAO,CACL,MAAOA,EAAM,MAAO,MAAM,OAC1B,KAAMA,EAAM,MAAO,KAAK,OACxB,KAAMA,EAAM,MAAO,KAAM,OACzB,MAAOA,EAAM,MAAO,UAAU,OAC9B,aAAcA,EAAM,MAAO,iBAAiB,OAC5C,WAAYA,EAAM,MAAO,WAAW,OAEpC,UAAWA,EAAM,MAAO,KAAM,MAC9B,UAAWA,EAAM,MAAO,KAAK,MAC7B,WAAYA,EAAM,MAAO,UAAU,KAAA,CAEvC,EACA,YAAawB,EAAI,QACjB,gBAAiB0B,EAAAA,SAAS1B,EAAI,eAAe,EAC7C,uBAAwBA,EAAI,uBAC5B,SAAUxB,EAAM,gBAAkB,EAAA,CACnC,EACKA,EAAA,gBAAkB,CAAE,GAAG2E,CAAO,EACpC,MAAM6I,GAAA,YAAAA,EAAa,wBAAsB,CAG3C,MAAc,iBAAiBxN,EAA2B,OAIxD,GAHAA,EAAM,KAAO,aACbA,EAAM,MAAQ,MAAM,KAAK,WAAWA,EAAM,WAAaA,EAAM,IAAK,EAE9D,CAACA,EAAM,MAAM,MAAM,QAClB,CAACA,EAAM,MAAM,KAAK,QAClB,CAACA,EAAM,MAAM,UAAU,QACvB,CAACA,EAAM,MAAM,iBAAiB,QAC9B,CAACA,EAAM,MAAM,WAAW,QACxB,GAACoE,EAAApE,EAAM,MAAM,OAAZ,MAAAoE,EAAkB,QAChB,MAAA,IAAI,MAAM,mDAAmD,CACrE,CAGF,MAAc,uBAAuBpE,EAA2BwN,EAA0DhM,EAAkB,CAC1IxB,EAAM,KAAO,mBACb,MAAMwN,GAAA,YAAAA,EAAa,mDAEnB,MAAMM,EAAe,MAAMC,GAAgB,KAAK,OAAQ,KAAK,SAAS,EACtE,GAAID,EAAed,EACjB,MAAM,IAAI,MAAM,iBAAiBc,CAAY,wBAAwBd,CAAuB,uCAAuC,EAE/H,MAAAgB,EAAc,MAAM,KAAK,qCAC7BxM,EAAI,aAAcA,EAAI,SAAWxB,EAAM,WAAaA,EAAM,KAAOA,EAAM,gBAAkB,EAC3F,EACA,MAAMwN,GAAA,YAAAA,EAAa,6DAEnBxN,EAAM,SAAW,CAAE,GAAGgO,EAAa,QAAS,MAAU,EACtDhO,EAAM,mBAAqBgO,EAAY,OAAA,CAGzC,MAAc,iBAAiBhO,EAA2BwN,EAA0D,CAClHxN,EAAM,KAAO,aACb,MAAMwN,GAAA,YAAAA,EAAa,qCACbxN,EAAA,KAAO,MAAM,KAAK,QAAQ,EAChC,MAAMwN,GAAA,YAAAA,EAAa,iCAA+B,CAGpD,MAAc,iBAAiBxN,EAA2BwN,EAA0D,CAClHxN,EAAM,KAAO,aACb,MAAMwN,GAAA,YAAAA,EAAa,oCACbxN,EAAA,WAAa,MAAM,KAAK,qBAAqB,EACnD,MAAMwN,GAAA,YAAAA,EAAa,kCAAgC,CAGrD,MAAc,wBACZxN,EACAwB,EACAgM,EACkB,OAKd,GAJJxN,EAAM,KAAO,aACb,MAAMwN,GAAA,YAAAA,EAAa,gCACbxN,EAAA,MAAQ,MAAM,KAAK,QAAQ,EAE7B,GAACoE,EAAApE,EAAM,QAAN,MAAAoE,EAAa,WACT,MAAA,GAML,GAHJ,MAAMoJ,GAAA,YAAAA,EAAa,uCAEnBxN,EAAM,gBAAkB,MAAM,KAAK,kBAAkBA,EAAM,UAAW,EAClE,CAACA,EAAM,gBACH,MAAA,IAAI,MAAM,4EAA4E,EAG9F,MAAMiO,EAASjO,EAAM,gBAAgB,iBAAmBwB,EAAI,gBACtD0M,EAAgBlO,EAAM,gBAAgB,WAAawB,EAAI,SAAU,QAWnE,OAVExB,EAAA,YAAc,EAAEiO,GAAUC,GAChC,KAAK,OAAO,KAAK,sCAAsClO,EAAM,WAAW,EAAE,EAEpEA,EAAA,eAAiBA,EAAM,gBAAgB,YACzCA,EAAM,eACH,KAAA,OAAO,KAAK,oCAAoC,EAEhD,KAAA,OAAO,KAAK,wCAAwC,EAGtDA,EAAM,aAKX,MAAMwN,GAAA,YAAAA,EAAa,yBACnB,MAAM,KAAK,KAAK,EAET,KAPL,MAAMA,GAAA,YAAAA,EAAa,4BACZ,GAMF,CAGT,MAAa,qCACXW,EACAC,EACAjF,EACA3K,EACA8L,EACA,CACA,MAAMtK,EAAiC,CAAC,EACpC,GAAA,CACI,MAAA+D,EAAK,MAAM,KAAK,iBACpBoK,EAAchF,EAAY3K,EAC1B,KAAM,MAAM4P,EAAS,OAAO,EAC9B,EACApO,EAAM,KAAK+D,CAAE,EAEP,MAAAsK,EAAa,MAAM,KAAK,iBAC5BF,EAAchF,EAAY3K,EAC1B,cAAe8P,EACjB,EACAtO,EAAM,KAAKqO,CAAU,EAErB,MAAM/C,EAAYiD,GAAgBpF,EAAY3K,EAAK,IAAI,EACvD,GAAI8L,EAAgB,CACZ,MAAAK,EAAQ,MAAM,KAAK,iBACvBwD,EAAchF,EAAY3K,EAC1B,QAASgQ,EACX,EACAxO,EAAM,KAAK2K,CAAK,EAChB,MAAM,KAAK,UAAU,MAAMW,EAAW,GAAK,CAAA,CAGtC,MAAA,CACL,QAAStL,EACT,aAAcsK,EAAiBgB,EAAY,OAC3C,aAAcmD,EAAoBtF,EAAY3K,EAAK,IAAI,CACzD,QACOmC,EAAY,CACnB,MAAMC,EAAM,4CAA4CD,CAAC,YAAY,KAAK,UAAUX,CAAK,CAAC,GACrF,WAAA,OAAO,MAAMY,CAAG,EACfD,CAAA,CACR,CASF,MAAa,iBACXwN,EACAhF,EACA3K,EACAK,EACAC,EACgC,CAChC,MAAMkB,EAA+B,CAAC,EAChCA,EAAA,YAAc0O,EAAmBvF,CAAU,EACjD,MAAM,KAAK,UAAU,uBAAuBnJ,EAAM,WAAW,EAC7DA,EAAM,mBAAqB,GAE3B,IAAI2O,EAAoD,KACxD,MAAMC,EAAW,EACjB,QAASC,EAAI,EAAGA,GAAKD,EAAUC,IACzB,GAAA,CACFF,EAAuB,MAAMhQ,GAC3B,KAAK,OACLwP,EACAtP,EACAC,EACAN,EAAK,KAAMA,EAAK,QAClB,EACA,YACOmC,EAAY,CAEnB,GADA,MAAMzC,EAAAA,MAAM,GAAG,EACX2Q,GAAKD,EACP,MAAM,IAAI,MAAM,qBAAqBA,CAAQ,0BAA0BjO,CAAC,EAAE,CAC5E,CAGEX,EAAA,eAAiBkD,WAASyL,CAAoB,EAEpD3O,EAAM,iBAAmBF,EAAM,QAAQE,EAAM,eAAe,WAAW,EACvEA,EAAM,UAAYF,EAAM,KAAKE,EAAM,YAAaA,EAAM,eAAe,QAAQ,EACvEA,EAAA,kBAAoBA,EAAM,UAAY,OAE5C,MAAM,KAAK,UAAU,uBAAuBA,EAAM,SAAS,EAC3D,MAAM,KAAK,UAAU,WAAWA,EAAM,iBAAkBA,EAAM,iBAAiB,EAC/EA,EAAM,WAAa,GAEf,GAAA,CACI,MAAA,KAAK,UAAU,KAAK,UAAU,OAC1B,CACJ,MAAA,IAAI,MAAM,iFAAiF,CAAA,CAI7F,MAAA8O,EAAc,MAAM,KAAK,UAAU,KACvC,6BAA6B9O,EAAM,iBAAiB,gBAAgBA,EAAM,SAAS,EACrF,EAEA,GAAI8O,EAAY,OACR,MAAA,IAAI,MAAM,6CAA6CA,EAAY,MAAM,aAAaA,EAAY,MAAM,EAAE,EAElH,OAAA9O,EAAM,UAAY,GAEXA,CAAA,CAGT,MAAa,aAAamJ,EAAoB3K,EAAY,CACxD,MAAMuQ,EAAsBpD,EAAqBxC,EAAY3K,EAAK,IAAI,EAChEwQ,EAAiBC,EAAgB9F,EAAY3K,EAAK,IAAI,EACtD0Q,EAAqBT,EAAoBtF,EAAY3K,EAAK,IAAI,EAEhE,MAAA,CAAC,MAAM,KAAK,UAAU,gBAAgB0Q,CAAkB,GACvD,CAAC,MAAM,KAAK,UAAU,gBAAgBF,CAAc,GACpD,CAAC,MAAM,KAAK,UAAU,gBAAgBD,CAAmB,CAIvD,CAGT,MAAa,yBAAyBzE,EAAyB6E,EAAmB,IAAMC,EAAQ,GAAIC,EAAc,GAAqB,CACrI,MAAMrR,EAAQoR,EAAQD,EAEtB,IAAIlR,EAAQ,EACRiG,EAAQ,MAAM,KAAK,QAAQ,EACxB,KAAAmL,EAAc,CAACjF,EAAWlG,EAAOoG,CAAc,EAAIF,EAAWlG,EAAOoG,CAAc,GAAG,CAG3F,GAFA,MAAMpM,EAAAA,MAAMiR,CAAQ,EACXlR,GAAAkR,EACLlR,EAAQD,EACV,MAAM,IAAI,MAAM,4CAA4CqR,EAAc,UAAY,SAAS,UAAUrR,CAAK,qBAAqB,KAAK,UAAUkG,CAAK,CAAC,EAAE,EAEpJA,EAAA,MAAM,KAAK,QAAQ,CAAA,CAC7B,CAGF,MAAa,kBAAkBiF,EAA6C,CACpE,MAAAW,EAAiB,MAAM,KAAK,UAAU,SAAS6D,EAAsBxE,CAAU,CAAC,EACtF,OAAO0D,GAAoB/C,CAAc,CAAA,CAG3C,MAAa,WAAWX,EAAoB3K,EAAiC,CA4BpE,MA3BmB,CACxB,KAAM,CACJ,MAAO,MAAM8Q,EAAAA,YAAY,EACzB,OAAQ,MAAM,KAAK,gCAAgCnG,EAAY3K,CAAI,CACrE,EACA,WAAY,CACV,MAAO,MAAM8Q,EAAAA,YAAY,EACzB,OAAQ,MAAM,KAAK,gCAAgCnG,EAAY3K,CAAI,CACrE,EACA,MAAO,CACL,MAAO,MAAM8Q,EAAAA,YAAY,EACzB,OAAQ,MAAM,KAAK,gCAAgCnG,EAAY3K,CAAI,CACrE,EACA,KAAM,CACJ,MAAO,MAAM8Q,EAAAA,YAAY,EACzB,OAAQ,MAAM,KAAK,gCAAgCnG,EAAY3K,CAAI,CACrE,EACA,UAAW,CACT,MAAO,MAAM8Q,EAAAA,YAAY,EACzB,OAAQ,MAAM,KAAK,gCAAgCnG,EAAY3K,CAAI,CACrE,EACA,iBAAkB,CAChB,MAAO,MAAM8Q,EAAAA,YAAY,EACzB,OAAQ,MAAM,KAAK,gCAAgCnG,EAAY3K,CAAI,CAAA,CAEvE,CAEO,CAGT,MAAa,kBAAoC,CACxC,OAAA,IAAI,QAAS+Q,GAAQ,CACpB,MAAAC,EAAMlJ,EAAI,aAAa,EACzBkJ,EAAA,OAAO,EAAG,IAAM,CACZ,MAAA9J,EAAQ8J,EAAI,QAAA,EAA8B,KAChDA,EAAI,MAAOC,GAAMF,EAAI7J,CAAI,CAAC,CAAA,CAC3B,CAAA,CACF,CAAA,CAGH,MAAa,gCAAgCyD,EAAoB3K,EAA6B,CAC5F,MAAMkR,EAAcC,GAA4BxG,EAAY3K,EAAK,IAAI,EAE/D,CAAE,OAAA6G,EAAQ,OAAAC,CAAA,EAAW,MAAM,KAAK,UAAU,KAAK,GAAGoK,CAAW,EAAE,EACrE,GAAIpK,EACF,MAAM,IAAI,MAAM,yDAAyDA,CAAM,aAAaD,CAAM,EAAE,EAGtG,MAAO,CAACA,CAAA,CAGV,MAAa,SAAyB,CAC9B,KAAA,CAAE,OAAAA,EAAQ,OAAAC,CAAO,EAAI,MAAM,KAAK,UAAU,KAAK,sBAAsB,EACvE,GAAAA,EACF,MAAM,IAAI,MAAM,iCAAiCA,CAAM,aAAaD,CAAM,EAAE,EAExE,MAAAuK,EAAMvK,EAAO,MAAM;AAAA,CAAI,EAEtB,MAAA,CACL,SAAUuK,EAAI,CAAC,EACf,KAAMA,EAAI,CAAC,CACb,CAAA,CAGF,MAAa,sBAAuB,CAC5B,KAAA,CAAE,OAAAvK,EAAQ,OAAAC,CAAO,EAAI,MAAM,KAAK,UAAU,KAAK,YAAY,EAEjE,GAAIA,EAAQ,CACJ,MAAAuK,EAAO,SAAS,KAAK,QAAQ,GACnC,eAAQ,KAAK,8CAA8CvK,CAAM,aAAaD,CAAM,8BAA8BwK,CAAI,EAAE,EAEjHA,CAAA,CAGT,OAAOxK,EAAO,KAAK,CAAA,CAEvB,CAcA,MAAMoI,GAIF,CACF,gBAAiB,GACjB,SAAU,CACR,KAAM,WACN,QAAStM,EAAoB,CAAA,CAEjC,EA0DA,eAAe4M,GAAgBtQ,EAAkBwM,EAAwC,CACnF,GAAA,CACF,KAAM,CAAE,OAAA5E,EAAQ,OAAAC,CAAA,EAAW,MAAM2E,EAAU,KAAK,2BAA2B,EAC3E,GAAI3E,EACF,MAAM,IAAI,MAAM,kCAAkCA,CAAM,EAAE,EAE5D,OAAOwK,GAAkBzK,CAAM,QACxB,EAAY,CACZ,MAAA5H,EAAA,MAAM,+BAA+B,CAAC,EAAE,EACzC,CAAA,CAEV,CAEO,SAASqS,GAAkBhE,EAAwB,CAClD,MAAAiE,EAAejE,EAAO,MAAM,UAAU,EAC5C,GAAI,CAACiE,EACH,MAAM,IAAI,MAAM,uCAAuCjE,CAAM,EAAE,EAG1D,OAAA,WAAWiE,EAAa,CAAC,CAAC,CACnC"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;"}
@@ -28,4 +28,3 @@ export type LocalPlRestartHook = {
28
28
  type: 'hook';
29
29
  hook(pl: any): void;
30
30
  };
31
- //# sourceMappingURL=options.d.ts.map
@@ -0,0 +1,24 @@
1
+ 'use strict';
2
+
3
+ var tsHelpers = require('@milaboratories/ts-helpers');
4
+ var fsp = require('node:fs/promises');
5
+ var upath = require('upath');
6
+
7
+ function filePid(dir) {
8
+ return upath.join(dir, 'pl_pid');
9
+ }
10
+ async function readPid(filePath) {
11
+ if (!(await tsHelpers.fileExists(filePath))) {
12
+ return undefined;
13
+ }
14
+ const text = await fsp.readFile(filePath);
15
+ return Number(text.toString());
16
+ }
17
+ async function writePid(filePath, pid) {
18
+ await fsp.writeFile(filePath, JSON.stringify(pid));
19
+ }
20
+
21
+ exports.filePid = filePid;
22
+ exports.readPid = readPid;
23
+ exports.writePid = writePid;
24
+ //# sourceMappingURL=pid.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pid.cjs","sources":["../../src/local/pid.ts"],"sourcesContent":["import { fileExists } from '@milaboratories/ts-helpers';\nimport fs from 'node:fs/promises';\nimport upath from 'upath';\n\nexport function filePid(dir: string) {\n return upath.join(dir, 'pl_pid');\n}\n\nexport async function readPid(filePath: string): Promise<number | undefined> {\n if (!(await fileExists(filePath))) {\n return undefined;\n }\n\n const text = await fs.readFile(filePath);\n\n return Number(text.toString());\n}\n\nexport async function writePid(filePath: string, pid: number) {\n await fs.writeFile(filePath, JSON.stringify(pid));\n}\n"],"names":["fileExists","fs"],"mappings":";;;;;;AAIM,SAAU,OAAO,CAAC,GAAW,EAAA;IACjC,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC;AAClC;AAEO,eAAe,OAAO,CAAC,QAAgB,EAAA;IAC5C,IAAI,EAAE,MAAMA,oBAAU,CAAC,QAAQ,CAAC,CAAC,EAAE;AACjC,QAAA,OAAO,SAAS;IAClB;IAEA,MAAM,IAAI,GAAG,MAAMC,GAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC;AAExC,IAAA,OAAO,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;AAChC;AAEO,eAAe,QAAQ,CAAC,QAAgB,EAAE,GAAW,EAAA;AAC1D,IAAA,MAAMA,GAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;AACnD;;;;;;"}
@@ -1,4 +1,3 @@
1
1
  export declare function filePid(dir: string): string;
2
2
  export declare function readPid(filePath: string): Promise<number | undefined>;
3
3
  export declare function writePid(filePath: string, pid: number): Promise<void>;
4
- //# sourceMappingURL=pid.d.ts.map
@@ -0,0 +1,20 @@
1
+ import { fileExists } from '@milaboratories/ts-helpers';
2
+ import fsp from 'node:fs/promises';
3
+ import upath from 'upath';
4
+
5
+ function filePid(dir) {
6
+ return upath.join(dir, 'pl_pid');
7
+ }
8
+ async function readPid(filePath) {
9
+ if (!(await fileExists(filePath))) {
10
+ return undefined;
11
+ }
12
+ const text = await fsp.readFile(filePath);
13
+ return Number(text.toString());
14
+ }
15
+ async function writePid(filePath, pid) {
16
+ await fsp.writeFile(filePath, JSON.stringify(pid));
17
+ }
18
+
19
+ export { filePid, readPid, writePid };
20
+ //# sourceMappingURL=pid.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pid.js","sources":["../../src/local/pid.ts"],"sourcesContent":["import { fileExists } from '@milaboratories/ts-helpers';\nimport fs from 'node:fs/promises';\nimport upath from 'upath';\n\nexport function filePid(dir: string) {\n return upath.join(dir, 'pl_pid');\n}\n\nexport async function readPid(filePath: string): Promise<number | undefined> {\n if (!(await fileExists(filePath))) {\n return undefined;\n }\n\n const text = await fs.readFile(filePath);\n\n return Number(text.toString());\n}\n\nexport async function writePid(filePath: string, pid: number) {\n await fs.writeFile(filePath, JSON.stringify(pid));\n}\n"],"names":["fs"],"mappings":";;;;AAIM,SAAU,OAAO,CAAC,GAAW,EAAA;IACjC,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC;AAClC;AAEO,eAAe,OAAO,CAAC,QAAgB,EAAA;IAC5C,IAAI,EAAE,MAAM,UAAU,CAAC,QAAQ,CAAC,CAAC,EAAE;AACjC,QAAA,OAAO,SAAS;IAClB;IAEA,MAAM,IAAI,GAAG,MAAMA,GAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC;AAExC,IAAA,OAAO,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;AAChC;AAEO,eAAe,QAAQ,CAAC,QAAgB,EAAE,GAAW,EAAA;AAC1D,IAAA,MAAMA,GAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;AACnD;;;;"}