@r5n/hydra 0.3.5 → 0.3.6

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 (2) hide show
  1. package/dist/cli.js +1 -1
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -40,7 +40,7 @@ ${g.default.gray(sn)} ${t}
40
40
  `);let $=0,k=0;d(),s=setInterval(()=>{if(e&&a===c)return;w(),c=a;let B=g.default.magenta(r[$]);if(e)process.stdout.write(`${B} ${a}...`);else if(t==="timer")process.stdout.write(`${B} ${a} ${U(l)}`);else{let J=".".repeat(Math.floor(k)).slice(0,3);process.stdout.write(`${B} ${a}${J}`)}$=$+1<r.length?$+1:0,k=k<r.length?k+0.125:0},o)},L=(y="",$=0)=>{n=!1,clearInterval(s),w();let k=$===0?g.default.green(Bn):$===1?g.default.red(dt):g.default.red(bt);a=T(y??a),t==="timer"?process.stdout.write(`${k} ${a} ${U(l)}
41
41
  `):process.stdout.write(`${k} ${a}
42
42
  `),u(),i()};return{start:b,stop:L,message:(y="")=>{a=T(y??a)}}},Et=async(t,r)=>{let o={},e=Object.keys(t);for(let i of e){let s=t[i],n=await s({results:o})?.catch((a)=>{throw a});if(typeof r?.onCancel=="function"&&C(n)){o[i]="canceled",r.onCancel({results:o});continue}o[i]=n}return o};var Xr=!process.env.NO_COLOR&&(process.env.FORCE_COLOR||process.stdout.isTTY),_r=["blue","cyan","gray","green","magenta","red","white","yellow"],Ar=["bold","dim","italic","underline","reset"],Ot={bold:"\x1B[1m",dim:"\x1B[2m",italic:"\x1B[3m",reset:"\x1B[0m",underline:"\x1B[4m"},Ur={"\x1B[30m":"\x1B[90m","\x1B[31m":"\x1B[91m","\x1B[32m":"\x1B[92m","\x1B[33m":"\x1B[93m","\x1B[34m":"\x1B[94m","\x1B[35m":"\x1B[95m","\x1B[36m":"\x1B[96m","\x1B[37m":"\x1B[97m"};function jn(t){return(r)=>Xr?`${t}${r}${Ot.reset}`:r}function Rt({bright:t=!1,color:r}){let o=Bun.color(r,"ansi");if(!o)return"";return t?Ur[o]||o:o}var qr=_r.reduce((t,r)=>{return t[r]=jn(Rt({color:r})),t[`${r}Bright`]=jn(Rt({bright:!0,color:r})),t},{}),Hr=Ar.reduce((t,r)=>{return t[r]=jn(Ot[r]),t},{}),h={...qr,...Hr};var R=({prefix:t="",level:r=process.env.DEBUG?0:1}={})=>{function o(...a){if(r<=0){let c=a.join(" ");console.log($n({level:h.dim("DEBUG"),message:c,prefix:t}))}}function e(...a){if(r<=1){let c=a.join(" ");console.info($n({level:h.cyan("INFO "),message:c,prefix:t}))}}function i(...a){if(r<=2){let c=a.join(" ");console.warn($n({level:h.yellow("WARN "),message:c,prefix:t}))}}function s(...a){if(r<=3){let c=a.join(" ");console.error($n({level:h.red("ERROR"),message:c,prefix:t}))}}function n(a){return R({level:r,prefix:`${t}:${a}`})}return{child:n,debug:o,error:s,info:e,warn:i}},Wn=R();function $n({prefix:t,level:r,message:o}){let e=new Date().toISOString().split("T")[1]?.split(".")[0]||"",i=t?`[${t}] `:"";return`${h.dim(e||"")} ${r} ${i}${o}`}var de=R({prefix:"cli-core"}).child("error");var I=new Uint32Array(65536),Lr=(t,r)=>{let o=t.length,e=r.length,i=1<<o-1,s=-1,n=0,a=o,c=o;while(c--)I[t.charCodeAt(c)]|=1<<c;for(c=0;c<e;c++){let l=I[r.charCodeAt(c)],f=l|n;if(l|=(l&s)+s^s,n|=~(l|s),s&=l,n&i)a++;if(s&i)a--;n=n<<1|1,s=s<<1|~(f|n),n&=f}c=o;while(c--)I[t.charCodeAt(c)]=0;return a},Br=(t,r)=>{let o=r.length,e=t.length,i=[],s=[],n=Math.ceil(o/32),a=Math.ceil(e/32);for(let u=0;u<n;u++)s[u]=-1,i[u]=0;let c=0;for(;c<a-1;c++){let u=0,w=-1,T=c*32,U=Math.min(32,e)+T;for(let b=T;b<U;b++)I[t.charCodeAt(b)]|=1<<b;for(let b=0;b<o;b++){let L=I[r.charCodeAt(b)],y=s[b/32|0]>>>b&1,$=i[b/32|0]>>>b&1,k=L|u,B=((L|$)&w)+w^w|L|$,J=u|~(B|w),pn=w&B;if(J>>>31^y)s[b/32|0]^=1<<b;if(pn>>>31^$)i[b/32|0]^=1<<b;J=J<<1|y,pn=pn<<1|$,w=pn|~(k|J),u=J&k}for(let b=T;b<U;b++)I[t.charCodeAt(b)]=0}let l=0,f=-1,m=c*32,p=Math.min(32,e-m)+m;for(let u=m;u<p;u++)I[t.charCodeAt(u)]|=1<<u;let d=e;for(let u=0;u<o;u++){let w=I[r.charCodeAt(u)],T=s[u/32|0]>>>u&1,U=i[u/32|0]>>>u&1,b=w|l,L=((w|U)&f)+f^f|w|U,y=l|~(L|f),$=f&L;if(d+=y>>>e-1&1,d-=$>>>e-1&1,y>>>31^T)s[u/32|0]^=1<<u;if($>>>31^U)i[u/32|0]^=1<<u;y=y<<1|T,$=$<<1|U,f=$|~(b|y),l=y&b}for(let u=m;u<p;u++)I[t.charCodeAt(u)]=0;return d},St=(t,r)=>{if(t.length<r.length){let o=r;r=t,t=o}if(r.length===0)return t.length;if(t.length<=32)return Lr(t,r);return Br(t,r)};function Ct(t,r,o=3){return r.map((s)=>({distance:St(t.toLowerCase(),s.toLowerCase()),option:s})).filter(({distance:s})=>s<=Math.max(3,t.length/2)).sort((s,n)=>s.distance-n.distance).slice(0,o).map(({option:s})=>s)}function Tt(t,r){return r[t.toLowerCase()]||null}var kt=R({prefix:"cli-core"}).child("graceful");async function nn(t,r={}){let{fallback:o,logLevel:e="warn",message:i="Operation failed gracefully"}=r;try{return await t()}catch(s){let n=s instanceof Error?s.message:String(s);if(kt[e](`${i}: ${n}`),o)try{await o()}catch(a){kt.error(`Fallback also failed: ${a instanceof Error?a.message:String(a)}`)}return}}var Ir={backoffMultiplier:2,initialDelay:1000,maxAttempts:3,maxDelay:30000,onRetry:(t,r)=>{Wn.warn(`Retry attempt ${t}: ${r.message}`)},retryableErrors:["ECONNREFUSED","ECONNRESET","ETIMEDOUT","ENOTFOUND","rate limit","429","503","502","504","npm ERR!","EPUBLISHCONFLICT"]};async function an(t,r={}){let o={...Ir,...r},e=new Error("Operation failed");for(let i=1;i<=o.maxAttempts;i++)try{return await t()}catch(s){if(e=s instanceof Error?s:new Error(String(s)),!o.retryableErrors.some((c)=>e.message.toLowerCase().includes(c.toLowerCase()))||i===o.maxAttempts)throw e;o.onRetry(i,e);let a=Math.min(o.initialDelay*o.backoffMultiplier**(i-1),o.maxDelay);await new Promise((c)=>setTimeout(c,a))}throw e}var q=(t)=>{return t.map=(r)=>q((o)=>r(t(o))),t.or=(r)=>q((o)=>{try{return t(o)}catch(e){return r(o)}}),t},wn=()=>"bad banditype"();var Nt=(t)=>q((r)=>t.includes(r)?r:wn()),En=(t)=>q((r)=>typeof r===typeof t?r:wn()),cn=()=>En(""),vt=()=>En(0),Pt=()=>En(!0);var Gt=()=>En();var Mt=(t)=>q((r)=>r instanceof t?r:wn()),zn=(t)=>Mt(Object).map((r)=>{let o={};for(let e in r){let i=t(r[e]);i!==void 0&&(o[e]=i)}return o}),tn=(t)=>Mt(Object).map((r)=>{let o={};for(let e in t){let i=t[e](r[e]);i!==void 0&&(o[e]=i)}return o});function jr(t,r){return q((o)=>{try{return t(o)}catch{try{return r(o)}catch{throw new Error("No matching schema in union")}}})}function Y(t,r){return(o)=>{if(o===void 0)return r;return t(o)}}function Xt(){return q((t)=>{let r=vt()(t);if(!Number.isInteger(r)||r<=0)throw new Error("Must be a positive integer");return r})}function _t(){return q((t)=>{let r=cn()(t);try{return new URL(r),r}catch{throw new Error("Must be a valid URL")}})}function At(t,r){return q((o)=>{let e=cn()(o);if(!t.test(e))throw new Error(r||`Must match pattern ${t}`);return e})}function fn(t){return{parse(r){try{return t(r)}catch(o){throw new Error(o instanceof Error?o.message:"Validation failed")}},safeParse(r){try{return{data:t(r),success:!0}}catch(o){return{error:o instanceof Error?o:new Error("Validation failed"),success:!1}}},validator:t}}function Q(){return jr(Gt(),cn())}function Z(t){return Y(cn(),t)}function rn(t){return Y(Pt(),t)}function Rn(t,r){return Y(Nt(t),r)}import{parseArgs as to}from"util";var{$:M}=globalThis.Bun;import{join as Lt}from"path";var{$:Ht}=globalThis.Bun;import{tmpdir as Wr}from"os";var Ut={MACHINES_DIR:"machines",TEMP_DIR:Wr()};var zr=tn({directory:Z("./gh-runners"),labels:Q(),name:Z("runner"),numberOfMachines:Y(Xt(),1),os:Rn(["osx","linux","windows"],"osx"),overwrite:rn(!1),run:rn(!1),runnerGroup:Q(),url:Q()}),Kr=fn(zr),qt=fn(tn({defaultProfile:Q(),profiles:zn(Kr.validator)}));var Jr=["hydra.json",".hydra.json","hydra.toml",".hydra.toml"];async function Yr(t=process.cwd()){for(let r of Jr){let o=Lt(t,r);if(await Bun.file(o).exists())return o}return null}async function z(t){let r=t?.configPath||await Yr();if(!r)return null;let o=Bun.file(r);if(!await o.exists())return null;try{let e=await o.text(),i=r.endsWith(".toml")?await Qr(e):JSON.parse(e);return qt.parse(i)}catch(e){if(e instanceof Error)throw new Error(`Invalid config file: ${e.message}`);throw e}}async function K(t,r){if(!t)return null;let o=r||t.defaultProfile;if(!o)return null;return t.profiles[o]||null}async function Qr(t){let r=Lt(Ut.TEMP_DIR,`hydra-config-${Date.now()}.toml`);await Bun.write(r,t);try{let o=await import(r);return await Ht`rm -f ${r}`.quiet(),o.default}catch(o){throw await Ht`rm -f ${r}`.quiet(),new Error(`Failed to parse TOML configuration: ${o}`)}}function mn(){let t=import.meta.path,r=t.substring(0,t.lastIndexOf("/"));return r.substring(0,r.lastIndexOf("/"))}var Zr=tn({directory:Z(`${process.cwd()}/gh-runners`),labels:Q(),name:Z("runner"),numberOfMachines:Y(At(/^\d+$/,"Number of machines must be a positive integer"),"1"),os:Rn(["osx","linux","windows"],"osx"),overwrite:rn(!1),run:rn(!1),token:Z(process.env.GITHUB_PERSONAL_ACCESS_TOKEN??""),url:_t()}),Ye=fn(Zr);var V=R({prefix:"WorkerManager"}),P=new Set,Bt=!1;function Vr(){if(Bt)return;Bt=!0;let t=()=>{if(P.size>0)V.info("Cleaning up workers...");It()};process.on("exit",t),process.on("SIGINT",t),process.on("SIGTERM",t),process.on("uncaughtException",(r)=>{V.error("Uncaught exception:",r),t(),process.exit(1)})}function Fr(t){P.add(t),V.debug(`Worker added. Total workers: ${P.size}`),t.addEventListener("exit",()=>{P.delete(t),V.debug(`Worker exited. Remaining workers: ${P.size}`)})}function Dr(t){if(P.has(t))t.terminate(),P.delete(t),V.debug(`Worker removed. Remaining workers: ${P.size}`)}function It(){if(P.size>0){V.info(`Terminating ${P.size} workers...`);for(let t of P)try{t.terminate()}catch(r){V.error("Failed to terminate worker:",r)}P.clear()}}function no(){return P.size}Vr();var On={add:Fr,remove:Dr,get size(){return no()},terminateAll:It};var G=R({prefix:"createMachines"});async function jt(){let t=await oo(),{overwrite:r,directory:o,numberOfMachines:e,os:i,run:s,name:n,labels:a,runnerGroup:c}=t,{token:l,url:f}=t;if(!f)throw new Error("URL (-u) parameter is required");if(!l){let d=ro(f),u=await W({message:`No token provided. Would you like to open GitHub to get a runner token?
