UNPKG

@shoc/shoc

Version:

Run your jobs on Shoc Platform via Shoc CLI.

5 lines (4 loc) 39.3 kB
#!/usr/bin/env node var Re="1.0.3";import{createCommand as Xt}from"commander";import{promises as ue}from"fs";import*as K from"path";import*as q from"yaml";var Ht=".shoc",Wt="config.yaml",pe=K.join(process.env.HOME||"",Ht,Wt);async function P(){try{let o=await ue.readFile(pe,"utf8");return q.parse(o)}catch{return null}}async function Te(o){let e=K.dirname(pe);await ue.mkdir(e,{recursive:!0}),await ue.writeFile(pe,q.stringify(o),"utf8")}import J from"chalk";var n={error(...o){console.log(J.red(...o))},warn(...o){console.log(J.yellow(...o))},info(...o){console.log(J.cyan(...o))},success(...o){console.log(J.green(...o))},just(...o){console.log(...o)},table(...o){console.table(...o)},break(){console.log("")}};import{createCommand as Kt}from"commander";import qt from"prompts";var L="build.shoc.yaml",ve="run.shoc.yaml";function Vt(o){let e=o;for(;e.parent;)e=e.parent;return e}function w(o){let t=Vt(o).opts();return{context:t.context,workspace:t.workspace,dir:t.dir}}var h=o=>(...e)=>{o(...e).catch(t=>{n.error(`${t.message}`),process.exit(1)})};var Ee=Kt("init"),Be="https://shoc.dev";Ee.description("Initialize the configuration").action(h(async o=>{if(await P()){n.warn("The configuration is already initialized.");return}let t=await(await fetch(`${Be}/well-known/endpoints`)).json(),r={providers:[{name:"global",url:Be,api:t.api,identity:t.idp}],contexts:[],defaultContext:"default"},s=o.workspace;s||(s=(await qt({type:"text",name:"name",message:"Please enter your workspace name"})).name),r.contexts.push({name:"default",provider:"global",workspace:s}),await Te(r),n.info("Configuration was successfully initialized.")}));var Ae=Ee;import{createCommand as Jt}from"commander";var Se=Jt("view");Se.description("View the configuration").action(h(async()=>{let o=await P();if(!o){n.warn('No configuration found. Run "shoc config init" to create one.');return}n.just("Available providers"),(o.providers||[]).forEach(e=>{n.just(` - Name: ${e.name}, Url: ${e.url}`)}),n.break(),n.just("Available contexts"),(o.contexts||[]).forEach(e=>{let t=e.name===o.defaultContext,r=` - Name: ${e.name}, Provider: ${e.provider}, Workspace: ${e.workspace} ${t?"(default)":""}`;(t?n.success:n.just)(r)})}));var je=Se;var me=Xt("config");me.addCommand(Ae);me.addCommand(je);var Ie=me;import{createCommand as uo}from"commander";import Pe from"fs";async function g(o){let{context:e,workspace:t,dir:r}=o,s=await P();if(!s)throw new Error('The Shoc CLI is not configured. You can run "shoc config init" to configure it.');let i=e??s.defaultContext,l=s.contexts.filter(c=>c.name===i)[0];if(!l)throw new Error(`The context '${i}' could not be resolved. Please check the configuration.`);let a=s.providers.filter(c=>c.name===l.provider)[0];if(!a)throw new Error(`The provider '${l.provider}' from the context '${l.name}' could not be resolved. Please check the configuration.`);if(!URL.canParse(a.url))throw new Error(`The provider ${a.name} has invalid URL (${a.url})`);if(!URL.canParse(a.api))throw new Error(`The provider ${a.name} has invalid API URL (${a.api})`);if(!URL.canParse(a.identity))throw new Error(`The provider ${a.name} has invalid Identity URL (${a.identity})`);let p=r??process.cwd();if(p.endsWith("/")&&(p=p.substring(0,p.length-1)),!Pe.existsSync(p))throw new Error(`The directory ${p} does not exist`);if(!Pe.lstatSync(p).isDirectory())throw new Error(`The path ${p} is not a directory`);return{name:l.name,providerName:a.name,providerUrl:new URL(a.url),apiUrl:new URL(a.api),identityUrl:new URL(a.identity),workspace:t??l.workspace,dir:p}}import{createCommand as ao}from"commander";import Qt from"open";import eo from"http";import de from"crypto";import Zt from"net";async function Le(){return new Promise(o=>{let e=Zt.createServer();e.listen(0,()=>{let t=e.address().port;e.close(()=>o(t))})})}import fe from"axios";var to=5*60,X={clientId:"cli",scope:"openid email profile shoc offline_access"};async function Ue(o){let e=new URL(o);e.pathname="/.well-known/openid-configuration";try{let t=(await fe.get(e.toString())).data;return{authorizationEndpoint:t.authorization_endpoint,tokenEndpoint:t.token_endpoint}}catch(t){throw Error(`Unable to obtain configuration to start authentication. Details: ${t?.message||"Unknown Reason"}.`)}}async function _e({idp:o,accessToken:e,refreshToken:t}){let r=await Ue(o),s=new URLSearchParams({grant_type:"refresh_token",client_id:X.clientId,refresh_token:t,access_token:e}),l=(await fe.post(r.tokenEndpoint,s)).data;return{accessToken:l.access_token,refreshToken:l.refresh_token}}async function Me({idp:o}){let e=await Le(),t=await Ue(o),r=de.randomBytes(16).toString("hex"),s=de.randomBytes(32).toString("hex"),i=de.createHash("sha256").update(s).digest("base64url"),l=`http://localhost:${e}/signed-in`,a=`${t.authorizationEndpoint}?${new URLSearchParams({response_type:"code",client_id:X.clientId,redirect_uri:l,scope:X.scope,state:r,code_challenge:i,code_challenge_method:"S256"}).toString()}`;n.info("To complete authentication please login in the browser..."),await Qt(a).catch(()=>{n.info(`If login page was not opened automatically, please use the following link to login: ${a}`)});let p=await oo({port:e});if(p.state!==r)throw Error("Authentication failed due to state mismatch");if(!p.code)throw Error("Authentication failed due to missing authorization code");return await io({tokenUrl:t.tokenEndpoint,code:p.code,codeVerifier:s,redirectUri:l})}async function oo({port:o}){let e=eo.createServer();e.listen(o,()=>{n.info("Waiting for the user to complete authentication in the browser...")});let t=new AbortController;try{return await Promise.race([ro(e),so(to,t)])}finally{t.abort(),e.close()}}function ro(o){return new Promise(e=>{o.on("request",(t,r)=>{if(!t.url?.includes("signed-in")){r.writeHead(404),r.end("Not found");return}let s=new URL(t.url||"",`http://${t.headers.host}`).searchParams,i=s.get("code"),l=s.get("state");e({code:i,state:l}),r.writeHead(200),r.end("Authentication is in progress. You can close the browser.")})})}function so(o,e){return new Promise((t,r)=>{let s=setTimeout(()=>{r(new Error(`Authentication was not completed within ${o/60} minutes.`)),e.abort()},o*1e3);e.signal.addEventListener("abort",()=>{clearTimeout(s)})})}async function io({tokenUrl:o,code:e,codeVerifier:t,redirectUri:r}){let s=new URLSearchParams({grant_type:"authorization_code",code:e,redirect_uri:r,client_id:X.clientId,code_verifier:t}),l=(await fe.post(o.toString(),s)).data;return{accessToken:l.access_token,refreshToken:l.refresh_token}}import B from"keytar";import{decodeJwt as no}from"jose";var ee="shoc_access_token",he="shoc_refresh_token";async function ge(o,e){await B.setPassword(o,ee,e.accessToken),await B.setPassword(o,he,e.refreshToken)}async function Z(o){let e=await B.getPassword(o,ee);if(!e)return null;let t=no(e),r=new Date((t.exp||0)*1e3),s=r.getTime()<new Date().getTime();return{id:t.sub||"",sub:t.sub||"",email:t.email,name:t.name,username:t.preferred_username,userType:t.user_type,expires:r,expired:s}}async function Q(o){await B.deletePassword(o,ee),await B.deletePassword(o,he)}async function U(o){let e=o.providerUrl.toString(),t=await Z(e),r=await B.getPassword(e,ee),s=await B.getPassword(e,he);if(!r||!t)throw Error("You need to authenticate to continue.");if(!t.expired)return{session:t,accessToken:r,refreshToken:s};if(!s)throw Error("Your session has expired. Please login again.");let i=o.identityUrl,l=null;try{l=await _e({idp:i,accessToken:r,refreshToken:s}),await ge(e,{accessToken:l.accessToken,refreshToken:l.refreshToken||s})}catch{await Q(e)}let a=await Z(e);if(!a||!l)throw await Q(e),Error("Could not extend your session, please login again.");return{session:a,accessToken:l.accessToken,refreshToken:l.refreshToken||s}}var Ne=ao("login");Ne.description("Authenticate the user in the provider").action(h(async(o,e)=>{let t=await g(w(e)),{accessToken:r,refreshToken:s}=await Me({idp:t.identityUrl});ge(t.providerUrl.toString(),{accessToken:r,refreshToken:s}),n.success("You have been successfully authenticated.")}));var Fe=Ne;import{createCommand as co}from"commander";import te from"chalk";var De=co("session");De.description("Show details of the current session").action(h(async(o,e)=>{let t=await g(w(e)),r=await Z(t.providerUrl.toString());if(!r){n.warn("You are not authenticated yet.");return}n.just(`Session details as of now (${new Date().toLocaleString()}):`),n.just(` - Status: ${r.expired?te.yellow("Expired"):te.green("Active")}`),n.just(` - Valid Until: ${r.expired?te.yellow(r.expires.toLocaleString()):te.green(r.expires.toLocaleString())}`),n.just(` - ID: ${r.id}`),n.just(` - Name: ${r.name}`),n.just(` - Email: ${r.email}`),n.just(` - Username: ${r.username}`),n.just(` - Type: ${r.userType}`)}));var Oe=De;import{createCommand as lo}from"commander";var Ye=lo("logout");Ye.description("Logout from the provider").action(h(async(o,e)=>{let t=await g(w(e));await Q(t.providerUrl.toString()),n.success("You have been logged out successfully!")}));var ze=Ye;var oe=uo("auth");oe.addCommand(Fe);oe.addCommand(Oe);oe.addCommand(ze);var Ge=oe;import{createCommand as cr}from"commander";import{createCommand as go}from"commander";import{createCommand as ho}from"commander";function He(o){return{root:o,gateway:"",serviceRouting:!0,client:"cli",timeout:6e4}}function d(o,e){return new e(He(o))}import po from"axios";var D=class{service;config;constructor(e,t){this.service=e,this.config=t}authBearer(e){return{Authorization:`Bearer ${e}`}}authBasic(e,t){return{Authorization:`Basic ${Buffer.from(`${e}:${t}`).toString("base64")}`}}allowCORS(){return{"Access-Control-Allow-Origin":"*"}}contentType(e){return e?{"Content-Type":e}:{"Content-Type":"application/json"}}form(e){let t=new FormData;return Object.keys(e).forEach(r=>t.append(r,e[r])),t}esc(e){return encodeURIComponent(e)}paramValue(e){return Array.isArray(e)?e.map(this.esc).join(","):this.esc(e)}queryParams(e={}){return Object.keys(e).map(t=>this.mapQueryParam(t,e[t])).join("&")}mapQueryParam(e,t){return t==null||t===""?`${this.esc(e)}`:`${this.esc(e)}=${this.paramValue(t)}`}getApi(e){let t=this.config.root,r=this.config.gateway,s=r===""?"":"/";return this.config.serviceRouting?`${t}${r}${s}${this.service}/${e}`:`${t}${e}`}urlify(e){let t=this.getApi(e.api);return e.query&&(t=`${t}?${this.queryParams(e.query)}`),t}isError(e){return e&&e.stack&&e.message&&typeof e.stack=="string"&&typeof e.message=="string"}};var C=class extends D{webClient;constructor(e,t){super(e,t),this.webClient=po.create({timeout:t.timeout,headers:{...this.contentType(),...this.allowCORS(),client:t.client}})}isXXX(e,t){return e&&e.response&&e.response.status===t}is404(e){return this.isXXX(e,404)}is401(e){return this.isXXX(e,401)}};var k=class extends C{constructor(e){super("shoc-workspace",e)}getAll(e){let t=this.urlify({api:"api/user-workspaces"});return this.webClient.get(t,{headers:{...this.authBearer(e)}})}getById(e,t){let r=this.urlify({api:`api/user-workspaces/${t}`});return this.webClient.get(r,{headers:{...this.authBearer(e)}})}getPermissionsById(e,t){let r=this.urlify({api:`api/user-workspaces/${t}/permissions`});return this.webClient.get(r,{headers:{...this.authBearer(e)}})}getByName(e,t){let r=this.urlify({api:`api/user-workspaces/by-name/${t}`});return this.webClient.get(r,{headers:{...this.authBearer(e)}})}getPermissionsByName(e,t){let r=this.urlify({api:`api/user-workspaces/by-name/${t}/permissions`});return this.webClient.get(r,{headers:{...this.authBearer(e)}})}create(e,t){let r=this.urlify({api:"api/user-workspaces"});return this.webClient.post(r,t,{headers:{...this.authBearer(e)}})}updateById(e,t,r){let s=this.urlify({api:`api/user-workspaces/${t}`});return this.webClient.put(s,r,{headers:{...this.authBearer(e)}})}deleteById(e,t){let r=this.urlify({api:`api/user-workspaces/${t}`});return this.webClient.delete(r,{headers:{...this.authBearer(e)}})}};var _=class extends Error{kind;code;payload;constructor(e,t,r,s){super(r),this.kind=e,this.code=t,this.payload=s}};var mo={UNKNOWN_ERROR:"Unknown Error",NOT_FOUND_ERROR:"The requested object could not be found"};function we(o){return mo[o]||o}import{AxiosError as fo}from"axios";async function f(o,e){let t=await U(o);try{return(await e({apiRoot:o.apiUrl.toString(),token:t.accessToken})).data}catch(r){throw We(r)}}async function re(o,e){try{return(await e({apiRoot:o.apiUrl.toString()})).data}catch(t){throw We(t)}}function We(o){if(o instanceof _)return Error(o.message||we(o.code));if(o instanceof fo&&o.response&&o.response.headers&&o.response.headers["x-shoc-error"]==="aggregate"&&o.response?.data?.errors){let t=o.response.data.errors[0];return t?.message?Error(`${t.message} (${t.code})`):Error(we(t?.code||""))}return Error(o.message)}var Ve=ho("list");Ve.description("List my workspaces").action(h(async(o,e)=>{let t=await g(w(e)),r=await f(t,s=>d(s.apiRoot,k).getAll(s.token));n.just("Available workspaces:"),n.break(),r.forEach(s=>{n.just(` - Name: ${s.name}`),n.just(` Description: ${s.description}`),n.just(` Role: ${s.role}`),n.break()})}));var Ke=Ve;var qe=go("workspaces");qe.addCommand(Ke);var Je=qe;import{createCommand as yo}from"commander";import{createCommand as wo}from"commander";var E=class extends C{constructor(e){super("shoc-cluster",e)}getAll(e,t){let r=this.urlify({api:`api/workspaces/${t}/workspace-clusters`});return this.webClient.get(r,{headers:{...this.authBearer(e)}})}getByName(e,t,r){let s=this.urlify({api:`api/workspaces/${t}/workspace-clusters/by-name/${r}`});return this.webClient.get(s,{headers:{...this.authBearer(e)}})}countAll(e,t){let r=this.urlify({api:`api/workspaces/${t}/workspace-clusters/count`});return this.webClient.get(r,{headers:{...this.authBearer(e)}})}create(e,t,r){let s=this.urlify({api:`api/workspaces/${t}/workspace-clusters`});return this.webClient.post(s,r,{headers:{...this.authBearer(e)}})}ping(e,t,r){let s=this.urlify({api:`api/workspaces/${t}/workspace-clusters/ping`});return this.webClient.post(s,r,{headers:{...this.authBearer(e)}})}};var Xe=wo("list");Xe.description("List my clusters").action(h(async(o,e)=>{let t=await g(w(e)),r=await f(t,i=>d(i.apiRoot,k).getByName(i.token,t.workspace)),s=await f(t,i=>d(i.apiRoot,E).getAll(i.token,r.id));n.just("Available clusters:"),n.break(),s.forEach(i=>{n.just(` - Name: ${i.name}`),n.just(` Description: ${i.description||"N/A"}`),n.just(` Status: ${i.status}`),n.break()})}));var Ze=Xe;var Qe=yo("clusters");Qe.addCommand(Ze);var et=Qe;import{createCommand as ko}from"commander";import{createCommand as Co}from"commander";var O=class extends C{constructor(e){super("shoc-secret",e)}getAll(e,t){let r=this.urlify({api:`api/workspaces/${t}/workspace-user-secrets`});return this.webClient.get(r,{headers:{...this.authBearer(e)}})}countAll(e,t){let r=this.urlify({api:`api/workspaces/${t}/workspace-user-secrets/count`});return this.webClient.get(r,{headers:{...this.authBearer(e)}})}create(e,t,r){let s=this.urlify({api:`api/workspaces/${t}/workspace-user-secrets`});return this.webClient.post(s,r,{headers:{...this.authBearer(e)}})}updateById(e,t,r,s){let i=this.urlify({api:`api/workspaces/${t}/workspace-user-secrets/${r}`});return this.webClient.put(i,s,{headers:{...this.authBearer(e)}})}updateValueById(e,t,r,s){let i=this.urlify({api:`api/workspaces/${t}/workspace-user-secrets/${r}/value`});return this.webClient.put(i,s,{headers:{...this.authBearer(e)}})}deleteById(e,t,r){let s=this.urlify({api:`api/workspaces/${t}/workspace-user-secrets/${r}`});return this.webClient.delete(s,{headers:{...this.authBearer(e)}})}};var tt=Co("list");tt.description("List my secrets").action(h(async(o,e)=>{let t=await g(w(e)),r=await f(t,i=>d(i.apiRoot,k).getByName(i.token,t.workspace)),s=await f(t,i=>d(i.apiRoot,O).getAll(i.token,r.id));n.just("List of defined user secrets:"),s.forEach(i=>{n.just(` - Name: ${i.name}, Encrypted: ${i.encrypted?"\u2713":"\u2717"}, Value: ${i.value}`)})}));var ot=tt;var rt=ko("user-secrets");rt.addCommand(ot);var st=rt;import{createCommand as xo}from"commander";import{createCommand as bo}from"commander";var Y=class extends C{constructor(e){super("shoc-secret",e)}getAll(e,t){let r=this.urlify({api:`api/workspaces/${t}/workspace-secrets`});return this.webClient.get(r,{headers:{...this.authBearer(e)}})}countAll(e,t){let r=this.urlify({api:`api/workspaces/${t}/workspace-secrets/count`});return this.webClient.get(r,{headers:{...this.authBearer(e)}})}create(e,t,r){let s=this.urlify({api:`api/workspaces/${t}/workspace-secrets`});return this.webClient.post(s,r,{headers:{...this.authBearer(e)}})}updateById(e,t,r,s){let i=this.urlify({api:`api/workspaces/${t}/workspace-secrets/${r}`});return this.webClient.put(i,s,{headers:{...this.authBearer(e)}})}updateValueById(e,t,r,s){let i=this.urlify({api:`api/workspaces/${t}/workspace-secrets/${r}/value`});return this.webClient.put(i,s,{headers:{...this.authBearer(e)}})}deleteById(e,t,r){let s=this.urlify({api:`api/workspaces/${t}/workspace-secrets/${r}`});return this.webClient.delete(s,{headers:{...this.authBearer(e)}})}};var it=bo("list");it.description("List workspace secrets").action(h(async(o,e)=>{let t=await g(w(e)),r=await f(t,i=>d(i.apiRoot,k).getByName(i.token,t.workspace)),s=await f(t,i=>d(i.apiRoot,Y).getAll(i.token,r.id));n.just("List of defined workspace secrets:"),s.forEach(i=>{n.just(` - Name: ${i.name}, Encrypted: ${i.encrypted?"\u2713":"\u2717"}, Value: ${i.value}`)})}));var nt=it;var at=xo("workspace-secrets");at.addCommand(nt);var ct=at;import{createCommand as vo}from"commander";import{createCommand as $o}from"commander";var A=class extends C{constructor(e){super("shoc-package",e)}getAll(){let e=this.urlify({api:"api/templates"});return this.webClient.get(e)}getBuildSpecInstanceByName(e,t){let r=this.urlify({api:`api/templates/${e}/variants/${t}/build-spec/instance`});return this.webClient.get(r)}};var lt=$o("list");lt.description("List available templates").action(h(async(o,e)=>{let t=await g(w(e)),r=await re(t,s=>d(s.apiRoot,A).getAll());n.just("Available templates:"),r.forEach(s=>{n.just(` - ${s.name}:[${s.variants.join(", ")}]`)})}));var ut=lt;import{createCommand as Ro}from"commander";import pt from"fs";import{stringify as To}from"yaml";var mt=Ro("init");mt.description("Initialize the target folder with the selected template").argument("<template>","The template name and variant to initialize. Example: alpine:default.").action(h(async(o,e,t)=>{let r=await g(w(t)),s=o.split(":",2),i=s[0].trim(),l=(s[1]||"default").trim(),a=await re(r,x=>d(x.apiRoot,A).getBuildSpecInstanceByName(i,l));n.info(`Initializing the template in the current directory (${r.dir})...`);let p={template:`${i}:${l}`,spec:a,ignore:[]},c=`${r.dir}/${L}`;if(pt.existsSync(c))throw Error(`The build file ${L} already exists!`);let R=To(p);pt.writeFileSync(c,R),n.success(`Initialized the build file (${L}) with the template ${i}:${l}`)}));var dt=mt;var ye=vo("templates");ye.addCommand(ut);ye.addCommand(dt);var ft=ye;import{createCommand as Po}from"commander";var T=class extends C{constructor(e){super("shoc-job",e)}getBy(e,t,r){let s=this.urlify({api:`api/workspaces/${t}/jobs`,query:{...r}});return this.webClient.get(s,{headers:{...this.authBearer(e)}})}getByLocalId(e,t,r){let s=this.urlify({api:`api/workspaces/${t}/jobs/by-local-id/${r}`});return this.webClient.get(s,{headers:{...this.authBearer(e)}})}create(e,t,r){let s=this.urlify({api:`api/workspaces/${t}/jobs`});return this.webClient.post(s,r,{headers:{...this.authBearer(e)}})}submit(e,t,r,s){let i=this.urlify({api:`api/workspaces/${t}/jobs/${r}/submit`});return this.webClient.post(i,s,{headers:{...this.authBearer(e)}})}};import{table as Lo}from"table";import ht from"dayjs";import Bo from"dayjs/plugin/utc.js";ht.extend(Bo);var z=ht;var se=o=>o?z.utc(o).local().format("YYYY-MM-DD HH:mm:ss"):"";var Eo=()=>z.utc().toDate();function M(o,e){let t=z.utc(o).toDate(),r=e?z.utc(e).toDate():Eo(),s=Math.floor((r.getTime()-t.getTime())/1e3),i=s<0?"-":"";s=Math.abs(s);let l=Math.floor(s/86400),a=Math.floor(s%86400/3600),p=Math.floor(s%3600/60),c=s%60,R=`${String(a).padStart(2,"0")}:${String(p).padStart(2,"0")}:${String(c).padStart(2,"0")}`;return l>0?`${i}${l}.${R}`:`${i}${R}`}import{createCommand as Ao}from"commander";import{table as So}from"table";import gt from"chalk";var wt=Ao("details");wt.description("Show details of the job").requiredOption("-j, --job <number>","The job number").option("--json","The raw json object").action(h(async(o,e)=>{let t=parseInt(o.job,10);if(!Number.isSafeInteger(t))throw Error(`The ${o.job} is not valid key for job`);let r=await g(w(e)),s=await f(r,a=>d(a.apiRoot,k).getByName(a.token,r.workspace)),i=await f(r,a=>d(a.apiRoot,T).getByLocalId(a.token,s.id,t));if(o.json){n.just(JSON.stringify(i,null,4));return}let l=[[gt.bold("Name"),gt.bold("Value")],["Id",i.id],["Job Key",i.localId],["Workspace",i.workspaceName],["Cluster",i.clusterName],["User",i.userFullName],["Scope",i.scope],["Namespace",i.namespace],["Status",i.status],["Tasks",i.totalTasks],["Completed Tasks",i.completedTasks],["Succeeded Tasks",i.succeededTasks],["Failed Tasks",i.failedTasks],["Cancelled Tasks",i.cancelledTasks],["Pending Since",se(i.pendingAt)],["Running Since",se(i.runningAt)],["Completed At",se(i.completedAt)]];n.just(So(l,{drawHorizontalLine:()=>!1,drawVerticalLine:()=>!1}))}));var yt=wt;import{createCommand as jo}from"commander";import{table as Io}from"table";var S=class extends C{constructor(e){super("shoc-job",e)}getAll(e,t,r){let s=this.urlify({api:`api/workspaces/${t}/jobs/${r}/tasks`});return this.webClient.get(s,{headers:{...this.authBearer(e)}})}getLogsBySequenceUrl(e,t,r,s){let i=this.urlify({api:`api/workspaces/${t}/jobs/${r}/tasks/by-sequence/${s}/logs`});return Promise.resolve({data:{token:e,url:i}})}};var Ct=jo("tasks");Ct.description("Show tasks of the job").requiredOption("-j, --job <number>","The job number").option("--json","The raw json object").action(h(async(o,e)=>{let t=parseInt(o.job,10);if(!Number.isSafeInteger(t))throw Error(`The ${o.job} is not valid key for job`);let r=await g(w(e)),s=await f(r,c=>d(c.apiRoot,k).getByName(c.token,r.workspace)),i=await f(r,c=>d(c.apiRoot,T).getByLocalId(c.token,s.id,t)),l=await f(r,c=>d(c.apiRoot,S).getAll(c.token,s.id,i.id));if(o.json){n.just(JSON.stringify(l,null,4));return}let p=[["Sequence","Status","Cluster","User","Type","Waiting","Running"]];l.forEach(c=>p.push([c.sequence??"",c.status??"",c.clusterName??"",c.userFullName??"",c.type??"",M(c.created,c.runningAt),c.runningAt?M(c.runningAt,c.completedAt):"N/A"])),n.just(Io(p,{drawHorizontalLine:()=>!1,drawVerticalLine:()=>!1}))}));var kt=Ct;import Uo from"chalk";var ie=Po("jobs").aliases(["job"]);ie.addCommand(yt);ie.addCommand(kt);ie.description("List available jobs").option("--page <number>","The page number","0").option("--size <number>","The size of the result","10").option("--status <string>","The status to filter","").option("--scope <string>","The scope to filter","").option("--all","Consider all jobs in the workspace","true").action(h(async(o,e)=>{let t=await g(w(e)),r=await f(t,y=>d(y.apiRoot,k).getByName(y.token,t.workspace)),s=o.page??0,i=o.size??20,l=o.all,a=o.status,p=o.scope,c={status:a,scope:p,all:l,page:s,size:i},{items:R,totalCount:x}=await f(t,y=>d(y.apiRoot,T).getBy(y.token,r.id,c)),m=[["Job","Name","Status","Cluster","User","Completed","Scope","Waiting","Running"]];R.forEach(y=>m.push([y.localId??"",y.name?y.name:Uo.italic("No name"),y.status??"",y.clusterName??"",y.userFullName??"",`${y.completedTasks} / ${y.totalTasks}`,y.scope,M(y.created,y.runningAt),y.runningAt?M(y.runningAt,y.completedAt):"N/A"])),n.just(Lo(m,{drawHorizontalLine:()=>!1,drawVerticalLine:()=>!1})),n.just(`Current page ${s}: ${R.length} jobs out of ${x}`)}));var bt=ie;import{createCommand as Wo}from"commander";import ne from"fs";import Ce from"path";import*as xt from"yaml";import{promises as ke}from"fs";import{glob as _o}from"glob";import $t from"crypto";import Mo from"os";import No from"archiver";var Fo=["**/.git/**","**/node_modules/**","**/run.shoc.yaml","**/run.shoc.yml"],Do="shoc-temp",Oo=16;async function Yo(o){let e=Ce.resolve(o.dir,o.buildFile||L);if(!ne.existsSync(e))throw Error(`The build file ${e} could not be found!`);let t=await ke.stat(e);return{fullPath:e,modified:t.mtime.getTime(),size:t.size}}async function Rt(o){let e=await Yo(o),t=await ke.readFile(e.fullPath,"utf8");return{manifest:xt.parse(t),buildFile:e}}async function Tt(o,e){let t=await _o("**/*",{ignore:[...Fo,...e.ignore||[]],cwd:o.dir,nodir:!0,withFileTypes:!0}),r=[];for(let s of t){let i=await ke.stat(s.fullpath());r.push({fullPath:s.fullpath(),localPath:s.relative(),modified:i.mtime.getTime(),size:i.size})}return r.sort((s,i)=>s.localPath.localeCompare(i.localPath)),{files:r}}function vt(o,e){let t=[];return t.push(`__BUILD_FILE__:${o.size}:${o.modified}`),e.forEach(r=>t.push(`${r.localPath}:${r.size}:${r.modified}`)),$t.createHash("sha256").update(t.join(",")).digest("hex")}function zo(){let o=Ce.join(Mo.tmpdir(),Do);return ne.existsSync(o)||ne.mkdirSync(o,{recursive:!0}),o}async function Bt(o){return new Promise((e,t)=>{let r=`${$t.randomBytes(Oo).toString("hex")}.zip`,s=Ce.join(zo(),r),i=ne.createWriteStream(s),l=No("zip",{zlib:{level:9}});i.on("close",()=>{e(s)}),l.on("error",a=>{t(a)}),l.pipe(i),o.forEach(a=>{l.file(a.fullPath,{name:a.localPath})}),l.finalize()})}var N=class extends C{constructor(e){super("shoc-package",e)}getAll(e,t){let r=this.urlify({api:`api/workspaces/${t}/build-tasks`});return this.webClient.get(r,{headers:{...this.authBearer(e)}})}create(e,t,r){let s=this.urlify({api:`api/workspaces/${t}/build-tasks`});return this.webClient.post(s,r,{headers:{...this.authBearer(e)}})}uploadBundleById(e,t,r,s){let i=this.urlify({api:`api/workspaces/${t}/build-tasks/${r}/bundle`});return this.webClient.put(i,s,{headers:{...this.authBearer(e),"Content-Type":"multipart/form-data"},timeout:2*60*60*1e3,maxBodyLength:1/0})}};import{fileFromPath as Go}from"formdata-node/file-from-path";import $ from"chalk";var G=class extends C{constructor(e){super("shoc-package",e)}fromCache(e,t,r){let s=this.urlify({api:`api/workspaces/${t}/packages/from-cache`});return this.webClient.post(s,r,{headers:{...this.authBearer(e)},timeout:2*60*60*1e3})}};import ae,{oraPromise as j}from"ora";async function H(o,e){e.authenticatedContext??await j(U(o),{text:"Validating user authentication",successText:m=>`\u{1F511} Authenticated by ${$.bold(m.session.name)} at ${$.bold(e.workspace)}`,failText:m=>`Could not authenticate: ${$.red(m.message)}`}).catch(()=>process.exit(1)),ae().succeed(`\u{1F4C1} Using ${$.bold(e.dir)} as a package directory`),ae().succeed(`\u{1F441}\uFE0F Packaing with scope ${$.bold(e.scope)}`);let{buildFile:t,manifest:r}=await j(Rt(e),{successText:m=>`\u{1F4C4} Detected build manifest at ${$.bold(m.buildFile.fullPath)}`,failText:m=>`Build manifest could not be found: ${$.red(m.message)}`}).catch(()=>process.exit(1)),{files:s}=await j(Tt(e,r),{successText:m=>`\u{1F5C3}\uFE0F Detected ${$.bold(m.files.length)} files to package`,failText:m=>`Could not fetch the list of files to package: ${$.red(m.message)}`}).catch(()=>process.exit(1)),i=vt(t,s);ae().succeed(`\u{1F522} Computed checksum of the package ${$.bold(i)}`);let{id:l}=e.workspaceReference??await j(f(o,m=>d(m.apiRoot,k).getByName(m.token,e.workspace)),{text:`Validating workspace ${$.bold(e.workspace)}`,successText:m=>`\u{1F30E} Workspace ${$.bold(m.name)} is valid`,failText:m=>`The workspace ${$.bold(e.workspace)} is not valid: ${$.red(m.message)}`}).catch(()=>process.exit(1)),a=await j(Ho(o,{workspaceId:l,scope:e.scope,listingChecksum:i}),{text:"Checking if the package could be restored from cache",successText:m=>m?"\u{1F4E6} The package has been already built":"\u2139\uFE0F The package was not cached",failText:m=>`Could not perform the cache check: ${$.red(m.message)}`}).catch(()=>process.exit(1));if(a)return{packageId:a.packageId};let p={workspaceId:l,provider:"remote",scope:e.scope,listingChecksum:i,manifest:JSON.stringify(r)},c=await j(f(o,m=>d(m.apiRoot,N).create(m.token,l,p)),{text:"Starting a build process for the package",successText:m=>`\u{1F528} Build process is successfully initiated with reference ${$.bold(m.id)}`,failText:m=>`Could not initiate the build process: ${$.red(m.message)}`}),R=await Bt(s),x=new FormData;x.append("file",await Go(R));let u=await j(f(o,m=>d(m.apiRoot,N).uploadBundleById(m.token,l,c.id,x)),{text:"Uploading the package bundle to build",successText:m=>`\u{1F4E6} The package was successfully built with reference ${$.bold(m.packageId)}`,failText:m=>`Could not upload and build the package: ${$.red(m.message)}`});return u.status==="failed"&&(ae().fail(`The package build failed: ${u.message} (${u.errorCode})`),process.exit(1)),{packageId:u.packageId}}async function Ho(o,e){try{return{packageId:(await f(o,r=>d(r.apiRoot,G).fromCache(r.token,e.workspaceId,e))).id}}catch(t){if(t instanceof _&&t.kind==="not_found")return null}return null}var Et=Wo("build");Et.description("Build a package from the selected directory").option("-f, --build-file <buildFile>","The name of the build file to use").option("--scope <scope>","The scope of the build (user or workspace)","workspace").action(h(async(o,e)=>{let t=await g(w(e)),r=Vo(t,o),s=await H(t,r)}));function Vo(o,e){return{workspace:o.workspace,dir:o.dir,buildFile:e.buildFile,scope:e.scope}}var At=Et;import{createCommand as sr}from"commander";import b from"chalk";import{oraPromise as I}from"ora";var W=class extends C{constructor(e){super("shoc-job",e)}getAll(e,t){let r=this.urlify({api:`api/workspaces/${t}/git-repos`});return this.webClient.get(r,{headers:{...this.authBearer(e)}})}create(e,t,r){let s=this.urlify({api:`api/workspaces/${t}/git-repos`});return this.webClient.post(s,r,{headers:{...this.authBearer(e)}})}ensure(e,t,r){let s=this.urlify({api:`api/workspaces/${t}/git-repos/ensure`});return this.webClient.post(s,r,{headers:{...this.authBearer(e)}})}};import be from"isomorphic-git";import xe from"fs";import Ko from"git-url-parse";async function St(o){try{return await qo(o)}catch{return null}}async function qo(o){let e=await be.findRoot({fs:xe,filepath:o}),t=await be.listRemotes({fs:xe,dir:e});if(t.length===0)return null;let r=t.filter(l=>l.remote==="origin")[0];r||(r=t[0]);let s=await be.currentBranch({fs:xe,dir:e,fullname:!1}),i=Ko(r.url);return{remote:r.remote,url:r.url,branch:s,source:i.source,owner:i.owner,name:i.name,repo:i.full_name}}import Jo from"fs";import*as jt from"yaml";import{promises as Xo}from"fs";import Zo from"path";var V=class extends C{constructor(e){super("shoc-job",e)}getAll(e,t){let r=this.urlify({api:`api/workspaces/${t}/labels`});return this.webClient.get(r,{headers:{...this.authBearer(e)}})}create(e,t,r){let s=this.urlify({api:`api/workspaces/${t}/labels`});return this.webClient.post(s,r,{headers:{...this.authBearer(e)}})}ensure(e,t,r){let s=this.urlify({api:`api/workspaces/${t}/labels/ensure`});return this.webClient.post(s,r,{headers:{...this.authBearer(e)}})}};async function It(o){let e=Zo.resolve(o.dir,o.runFile||ve);if(!Jo.existsSync(e))throw Error(`The run file ${e} could not be found!`);let t=await Xo.readFile(e,"utf8");return{manifest:jt.parse(t),runFile:e}}async function Pt(o,e,t,r){let s=await St(e.dir),i=s?await Qo(o,t,s):null,l=!r.labels||r.labels.length===0?[]:await er(o,t,r.labels);return{gitRepoId:i,labelIds:l}}async function Qo(o,e,t){let r={workspaceId:e,name:t.name,owner:t.owner,source:t.source,repository:t.repo,remoteUrl:t.url};try{return(await f(o,i=>d(i.apiRoot,W).ensure(i.token,e,r))).id}catch{return null}}async function er(o,e,t){let r={workspaceId:e,names:t};try{return(await f(o,i=>d(i.apiRoot,V).ensure(i.token,e,r))).map(i=>i.id)}catch(s){throw Error(`Could not resolve labels ([${t.join(", ")}]): ${s?.message||"Unknown error"}`)}}var F;(l=>{let o=new RegExp(/^([\d.]+)([KMGTPE]i?B?)$/,"i"),e={Ki:1024,Mi:1024**2,Gi:1024**3,Ti:1024**4,Pi:1024**5,Ei:1024**6},t={K:1e3,M:1e3**2,G:1e3**3,T:1e3**4,P:1e3**5,E:1e3**6};function r(a){if(!a||!a.trim())return null;let p=a.trim(),c=p.toLowerCase();if(c.endsWith("m")){let x=parseInt(p.slice(0,-1),10);if(isNaN(x))throw new Error(`The ${a} is not a valid value for CPU.`);return x}if(c.endsWith("n")){let x=parseInt(p.slice(0,-1),10);if(isNaN(x))throw new Error(`The ${a} is not a valid value for CPU.`);return Math.round(x/1e6)}if(c.endsWith("u")){let x=parseInt(p.slice(0,-1),10);if(isNaN(x))throw new Error(`The ${a} is not a valid value for CPU.`);return Math.round(x/1e3)}let R=parseFloat(p);if(!isNaN(R))return Math.round(R*1e3);throw new Error(`The ${a} is not a valid value for CPU.`)}l.parseToMillicores=r;function s(a){if(!a||!a.trim())return null;let p=a.trim(),c=parseInt(p,10);if(!isNaN(c)&&String(c)===p)return c;let R=p.match(o);if(!R){let y=parseFloat(p);if(!isNaN(y))return Math.round(y);throw new Error(`The ${a} is not a valid value for Memory.`)}let x=parseFloat(R[1]),u=R[2];if(u.endsWith("i")){let y=e[u];if(y)return Math.round(x*y)}let m=u.replace("B","");if(t[m]){let y=t[m];return Math.round(x*y)}throw new Error(`The ${a} has an unknown suffix.`)}l.parseToBytes=s;function i(a){if(!a||!a.trim())return null;let p=a.trim(),c=parseInt(p,10);if(!isNaN(c)&&String(c)===p)return c;throw new Error(`The ${a} is not a valid value for GPU.`)}l.parseToGpu=i})(F||={});function Lt(o){return o??[]}function Ut(o){return o?{replicas:o?.replicas??null,indexer:o?.indexer??null,counter:o?.counter??null}:null}function _t(o){return o?{use:o?.use??[],override:o?.override??{}}:null}function le(o){return o?{cpu:F.parseToMillicores(ce(o?.cpu??null)),memory:F.parseToBytes(ce(o?.memory??null)),nvidiaGpu:F.parseToGpu(ce(o?.nvidiaGpu??null)),amdGpu:F.parseToGpu(ce(o?.amdGpu??null))}:null}function tr(o){return o?{dedicated:o?.dedicated,resources:le(o?.resources)}:null}function or(o){return o?{replicas:o?.replicas,resources:le(o?.resources)}:null}function rr(o){return{launcher:tr(o?.launcher),workers:or(o?.workers)}}function Mt(o){let e={};return o?.mpi&&(e={...e,mpi:rr(o.mpi)}),e}function ce(o){return o==null||typeof o>"u"?null:typeof o=="bigint"||typeof o=="number"||typeof o=="boolean"?String(o):typeof o=="string"?o:typeof o=="symbol"?o.toString():null}async function $e(o,e){let t=await I(U(o),{text:"Validating user authentication",successText:u=>`\u{1F511} Authenticated by ${b.bold(u.session.name)} at ${b.bold(e.workspace)}`,failText:u=>`Could not authenticate: ${b.red(u.message)}`}).catch(()=>process.exit(1)),{id:r}=await I(f(o,u=>d(u.apiRoot,k).getByName(u.token,e.workspace)),{text:`Validating workspace ${b.bold(e.workspace)}`,successText:u=>`\u{1F30E} Workspace ${b.bold(u.name)} is valid`,failText:u=>`The workspace ${b.bold(e.workspace)} is not valid: ${b.red(u.message)}`}).catch(()=>process.exit(1)),{manifest:s}=await I(It(e),{successText:u=>`\u{1F4C4} Detected build manifest at ${b.bold(u.runFile)}`,failText:u=>`Build manifest could not be found: ${b.red(u.message)}`}).catch(()=>process.exit(1)),{gitRepoId:i,labelIds:l}=await I(Pt(o,e,r,s),{successText:u=>"\u2139\uFE0F Initialization completed successfully",failText:u=>`Initialization failed: ${b.red(u.message)}`}).catch(()=>process.exit(1)),{packageId:a}=await H(o,{workspace:e.workspace,dir:e.dir,buildFile:e.buildFile,scope:e.scope,workspaceReference:{id:r},authenticatedContext:t}),{id:p}=await I(f(o,u=>d(u.apiRoot,E).getByName(u.token,r,s.cluster)),{text:`Ensuring cluster ${b.bold(s.cluster)} exists`,successText:u=>`\u{1F50C} Cluster ${b.bold(u.name)} exists`,failText:u=>`The cluster ${b.bold(s.cluster)} could not be found: ${b.red(u.message)}`}).catch(()=>process.exit(1)),c={workspaceId:r,scope:e.scope,manifest:{name:s.name??"",description:s.description??"",gitRepoId:i,labelIds:l??[],clusterId:p,packageId:a,args:Lt(s.args),array:Ut(s.array),env:_t(s.env),resources:le(s.resources),spec:Mt(s.spec)}},R=await I(f(o,u=>d(u.apiRoot,T).create(u.token,r,c)),{text:"Initializing a new job in the system",successText:u=>`\u231B Job (${b.bold(u.localId)}) in workspace ${b.bold(e.workspace)} was successfully created`,failText:u=>`Could not initialize a job with the given manifest: ${b.red(u.message)}`}),x=await I(async()=>{let u=await f(o,m=>d(m.apiRoot,T).submit(m.token,r,R.id,c));if(u.status==="failed")throw Error(`Reason: ${u.message||"unknown"}`);return u},{text:"Submitting job to the cluster",successText:u=>`\u2705\uFE0F Job ${b.bold(u.localId)} is submitted to the cluster ${b.bold(s.cluster)}`,failText:u=>`Could not submit the job to the cluster: ${b.red(u.message)}`});return{}}var Nt=sr("run");Nt.description("Submit a new job according to run manifest").option("--build-file <buildFile>","The name of the build manifest file to use").option("--run-file <runFile>","The name of the run manifest file to use").option("--scope <scope>","The scope of the build (user or workspace)","workspace").action(h(async(o,e)=>{let t=await g(w(e)),r=ir(t,o),s=await $e(t,r)}));function ir(o,e){return{workspace:o.workspace,dir:o.dir,buildFile:e.buildFile,scope:e.scope,runFile:e.runFile}}var Ft=Nt;import{createCommand as nr}from"commander";var Dt=nr("logs");Dt.description("Watch logs of the job tasks").requiredOption("-j, --job <number>","The job number").option("-t, --task <number>","The task number","0").action(h(async(o,e)=>{let t=await g(w(e)),r=await f(t,c=>d(c.apiRoot,k).getByName(c.token,t.workspace)),s=parseInt(o.job,10),i=o.task??0;if(!Number.isSafeInteger(s))throw Error(`The ${o.job} is not valid key for job`);let l=await f(t,c=>d(c.apiRoot,T).getByLocalId(c.token,r.id,s)),{url:a,token:p}=await f(t,c=>d(c.apiRoot,S).getLogsBySequenceUrl(c.token,r.id,l.id,i));try{await ar(a,p)}catch(c){n.error(`Unable to read logs: ${c.message}`)}}));async function ar(o,e){let t=await fetch(o,{headers:{"x-shoc-sse":"yes",Authorization:`Bearer ${e}`}});if(!t.ok){if(t.status===404)throw Error("The object is was not found");if(t.status===403)throw Error("No enough permissions for the operation");let{errors:i}=await t.json()??{errors:[]};throw Error(i.length>0?`${i[0].code} ${i[0]?.message}`:"Unknown error")}if(!t.body)return;let r=t.body.getReader(),s=new TextDecoder;for(;;){let{done:i,value:l}=await r.read();if(i)break;let p=s.decode(l,{stream:!0}).split(` `);for(let c of p)c.startsWith("data: ")&&n.just(c.slice(6))}}var Ot=Dt;var v=cr();v.name("shoc").description("A command-line interface for managing your job on Shoc Platform").version(Re,"-v, --version").option("-c, --context <context>","Use the mentioned context").option("-w, --workspace <workspace>","Use the mentioned workspace").option("-d, --dir <dir>","Use the given working directory").enablePositionalOptions();v.addCommand(At);v.addCommand(Ft);v.addCommand(Ie);v.addCommand(Ge);v.addCommand(bt);v.addCommand(Ot);v.addCommand(ft);v.addCommand(Je);v.addCommand(et);v.addCommand(st);v.addCommand(ct);var Yt=v;import lr from"axios";import ur from"@dotenvx/dotenvx";import pr from"https";import{Agent as mr,setGlobalDispatcher as dr}from"undici";var fr=!0;function zt(){if(ur.config({convention:"nextjs",quiet:!0}),process.env.ALLOW_INSECURE==="yes"||fr){let o=new pr.Agent({rejectUnauthorized:!1});lr.defaults.httpsAgent=o;let e=new mr({connect:{rejectUnauthorized:!1}});dr(e)}}zt();try{Yt.parse(process.argv)}catch(o){n.error(o.message||"Command failed due to an unexpected error"),process.exit(1)} //# sourceMappingURL=index.js.map