43
- ${d}`});if(C(u)||!u)throw new Error("Token is required to create runners");G.info("Opening GitHub runner settings..."),await M`open ${d}`.quiet();let w=await v({message:"Enter the runner registration token:",placeholder:"AXXXXXXXXXXXXXXXXXXXXXXXXX",validate:(T)=>{if(!T||T.trim().length===0)return"Token is required";return}});if(C(w))throw new Error("Token is required to create runners");l=w.trim()}if(G.info(`Creating ${e} runners in ${o}`),G.debug(`OS: ${i}, Name: ${n}, Labels: ${a||"none"}`),await Bun.file(o).exists())if(r)G.warn(`Directory ${o} exists, removing...`),await M`rm -rf ${o}`;else throw new Error(`Directory ${o} already exists`);let p=await eo({directory:o,os:i});if(await io({directory:o,numberOfMachines:e,overwrite:r,runnerBase:p}),!s){G.info("Runners created but not started");return}so({directory:o,labels:a,name:n,numberOfMachines:e,runnerGroup:c,token:l,url:f})}function ro(t){try{let r=new URL(t),o=r.pathname.split("/").filter(Boolean);if(r.hostname==="github.com"||r.hostname==="www.github.com"){if(o[0]==="orgs"&&o[1])return`https://github.com/organizations/${o[1]}/settings/actions/runners/new`;else if(o.length>=2)return`https://github.com/${o[0]}/${o[1]}/settings/actions/runners/new`}else if(o[0]==="orgs"&&o[1])return`${r.origin}/organizations/${o[1]}/settings/actions/runners/new`;else if(o.length>=2)return`${r.origin}/${o[0]}/${o[1]}/settings/actions/runners/new`;return t}catch{return t}}async function oo(){let{values:t}=to({allowPositionals:!0,args:Bun.argv,options:{config:{short:"c",type:"string"},directory:{default:`${process.cwd()}/gh-runners`,short:"d",type:"string"},labels:{short:"l",type:"string"},name:{default:"runner",short:"n",type:"string"},numberOfMachines:{default:"1",short:"m",type:"string"},os:{default:"osx",short:"s",type:"string"},overwrite:{default:!1,short:"f",type:"boolean"},profile:{short:"p",type:"string"},run:{default:!1,short:"r",type:"boolean"},runnerGroup:{short:"g",type:"string"},token:{short:"t",type:"string"},url:{short:"u",type:"string"}},strict:!0}),r=await z({configPath:t.config}),o=await K(r,t.profile),e={directory:t.directory,labels:t.labels,name:t.name,numberOfMachines:t.numberOfMachines,os:t.os,overwrite:t.overwrite,run:t.run,runnerGroup:t.runnerGroup,token:t.token,url:t.url};if(o)return{directory:e.directory??o.directory??`${process.cwd()}/gh-runners`,labels:e.labels??o.labels,name:e.name??o.name??"runner",numberOfMachines:e.numberOfMachines??String(o.numberOfMachines)??"1",os:e.os??o.os??"osx",overwrite:e.overwrite??o.overwrite??!1,run:e.run??o.run??!1,runnerGroup:e.runnerGroup??o.runnerGroup,token:e.token,url:e.url??o.url};return{directory:e.directory,labels:e.labels,name:e.name,numberOfMachines:e.numberOfMachines,os:e.os,overwrite:e.overwrite,run:e.run,runnerGroup:e.runnerGroup,token:e.token,url:e.url}}async function eo({directory:t,os:r}){let o=G.child("download");o.info(`Fetching latest runner version for ${r}...`);let e=await an(async()=>(await M`curl -Ls -o /dev/null -w "%{url_effective}" https://github.com/actions/runner/releases/latest`).text(),{onRetry:(l,f)=>{o.warn(`Retrying GitHub API call (attempt ${l}): ${f.message}`)}}),s=(await M`basename ${e}`).text().trim().replace("v",""),n=`actions-runner-${r}-x64-${s}.tar.gz`,a=`https://github.com/actions/runner/releases/download/v${s}/${n}`;if(o.debug(`Runner version: ${s}`),o.debug(`Download URL: ${a}`),await M`mkdir -p ${t}`,await M`mkdir -p ${t}/machines`,!await Bun.file(`${t}/${n}`).exists())o.info("Downloading runner image..."),await an(async()=>await M`curl -o ${t}/${n} -L ${a}`,{maxAttempts:5,onRetry:(l,f)=>{o.warn(`Retrying runner download (attempt ${l}): ${f.message}`)}}),o.info("Download complete");else o.info("Runner image already exists, skipping download");return n}async function io({overwrite:t,numberOfMachines:r,directory:o,runnerBase:e}){let i=G.child("extract"),s=Number(r),n=(()=>{try{let f=Bun.spawnSync(["nproc"]);return Number.parseInt(f.stdout.toString().trim(),10)||4}catch{try{let f=Bun.spawnSync(["sysctl","-n","hw.ncpu"]);return Number.parseInt(f.stdout.toString().trim(),10)||4}catch{return 4}}})(),a=Math.min(Math.max(2,Math.floor(n/2)),s,8);i.info(`Extracting ${s} runners with concurrency of ${a}`);let c=Array.from({length:s},(f,m)=>m+1).map((f)=>({execute:async()=>{let m=`${o}/machines/${f}`;if(await Bun.file(m).exists())if(t)i.debug(`Machine ${f} already exists, removing...`),await M`rm -rf ${m}`;else{i.debug(`Machine ${f} already exists, skipping...`);return}await M`mkdir -p ${m}`,await M`tar xzf ${e} -C ${m.replace(`${o}/`,"")}`.cwd(o);let d=`${mn()}/src/runner`;await M`cp -R ${d}/hooks ${m}`,await M`cp -R ${d}/.runner-env ${m}/.runner-env`,i.debug(`Machine ${f} extracted successfully`)},runnerIndex:f})),l=[];for(let f=0;f<c.length;f+=a){let m=c.slice(f,f+a),p=await Promise.allSettled(m.map((d)=>d.execute()));for(let[d,u]of p.entries())if(u.status==="rejected")throw i.error(`Failed to extract runner ${m[d]?.runnerIndex}: ${u.reason}`),u.reason;l.push(...p)}i.info(`Successfully extracted ${s} runners`)}function so({directory:t,numberOfMachines:r,token:o,url:e,name:i,labels:s,runnerGroup:n}){G.info(`Starting ${r} runners...`);for(let a=1;a<=Number(r);a++){let c=`${mn()}/src/runner-worker.ts`,l=`${i}-${a}`,f=new Worker(c,{name:l,ref:!0});f.addEventListener("open",()=>{G.info(`Worker ${l} started`),f.postMessage({index:a,labels:s,machineCwd:`${t}/machines/${a}`,name:i,runnerGroup:n,token:o,url:e})}),f.addEventListener("message",({data:m})=>{let p=m.type==="error"?"error":"info";G[p](`${l}: ${m.message}`)}),f.addEventListener("error",(m)=>{G.error(`Worker ${l} error:`,m)}),f.addEventListener("exit",(m)=>{G.info(`Worker ${l} exited with code ${m}`)}),On.add(f)}G.info("All workers started. Press Ctrl+C to stop all machines")}var Wt="0.3.5";var zt={create:`
43
+ ${d}`});if(C(u)||!u)throw new Error("Token is required to create runners");G.info("Opening GitHub runner settings..."),await M`open ${d}`.quiet();let w=await v({message:"Enter the runner registration token:",placeholder:"AXXXXXXXXXXXXXXXXXXXXXXXXX",validate:(T)=>{if(!T||T.trim().length===0)return"Token is required";return}});if(C(w))throw new Error("Token is required to create runners");l=w.trim()}if(G.info(`Creating ${e} runners in ${o}`),G.debug(`OS: ${i}, Name: ${n}, Labels: ${a||"none"}`),await Bun.file(o).exists())if(r)G.warn(`Directory ${o} exists, removing...`),await M`rm -rf ${o}`;else throw new Error(`Directory ${o} already exists`);let p=await eo({directory:o,os:i});if(await io({directory:o,numberOfMachines:e,overwrite:r,runnerBase:p}),!s){G.info("Runners created but not started");return}so({directory:o,labels:a,name:n,numberOfMachines:e,runnerGroup:c,token:l,url:f})}function ro(t){try{let r=new URL(t),o=r.pathname.split("/").filter(Boolean);if(r.hostname==="github.com"||r.hostname==="www.github.com"){if(o[0]==="orgs"&&o[1])return`https://github.com/organizations/${o[1]}/settings/actions/runners/new`;else if(o.length>=2)return`https://github.com/${o[0]}/${o[1]}/settings/actions/runners/new`}else if(o[0]==="orgs"&&o[1])return`${r.origin}/organizations/${o[1]}/settings/actions/runners/new`;else if(o.length>=2)return`${r.origin}/${o[0]}/${o[1]}/settings/actions/runners/new`;return t}catch{return t}}async function oo(){let{values:t}=to({allowPositionals:!0,args:Bun.argv,options:{config:{short:"c",type:"string"},directory:{default:`${process.cwd()}/gh-runners`,short:"d",type:"string"},labels:{short:"l",type:"string"},name:{default:"runner",short:"n",type:"string"},numberOfMachines:{default:"1",short:"m",type:"string"},os:{default:"osx",short:"s",type:"string"},overwrite:{default:!1,short:"f",type:"boolean"},profile:{short:"p",type:"string"},run:{default:!1,short:"r",type:"boolean"},runnerGroup:{short:"g",type:"string"},token:{short:"t",type:"string"},url:{short:"u",type:"string"}},strict:!0}),r=await z({configPath:t.config}),o=await K(r,t.profile),e={directory:t.directory,labels:t.labels,name:t.name,numberOfMachines:t.numberOfMachines,os:t.os,overwrite:t.overwrite,run:t.run,runnerGroup:t.runnerGroup,token:t.token,url:t.url};if(o)return{directory:e.directory??o.directory??`${process.cwd()}/gh-runners`,labels:e.labels??o.labels,name:e.name??o.name??"runner",numberOfMachines:e.numberOfMachines??String(o.numberOfMachines)??"1",os:e.os??o.os??"osx",overwrite:e.overwrite??o.overwrite??!1,run:e.run??o.run??!1,runnerGroup:e.runnerGroup??o.runnerGroup,token:e.token,url:e.url??o.url};return{directory:e.directory,labels:e.labels,name:e.name,numberOfMachines:e.numberOfMachines,os:e.os,overwrite:e.overwrite,run:e.run,runnerGroup:e.runnerGroup,token:e.token,url:e.url}}async function eo({directory:t,os:r}){let o=G.child("download");o.info(`Fetching latest runner version for ${r}...`);let e=await an(async()=>(await M`curl -Ls -o /dev/null -w "%{url_effective}" https://github.com/actions/runner/releases/latest`).text(),{onRetry:(l,f)=>{o.warn(`Retrying GitHub API call (attempt ${l}): ${f.message}`)}}),s=(await M`basename ${e}`).text().trim().replace("v",""),n=`actions-runner-${r}-x64-${s}.tar.gz`,a=`https://github.com/actions/runner/releases/download/v${s}/${n}`;if(o.debug(`Runner version: ${s}`),o.debug(`Download URL: ${a}`),await M`mkdir -p ${t}`,await M`mkdir -p ${t}/machines`,!await Bun.file(`${t}/${n}`).exists())o.info("Downloading runner image..."),await an(async()=>await M`curl -o ${t}/${n} -L ${a}`,{maxAttempts:5,onRetry:(l,f)=>{o.warn(`Retrying runner download (attempt ${l}): ${f.message}`)}}),o.info("Download complete");else o.info("Runner image already exists, skipping download");return n}async function io({overwrite:t,numberOfMachines:r,directory:o,runnerBase:e}){let i=G.child("extract"),s=Number(r),n=(()=>{try{let f=Bun.spawnSync(["nproc"]);return Number.parseInt(f.stdout.toString().trim(),10)||4}catch{try{let f=Bun.spawnSync(["sysctl","-n","hw.ncpu"]);return Number.parseInt(f.stdout.toString().trim(),10)||4}catch{return 4}}})(),a=Math.min(Math.max(2,Math.floor(n/2)),s,8);i.info(`Extracting ${s} runners with concurrency of ${a}`);let c=Array.from({length:s},(f,m)=>m+1).map((f)=>({execute:async()=>{let m=`${o}/machines/${f}`;if(await Bun.file(m).exists())if(t)i.debug(`Machine ${f} already exists, removing...`),await M`rm -rf ${m}`;else{i.debug(`Machine ${f} already exists, skipping...`);return}await M`mkdir -p ${m}`,await M`tar xzf ${e} -C ${m.replace(`${o}/`,"")}`.cwd(o);let d=`${mn()}/src/runner`;await M`cp -R ${d}/hooks ${m}`,await M`cp -R ${d}/.runner-env ${m}/.runner-env`,i.debug(`Machine ${f} extracted successfully`)},runnerIndex:f})),l=[];for(let f=0;f<c.length;f+=a){let m=c.slice(f,f+a),p=await Promise.allSettled(m.map((d)=>d.execute()));for(let[d,u]of p.entries())if(u.status==="rejected")throw i.error(`Failed to extract runner ${m[d]?.runnerIndex}: ${u.reason}`),u.reason;l.push(...p)}i.info(`Successfully extracted ${s} runners`)}function so({directory:t,numberOfMachines:r,token:o,url:e,name:i,labels:s,runnerGroup:n}){G.info(`Starting ${r} runners...`);for(let a=1;a<=Number(r);a++){let c=`${mn()}/src/runner-worker.ts`,l=`${i}-${a}`,f=new Worker(c,{name:l,ref:!0});f.addEventListener("open",()=>{G.info(`Worker ${l} started`),f.postMessage({index:a,labels:s,machineCwd:`${t}/machines/${a}`,name:i,runnerGroup:n,token:o,url:e})}),f.addEventListener("message",({data:m})=>{let p=m.type==="error"?"error":"info";G[p](`${l}: ${m.message}`)}),f.addEventListener("error",(m)=>{G.error(`Worker ${l} error:`,m)}),f.addEventListener("exit",(m)=>{G.info(`Worker ${l} exited with code ${m}`)}),On.add(f)}G.info("All workers started. Press Ctrl+C to stop all machines")}var Wt="0.3.6";var zt={create:`
44
44
  hydra create - Create and register new GitHub runner machines
45
45
 
46
46
  USAGE
package/package.json CHANGED
@@ -39,5 +39,5 @@
39
39
  },
40
40
  "type": "module",
41
41
  "types": "src/cli.ts",
42
- "version": "0.3.5"
42
+ "version": "0.3.6"
43
43
  }