@r5n/hydra 0.11.3 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -2
- package/dist/cli.js +239 -90
- package/package.json +1 -1
- package/schema.json +54 -0
package/README.md
CHANGED
|
@@ -35,12 +35,13 @@ hydra status
|
|
|
35
35
|
| Start or stop runners | `hydra start [profile] [ids...]` / `hydra stop [profile] [ids...]` |
|
|
36
36
|
| Inspect local runner state | `hydra status` |
|
|
37
37
|
| Tail the latest job log | `hydra logs [id]` |
|
|
38
|
+
| Check DNS and each runner's job hooks | `hydra doctor` |
|
|
38
39
|
| Preview disk cleanup | `hydra cleanup --dry-run` |
|
|
39
|
-
| Update
|
|
40
|
+
| Update runner binaries and job hooks | `hydra update` |
|
|
40
41
|
| Deregister and remove runners | `hydra remove [profile] [ids...]` |
|
|
41
42
|
| Manage fleet profiles | `hydra profile` |
|
|
42
43
|
|
|
43
|
-
Runners are background processes; start them again after a reboot. Cleanup preserves job workspaces by default, and automatic runner updates are disabled in favour of `hydra update`.
|
|
44
|
+
Runners are background processes; start them again after a reboot. Cleanup preserves job workspaces by default, and automatic runner updates are disabled in favour of `hydra update`. Every job first waits for DNS to resolve the GitHub hosts, so a brief resolver outage delays the job instead of failing its checkout. New profiles also remove each job's workspace when it finishes; runners still share one user, so keep them to private repositories you trust.
|
|
44
45
|
|
|
45
46
|
See the [command and configuration reference](https://github.com/r5n-labs/clis/blob/develop/packages/hydra/docs/reference.md) for flags, profiles, cleanup policy and storage details.
|
|
46
47
|
|
package/dist/cli.js
CHANGED
|
@@ -1,103 +1,252 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
// @bun
|
|
3
|
-
var
|
|
4
|
-
`),
|
|
5
|
-
`){if(s)n+=
|
|
6
|
-
`){if(i&&
|
|
7
|
-
`)}var
|
|
8
|
-
`),n=
|
|
9
|
-
`),i=Math.max(r.length,n.length),s=[];for(let o=0;o<i;o++)r[o]!==n[o]&&s.push(o);return{lines:s,numLinesBefore:r.length,numLinesAfter:n.length,numLines:i}}var
|
|
10
|
-
`).map((a,
|
|
11
|
-
`)}function
|
|
12
|
-
`),
|
|
13
|
-
`).length-1;this.output.write(
|
|
14
|
-
`);this.output.write(o[s]),this._prevFrame=
|
|
3
|
+
var yi=Object.create;var{getPrototypeOf:$i,defineProperty:Ar,getOwnPropertyNames:wi}=Object;var Ir=Object.prototype.hasOwnProperty;function bi(e){return this[e]}var vi,Ci,Pr=(e,t,r)=>{var n=e!=null&&typeof e==="object";if(n){var i=t?vi??=new WeakMap:Ci??=new WeakMap,s=i.get(e);if(s)return s}r=e!=null?yi($i(e)):{};let o=t||!e||!e.__esModule||!Ir.call(e,"default")?Ar(r,"default",{value:e,enumerable:!0}):r;if(e&&typeof e==="object"||typeof e==="function"){for(let l of wi(e))if(!Ir.call(o,l))Ar(o,l,{get:bi.bind(e,l),enumerable:!0})}if(n)i.set(e,o);return o};var xi=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var Dt=xi(function(Oa,Kr){var Ot={to(e,t){if(!t)return`\x1B[${e+1}G`;return`\x1B[${t+1};${e+1}H`},move(e,t){let r="";if(e<0)r+=`\x1B[${-e}D`;else if(e>0)r+=`\x1B[${e}C`;if(t<0)r+=`\x1B[${-t}A`;else if(t>0)r+=`\x1B[${t}B`;return r},up:(e=1)=>`\x1B[${e}A`,down:(e=1)=>`\x1B[${e}B`,forward:(e=1)=>`\x1B[${e}C`,backward:(e=1)=>`\x1B[${e}D`,nextLine:(e=1)=>"\x1B[E".repeat(e),prevLine:(e=1)=>"\x1B[F".repeat(e),left:"\x1B[G",hide:"\x1B[?25l",show:"\x1B[?25h",save:"\x1B7",restore:"\x1B8"},Fi={up:(e=1)=>"\x1B[S".repeat(e),down:(e=1)=>"\x1B[T".repeat(e)},Hi={screen:"\x1B[2J",up:(e=1)=>"\x1B[1J".repeat(e),down:(e=1)=>"\x1B[J".repeat(e),line:"\x1B[2K",lineEnd:"\x1B[K",lineStart:"\x1B[1K",lines(e){let t="";for(let r=0;r<e;r++)t+=this.line+(r<e-1?Ot.up():"");if(e)t+=Ot.left;return t}};Kr.exports={cursor:Ot,scroll:Fi,erase:Hi,beep:"\x07"}});class X extends Error{constructor(e="Operation cancelled"){super(e)}}class w extends Error{hint;exitCode;constructor(e,t,r=1){super(e);this.hint=t;this.exitCode=r}}import{styleText as Mi}from"util";import{stdout as Lt,stdin as qr}from"process";import*as ae from"readline";import ji from"readline";var Tr=(()=>{let e=/[\uD800-\uDBFF][\uDC00-\uDFFF]/g;return(t)=>{let r=0;e.lastIndex=0;while(e.test(t))r+=1;return t.length-r}})(),Or=(e)=>e===12288||e>=65281&&e<=65376||e>=65504&&e<=65510,Dr=(e)=>e===8987||e===9001||e>=12272&&e<=12287||e>=12289&&e<=12350||e>=12441&&e<=12543||e>=12549&&e<=12591||e>=12593&&e<=12686||e>=12688&&e<=12771||e>=12783&&e<=12830||e>=12832&&e<=12871||e>=12880&&e<=19903||e>=65040&&e<=65049||e>=65072&&e<=65106||e>=65108&&e<=65126||e>=65128&&e<=65131||e>=127488&&e<=127490||e>=127504&&e<=127547||e>=127552&&e<=127560||e>=131072&&e<=196605||e>=196608&&e<=262141;var Si=/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]|\u001b\]8;[^;]*;.*?(?:\u0007|\u001b\u005c)/y,Ri=/[\x00-\x08\x0A-\x1F\x7F-\x9F]{1,1000}/y,Nr=/(?:(?![\uFF61-\uFF9F\uFF00-\uFFEF])[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}\p{Script=Tangut}]){1,1000}/yu,Ei=/\t{1,1000}/y,Lr=/[\u{1F1E6}-\u{1F1FF}]{2}|\u{1F3F4}[\u{E0061}-\u{E007A}]{2}[\u{E0030}-\u{E0039}\u{E0061}-\u{E007A}]{1,3}\u{E007F}|(?:\p{Emoji}\uFE0F\u20E3?|\p{Emoji_Modifier_Base}\p{Emoji_Modifier}?|\p{Emoji_Presentation})(?:\u200D(?:\p{Emoji_Modifier_Base}\p{Emoji_Modifier}?|\p{Emoji_Presentation}|\p{Emoji}\uFE0F\u20E3?))*/yu,_i=/(?:[\x20-\x7E\xA0-\xFF](?!\uFE0F)){1,1000}/y,ki=/\p{M}+/gu,Ai={limit:1/0,ellipsis:""},Fr=(e,t={},r={})=>{let n=t.limit??1/0,i=t.ellipsis??"",s=t?.ellipsisWidth??(i?Fr(i,Ai,r).width:0),o=0,l=r.controlWidth??0,a=r.tabWidth??8,u=r.emojiWidth??2,f=2,m=r.regularWidth??1,d=r.wideWidth??2,y=[[_i,m],[Si,0],[Ri,l],[Ei,a],[Lr,u],[Nr,d]],h=0,g=0,S=e.length,A=0,C=!1,D=S,T=Math.max(0,n-s),R=0,N=0,O=0,v=0;e:while(!0){if(N>R||g>=S&&g>h){let _=e.slice(R,N)||e.slice(h,g);A=0;for(let U of _.replaceAll(ki,"")){let G=U.codePointAt(0)||0;if(Or(G))v=2;else if(Dr(G))v=d;else v=m;if(O+v>T)D=Math.min(D,Math.max(R,h)+A);if(O+v>n){C=!0;break e}A+=U.length,O+=v}R=N=0}if(g>=S)break e;for(let _=0,U=y.length;_<U;_++){let[G,Fe]=y[_];if(G.lastIndex=g,G.test(e)){if(A=G===Nr?Tr(e.slice(g,G.lastIndex)):G===Lr?1:G.lastIndex-g,v=A*Fe,O+v>T)D=Math.min(D,g+Math.floor((T-O)/Fe));if(O+v>n){C=!0;break e}O+=v,R=h,N=g,g=h=G.lastIndex;continue e}}g+=1}return{width:C?T:O,index:C?D:S,truncated:C,ellipsed:C&&n>=s}},Hr=Fr;var Ii={limit:1/0,ellipsis:"",ellipsisWidth:0},Pi=(e,t={})=>Hr(e,Ii,t).width,J=Pi;var Me="\x1B",Br="\x9B",Ti=39,Pt="\x07",Gr="[",Oi="]",Jr="m",Tt=`${Oi}8;;`,Mr=new RegExp(`(?:\\${Gr}(?<code>\\d+)m|\\${Tt}(?<uri>.*)${Pt})`,"y"),jr=(e)=>{if(e>=30&&e<=37)return 39;if(e>=90&&e<=97)return 39;if(e>=40&&e<=47)return 49;if(e>=100&&e<=107)return 49;if(e===1||e===2)return 22;if(e===3)return 23;if(e===4)return 24;if(e===7)return 27;if(e===8)return 28;if(e===9)return 29;if(e===0)return 0;return},Ur=(e)=>`${Me}${Gr}${e}${Jr}`,Vr=(e)=>`${Me}${Tt}${e}${Pt}`,It=(e,t,r)=>{let n=t[Symbol.iterator](),i=!1,s=!1,o=e.at(-1),l=o===void 0?0:J(o),a=n.next(),u=n.next(),f=0;while(!a.done){let m=a.value,d=J(m);if(l+d<=r)e[e.length-1]+=m;else e.push(m),l=0;if(m===Me||m===Br)i=!0,s=t.startsWith(Tt,f+1);if(i){if(s){if(m===Pt)i=!1,s=!1}else if(m===Jr)i=!1}else if(l+=d,l===r&&!u.done)e.push(""),l=0;a=u,u=n.next(),f+=m.length}if(o=e.at(-1),!l&&o!==void 0&&o.length&&e.length>1)e[e.length-2]+=e.pop()},Di=(e)=>{let t=e.split(" "),r=t.length;while(r){if(J(t[r-1]))break;r--}if(r===t.length)return e;return t.slice(0,r).join(" ")+t.slice(r).join("")},Ni=(e,t,r={})=>{if(r.trim!==!1&&e.trim()==="")return"";let n="",i,s,o=e.split(" "),l=[""],a=0;for(let m=0;m<o.length;m++){let d=o[m];if(r.trim!==!1){let h=l.at(-1)??"",g=h.trimStart();if(h.length!==g.length)l[l.length-1]=g,a=J(g)}if(m!==0){if(a>=t&&(r.wordWrap===!1||r.trim===!1))l.push(""),a=0;if(a||r.trim===!1)l[l.length-1]+=" ",a++}let y=J(d);if(r.hard&&y>t){let h=t-a,g=1+Math.floor((y-h-1)/t);if(Math.floor((y-1)/t)<g)l.push("");It(l,d,t),a=J(l.at(-1)??"");continue}if(a+y>t&&a&&y){if(r.wordWrap===!1&&a<t){It(l,d,t),a=J(l.at(-1)??"");continue}l.push(""),a=0}if(a+y>t&&r.wordWrap===!1){It(l,d,t),a=J(l.at(-1)??"");continue}l[l.length-1]+=d,a+=y}if(r.trim!==!1)l=l.map((m)=>Di(m));let u=l.join(`
|
|
4
|
+
`),f=!1;for(let m=0;m<u.length;m++){let d=u[m];if(n+=d,!f){if(f=d>="\uD800"&&d<="\uDBFF",f)continue}else f=!1;if(d===Me||d===Br){Mr.lastIndex=m+1;let h=Mr.exec(u)?.groups;if(h?.code!==void 0){let g=Number.parseFloat(h.code);i=g===Ti?void 0:g}else if(h?.uri!==void 0)s=h.uri.length===0?void 0:h.uri}if(u[m+1]===`
|
|
5
|
+
`){if(s)n+=Vr("");let y=i?jr(i):void 0;if(i&&y)n+=Ur(y)}else if(d===`
|
|
6
|
+
`){if(i&&jr(i))n+=Ur(i);if(s)n+=Vr(s)}}return n},Li=/\r?\n/;function Z(e,t,r){return String(e).normalize().split(Li).map((n)=>Ni(n,t,r)).join(`
|
|
7
|
+
`)}var V=Pr(Dt(),1);import{ReadStream as Wr}from"tty";function pe(e,t,r){if(!r.some((o)=>!o.disabled))return e;let n=e+t,i=Math.max(r.length-1,0),s=n<0?i:n>i?0:n;return r[s]?.disabled?pe(s,t<0?-1:1,r):s}var Ui=["up","down","left","right","space","enter","cancel"],Vi=["January","February","March","April","May","June","July","August","September","October","November","December"],j={actions:new Set(Ui),aliases:new Map([["k","up"],["j","down"],["h","left"],["l","right"],["\x03","cancel"],["escape","cancel"]]),messages:{cancel:"Canceled",error:"Something went wrong"},withGuide:!0,date:{monthNames:[...Vi],messages:{required:"Please enter a valid date",invalidMonth:"There are only 12 months in a year",invalidDay:(e,t)=>`There are only ${e} days in ${t}`,afterMin:(e)=>`Date must be on or after ${e.toISOString().slice(0,10)}`,beforeMax:(e)=>`Date must be on or before ${e.toISOString().slice(0,10)}`}}};function Ft(e,t){if(typeof e=="string")return j.aliases.get(e)===t;for(let r of e)if(r!==void 0&&Ft(r,t))return!0;return!1}function Bi(e,t){if(e===t)return;let r=e.split(`
|
|
8
|
+
`),n=t.split(`
|
|
9
|
+
`),i=Math.max(r.length,n.length),s=[];for(let o=0;o<i;o++)r[o]!==n[o]&&s.push(o);return{lines:s,numLinesBefore:r.length,numLinesAfter:n.length,numLines:i}}var Gi=globalThis.process.platform.startsWith("win"),Nt=Symbol("clack:cancel");function _e(e){return e===Nt}function je(e,t){let r=e;r.isTTY&&r.setRawMode(t)}function zr({input:e=qr,output:t=Lt,overwrite:r=!0,hideCursor:n=!0}={}){let i=ae.createInterface({input:e,output:t,prompt:"",tabSize:1});ae.emitKeypressEvents(e,i),e instanceof Wr&&e.isTTY&&e.setRawMode(!0);let s=(o,{name:l,sequence:a})=>{let u=String(o);if(Ft([u,l,a],"cancel")){n&&t.write(V.cursor.show),process.exit(0);return}if(!r)return;ae.moveCursor(t,l==="return"?0:-1,l==="return"?-1:0,()=>{ae.clearLine(t,1,()=>{e.once("keypress",s)})})};return n&&t.write(V.cursor.hide),e.once("keypress",s),()=>{e.off("keypress",s),n&&t.write(V.cursor.show),e instanceof Wr&&e.isTTY&&!Gi&&e.setRawMode(!1),i.terminal=!1,i.close()}}var ke=(e)=>("columns"in e)&&typeof e.columns=="number"?e.columns:80,Ht=(e)=>("rows"in e)&&typeof e.rows=="number"?e.rows:20;function le(e,t,r,n=r,i=r,s){let o=ke(e??Lt);return Z(t,o-r.length,{hard:!0,trim:!1}).split(`
|
|
10
|
+
`).map((l,a,u)=>{let f=s?s(l,a):l;return a===0?`${n}${f}`:a===u.length-1?`${i}${f}`:`${r}${f}`}).join(`
|
|
11
|
+
`)}function Yr(e,t){if("~standard"in e){let r=e["~standard"].validate(t);if(r instanceof Promise)throw TypeError("Schema validation must be synchronous. Update `validate()` and remove any asynchronous logic.");return r.issues?.at(0)?.message}return e(t)}class Ae{input;output;_abortSignal;rl;opts;_render;_track=!1;_prevFrame="";_subscribers=new Map;_cursor=0;state="initial";error="";value;userInput="";constructor(e,t=!0){let{input:r=qr,output:n=Lt,render:i,signal:s,...o}=e;this.opts=o,this.onKeypress=this.onKeypress.bind(this),this.close=this.close.bind(this),this.render=this.render.bind(this),this._render=i.bind(this),this._track=t,this._abortSignal=s,this.input=r,this.output=n}unsubscribe(){this._subscribers.clear()}setSubscriber(e,t){let r=this._subscribers.get(e)??[];r.push(t),this._subscribers.set(e,r)}on(e,t){this.setSubscriber(e,{cb:t})}once(e,t){this.setSubscriber(e,{cb:t,once:!0})}emit(e,...t){let r=this._subscribers.get(e)??[],n=[];for(let i of r)i.cb(...t),i.once&&n.push(()=>r.splice(r.indexOf(i),1));for(let i of n)i()}prompt(){return new Promise((e)=>{if(this._abortSignal){if(this._abortSignal.aborted)return this.state="cancel",this.close(),e(Nt);this._abortSignal.addEventListener("abort",()=>{this.state="cancel",this.close()},{once:!0})}this.rl=ji.createInterface({input:this.input,tabSize:2,prompt:"",escapeCodeTimeout:50,terminal:!0}),this.rl.prompt(),this.opts.initialUserInput!==void 0&&this._setUserInput(this.opts.initialUserInput,!0),this.input.on("keypress",this.onKeypress),je(this.input,!0),this.output.on("resize",this.render),this.render(),this.once("submit",()=>{this.output.write(V.cursor.show),this.output.off("resize",this.render),je(this.input,!1),e(this.value)}),this.once("cancel",()=>{this.output.write(V.cursor.show),this.output.off("resize",this.render),je(this.input,!1),e(Nt)})})}_isActionKey(e,t){return e==="\t"}_shouldSubmit(e,t){return!0}_setValue(e){this.value=e,this.emit("value",this.value)}_setUserInput(e,t){this.userInput=e??"",this.emit("userInput",this.userInput),t&&this._track&&this.rl&&(this.rl.write(this.userInput),this._cursor=this.rl.cursor)}_clearUserInput(){this.rl?.write(null,{ctrl:!0,name:"u"}),this._setUserInput("")}onKeypress(e,t){if(this._track&&t.name!=="return"&&(t.name&&this._isActionKey(e,t)&&this.rl?.write(null,{ctrl:!0,name:"h"}),this._cursor=this.rl?.cursor??0,this._setUserInput(this.rl?.line)),this.state==="error"&&(this.state="active"),t?.name&&(!this._track&&j.aliases.has(t.name)&&this.emit("cursor",j.aliases.get(t.name)),j.actions.has(t.name)&&this.emit("cursor",t.name)),e&&(e.toLowerCase()==="y"||e.toLowerCase()==="n")&&this.emit("confirm",e.toLowerCase()==="y"),this.emit("key",e,t),t?.name==="return"&&this._shouldSubmit(e,t)){if(this.opts.validate){let r=Yr(this.opts.validate,this.value);r&&(this.error=r instanceof Error?r.message:r,this.state="error",this.rl?.write(this.userInput))}this.state!=="error"&&(this.state="submit")}Ft([e,t?.name,t?.sequence],"cancel")&&(this.state="cancel"),(this.state==="submit"||this.state==="cancel")&&this.emit("finalize"),this.render(),(this.state==="submit"||this.state==="cancel")&&this.close()}close(){this.input.unpipe(),this.input.removeListener("keypress",this.onKeypress),this.output.write(`
|
|
12
|
+
`),je(this.input,!1),this.rl?.close(),this.rl=void 0,this.emit(`${this.state}`,this.value),this.unsubscribe()}restoreCursor(){let e=Z(this._prevFrame,process.stdout.columns,{hard:!0,trim:!1}).split(`
|
|
13
|
+
`).length-1;this.output.write(V.cursor.move(-999,e*-1))}render(){let e=Z(this._render(this)??"",process.stdout.columns,{hard:!0,trim:!1});if(e!==this._prevFrame){if(this.state==="initial")this.output.write(V.cursor.hide);else{let t=Bi(this._prevFrame,e),r=Ht(this.output);if(this.restoreCursor(),t){let n=Math.max(0,t.numLinesAfter-r),i=Math.max(0,t.numLinesBefore-r),s=t.lines.find((o)=>o>=n);if(s===void 0){this._prevFrame=e;return}if(t.lines.length===1){this.output.write(V.cursor.move(0,s-i)),this.output.write(V.erase.lines(1));let o=e.split(`
|
|
14
|
+
`);this.output.write(o[s]),this._prevFrame=e,this.output.write(V.cursor.move(0,o.length-s-1));return}else if(t.lines.length>1){if(n<i)s=n;else{let l=s-i;l>0&&this.output.write(V.cursor.move(0,l))}this.output.write(V.erase.down());let o=e.split(`
|
|
15
15
|
`).slice(s);this.output.write(o.join(`
|
|
16
|
-
`)),this._prevFrame=
|
|
17
|
-
`);
|
|
16
|
+
`)),this._prevFrame=e;return}}this.output.write(V.erase.down())}this.output.write(e),this.state==="initial"&&(this.state="active"),this._prevFrame=e}}}class Mt extends Ae{get cursor(){return this.value?0:1}get _value(){return this.cursor===0}constructor(e){super(e,!1),this.value=!!e.initialValue,this.on("userInput",()=>{this.value=this._value}),this.on("confirm",(t)=>{this.output.write(V.cursor.move(0,-1)),this.value=t,this.state="submit",this.close()}),this.on("cursor",()=>{this.value=!this.value})}}class jt extends Ae{options;cursor=0;get _value(){return this.options[this.cursor]?.value}get _enabledOptions(){return this.options.filter((e)=>e.disabled!==!0)}toggleAll(){let e=this._enabledOptions,t=this.value!==void 0&&this.value.length===e.length;this.value=t?[]:e.map((r)=>r.value)}toggleInvert(){let e=this.value;if(!e)return;let t=this._enabledOptions.filter((r)=>!e.includes(r.value));this.value=t.map((r)=>r.value)}toggleValue(){this.value===void 0&&(this.value=[]);let e=this.value.includes(this._value);this.value=e?this.value.filter((t)=>t!==this._value):[...this.value,this._value]}constructor(e){super(e,!1),this.options=e.options,this.value=[...e.initialValues??[]];let t=Math.max(this.options.findIndex(({value:r})=>r===e.cursorAt),0);this.cursor=this.options[t]?.disabled?pe(t,1,this.options):t,this.on("key",(r,n)=>{n.name==="a"&&this.toggleAll(),n.name==="i"&&this.toggleInvert()}),this.on("cursor",(r)=>{switch(r){case"left":case"up":this.cursor=pe(this.cursor,-1,this.options);break;case"down":case"right":this.cursor=pe(this.cursor,1,this.options);break;case"space":this.toggleValue();break}})}}var Xr=class extends Ae{options;cursor=0;get _selectedValue(){return this.options[this.cursor]}changeValue(){let t=this._selectedValue;this.value=t===void 0?void 0:t.value}constructor(t){super(t,!1),this.options=t.options;let r=this.options.findIndex(({value:i})=>i===t.initialValue),n=r===-1?0:r;this.cursor=this.options[n]?.disabled?pe(n,1,this.options):n,this.changeValue(),this.on("cursor",(i)=>{switch(i){case"left":case"up":this.cursor=pe(this.cursor,-1,this.options);break;case"down":case"right":this.cursor=pe(this.cursor,1,this.options);break}this.changeValue()})}};class Ut extends Ae{get userInputWithCursor(){if(this.state==="submit")return this.userInput;let e=this.userInput;if(this.cursor>=e.length)return`${this.userInput}\u2588`;let t=e.slice(0,this.cursor),r=e.slice(this.cursor,this.cursor+1),n=e.slice(this.cursor+1);return`${t}${Mi("inverse",r)}${n}`}get cursor(){return this._cursor}constructor(e){super({...e,initialUserInput:e.initialUserInput??e.initialValue}),this.on("userInput",(t)=>{this._setValue(t)}),this.on("finalize",()=>{this.value||(this.value=e.defaultValue),this.value===void 0&&(this.value="")})}}import{styleText as p,stripVTControlCharacters as za}from"util";import q from"process";var Pe=Pr(Dt(),1);function Ji(){if(q.platform!=="win32")return q.env.TERM!=="linux";return Boolean(q.env.CI)||Boolean(q.env.WT_SESSION)||Boolean(q.env.TERMINUS_SUBLIME)||q.env.ConEmuTask==="{cmd::Cmder}"||q.env.TERM_PROGRAM==="Terminus-Sublime"||q.env.TERM_PROGRAM==="vscode"||q.env.TERM==="xterm-256color"||q.env.TERM==="alacritty"||q.env.TERMINAL_EMULATOR==="JetBrains-JediTerm"}var Vt=Ji(),Ki=()=>process.env.CI==="true";var P=(e,t)=>Vt?e:t,Wi=P("\u25C6","*"),rn=P("\u25A0","x"),nn=P("\u25B2","x"),Be=P("\u25C7","o"),qi=P("\u250C","T"),E=P("\u2502","|"),ue=P("\u2514","\u2014"),Qa=P("\u2510","T"),el=P("\u2518","\u2014"),Bt=P("\u25CF",">"),Ve=P("\u25CB"," "),zi=P("\u25FB","[\u2022]"),Zr=P("\u25FC","[+]"),Qr=P("\u25FB","[ ]"),tl=P("\u25AA","\u2022"),en=P("\u2500","-"),Yi=P("\u256E","+"),Xi=P("\u251C","+"),Zi=P("\u256F","+"),Qi=P("\u2570","+"),rl=P("\u256D","+"),es=P("\u25CF","\u2022"),ts=P("\u25C6","*"),rs=P("\u25B2","!"),ns=P("\u25A0","x"),Ge=(e)=>{switch(e){case"initial":case"active":return p("cyan",Wi);case"cancel":return p("red",rn);case"error":return p("yellow",nn);case"submit":return p("green",Be)}},sn=(e)=>{switch(e){case"initial":case"active":return p("cyan",E);case"cancel":return p("red",E);case"error":return p("yellow",E);case"submit":return p("green",E)}};function on(e,t){let r=[`${t?`${p("cyan",E)} `:""}${e.join(" \u2022 ")}`];return t&&r.push(p("cyan",ue)),r}var tn=(e,t,r,n,i,s=!1)=>{let o=t,l=0;if(s)for(let a=n-1;a>=r;a--){let u=e[a];if(u&&(o-=u.length),l++,o<=i)break}else for(let a=r;a<n;a++){let u=e[a];if(u&&(o-=u.length),l++,o<=i)break}return{lineCount:o,removals:l}},Gt=({cursor:e,options:t,style:r,output:n=process.stdout,maxItems:i=Number.POSITIVE_INFINITY,columnPadding:s=0,rowPadding:o=4})=>{let l=ke(n)-s,a=Ht(n),u=p("dim","..."),f=Math.max(a-o,0),m=Math.max(Math.min(i,f),5),d=0;e>=m-3&&(d=Math.max(Math.min(e-m+3,t.length-m),0));let y=m<t.length&&d>0,h=m<t.length&&d+m<t.length,g=Math.min(d+m,t.length),S=[],A=0;y&&A++,h&&A++;let C=d+(y?1:0),D=g-(h?1:0);for(let R=C;R<D;R++){let N=t[R],O=N?r(N,R===e):"",v=Z(O,l,{hard:!0,trim:!1}).split(`
|
|
17
|
+
`);S.push(v),A+=v.length}if(A>f){let R=0,N=0,O=A,v=e-C,_=f,U=()=>tn(S,O,0,v,_),G=()=>tn(S,O,v+1,S.length,_,!0);y?({lineCount:O,removals:R}=U(),O>_&&(h||(_-=1),{lineCount:O,removals:N}=G())):(h||(_-=1),{lineCount:O,removals:N}=G(),O>_&&(_-=1,{lineCount:O,removals:R}=U())),R>0&&(y=!0,S.splice(0,R)),N>0&&(h=!0,S.splice(S.length-N,N))}let T=[];y&&T.push(u);for(let R of S)for(let N of R)T.push(N);return h&&T.push(u),T};var an=(e)=>{let t=e.active??"Yes",r=e.inactive??"No";return new Mt({active:t,inactive:r,signal:e.signal,input:e.input,output:e.output,initialValue:e.initialValue??!0,render(){let n=e.withGuide??j.withGuide,i=`${Ge(this.state)} `,s=n?`${p("gray",E)} `:"",o=le(e.output,e.message,s,i),l=`${n?`${p("gray",E)}
|
|
18
18
|
`:""}${o}
|
|
19
|
-
`,
|
|
20
|
-
${
|
|
21
|
-
${
|
|
22
|
-
`:` ${
|
|
23
|
-
${
|
|
24
|
-
`}}}}).prompt()};var
|
|
25
|
-
`).map((r)=>
|
|
26
|
-
`),
|
|
27
|
-
${
|
|
19
|
+
`,a=this.value?t:r;switch(this.state){case"submit":{let u=n?`${p("gray",E)} `:"";return`${l}${u}${p("dim",a)}`}case"cancel":{let u=n?`${p("gray",E)} `:"";return`${l}${u}${p(["strikethrough","dim"],a)}${n?`
|
|
20
|
+
${p("gray",E)}`:""}`}default:{let u=n?`${p("cyan",E)} `:"",f=n?p("cyan",ue):"";return`${l}${u}${this.value?`${p("green",Bt)} ${t}`:`${p("dim",Ve)} ${p("dim",t)}`}${e.vertical?n?`
|
|
21
|
+
${p("cyan",E)} `:`
|
|
22
|
+
`:` ${p("dim","/")} `}${this.value?`${p("dim",Ve)} ${p("dim",r)}`:`${p("green",Bt)} ${r}`}
|
|
23
|
+
${f}
|
|
24
|
+
`}}}}).prompt()};var Je=async(e,t)=>{let r={},n=Object.keys(e);for(let i of n){let s=e[i],o=await s({results:r})?.catch((l)=>{throw l});if(typeof t?.onCancel=="function"&&_e(o)){r[i]="canceled",t.onCancel({results:r});continue}r[i]=o}return r},is=[`${p("dim","\u2191/\u2193")} to navigate`,`${p("dim","Space:")} select`,`${p("dim","Enter:")} confirm`],Ie=(e,t)=>e.split(`
|
|
25
|
+
`).map((r)=>t(r)).join(`
|
|
26
|
+
`),ln=(e)=>{let t=(i,s)=>{let o=i.label??String(i.value);return s==="disabled"?`${p("gray",Qr)} ${Ie(o,(l)=>p(["strikethrough","gray"],l))}${i.hint?` ${p("dim",`(${i.hint??"disabled"})`)}`:""}`:s==="active"?`${p("cyan",zi)} ${o}${i.hint?` ${p("dim",`(${i.hint})`)}`:""}`:s==="selected"?`${p("green",Zr)} ${Ie(o,(l)=>p("dim",l))}${i.hint?` ${p("dim",`(${i.hint})`)}`:""}`:s==="cancelled"?`${Ie(o,(l)=>p(["strikethrough","dim"],l))}`:s==="active-selected"?`${p("green",Zr)} ${o}${i.hint?` ${p("dim",`(${i.hint})`)}`:""}`:s==="submitted"?`${Ie(o,(l)=>p("dim",l))}`:`${p("dim",Qr)} ${Ie(o,(l)=>p("dim",l))}`},r=e.required??!0,n=e.showInstructions??!0;return new jt({options:e.options,signal:e.signal,input:e.input,output:e.output,initialValues:e.initialValues,required:r,cursorAt:e.cursorAt,validate(i){if(r&&(i===void 0||i.length===0))return`Please select at least one option.
|
|
27
|
+
${p("reset",p("dim",`Press ${p(["gray","bgWhite","inverse"]," space ")} to select, ${p("gray",p("bgWhite",p("inverse"," enter ")))} to submit`))}`},render(){let i=e.withGuide??j.withGuide,s=le(e.output,e.message,i?`${sn(this.state)} `:"",`${Ge(this.state)} `),o=`${i?`${p("gray",E)}
|
|
28
28
|
`:""}${s}
|
|
29
|
-
`,
|
|
30
|
-
${
|
|
31
|
-
`).map((
|
|
32
|
-
`),
|
|
33
|
-
`).length,
|
|
34
|
-
`).length+1;return`${o}${u}${
|
|
29
|
+
`,l=this.value??[],a=(u,f)=>{if(u.disabled)return t(u,"disabled");let m=l.includes(u.value);return f&&m?t(u,"active-selected"):m?t(u,"selected"):t(u,f?"active":"inactive")};switch(this.state){case"submit":{let u=this.options.filter(({value:m})=>l.includes(m)).map((m)=>t(m,"submitted")).join(p("dim",", "))||p("dim","none"),f=le(e.output,u,i?`${p("gray",E)} `:"");return`${o}${f}`}case"cancel":{let u=this.options.filter(({value:m})=>l.includes(m)).map((m)=>t(m,"cancelled")).join(p("dim",", "));if(u.trim()==="")return`${o}${p("gray",E)}`;let f=le(e.output,u,i?`${p("gray",E)} `:"");return`${o}${f}${i?`
|
|
30
|
+
${p("gray",E)}`:""}`}case"error":{let u=i?`${p("yellow",E)} `:"",f=this.error.split(`
|
|
31
|
+
`).map((y,h)=>h===0?`${i?`${p("yellow",ue)} `:""}${p("yellow",y)}`:` ${y}`).join(`
|
|
32
|
+
`),m=o.split(`
|
|
33
|
+
`).length,d=f.split(`
|
|
34
|
+
`).length+1;return`${o}${u}${Gt({output:e.output,options:this.options,cursor:this.cursor,maxItems:e.maxItems,columnPadding:u.length,rowPadding:m+d,style:a}).join(`
|
|
35
35
|
${u}`)}
|
|
36
|
-
${
|
|
37
|
-
`}default:{let u=i?`${
|
|
38
|
-
`).length,
|
|
39
|
-
`),
|
|
36
|
+
${f}
|
|
37
|
+
`}default:{let u=i?`${p("cyan",E)} `:"",f=o.split(`
|
|
38
|
+
`).length,m=n?on(is,i):i?[p("cyan",ue)]:[],d=m.join(`
|
|
39
|
+
`),y=m.length+1;return`${o}${u}${Gt({output:e.output,options:this.options,cursor:this.cursor,maxItems:e.maxItems,columnPadding:u.length,rowPadding:f+y,style:a}).join(`
|
|
40
40
|
${u}`)}
|
|
41
|
-
${
|
|
42
|
-
`}}}}).prompt()};var
|
|
43
|
-
`);if(
|
|
41
|
+
${d}
|
|
42
|
+
`}}}}).prompt()};var b={message:(e=[],{symbol:t=p("gray",E),secondarySymbol:r=p("gray",E),output:n=process.stdout,spacing:i=1,withGuide:s}={})=>{let o=[],l=s??j.withGuide,a=l?r:"",u=l?`${t} `:"",f=l?`${r} `:"";for(let d=0;d<i;d++)o.push(a);let m=Array.isArray(e)?e:e.split(`
|
|
43
|
+
`);if(m.length>0){let[d,...y]=m;d.length>0?o.push(`${u}${d}`):o.push(l?t:"");for(let h of y)h.length>0?o.push(`${f}${h}`):o.push(l?r:"")}n.write(`${o.join(`
|
|
44
44
|
`)}
|
|
45
|
-
`)},info:(t
|
|
46
|
-
`)},
|
|
47
|
-
${
|
|
45
|
+
`)},info:(e,t)=>{b.message(e,{...t,symbol:p("blue",es)})},success:(e,t)=>{b.message(e,{...t,symbol:p("green",ts)})},step:(e,t)=>{b.message(e,{...t,symbol:p("green",Be)})},warn:(e,t)=>{b.message(e,{...t,symbol:p("yellow",rs)})},warning:(e,t)=>{b.warn(e,t)},error:(e,t)=>{b.message(e,{...t,symbol:p("red",ns)})}};var Ke=(e="",t)=>{let r=t?.output??process.stdout,n=t?.withGuide??j.withGuide?`${p("gray",qi)} `:"";r.write(`${n}${e}
|
|
46
|
+
`)},We=(e="",t)=>{let r=t?.output??process.stdout,n=t?.withGuide??j.withGuide?`${p("gray",E)}
|
|
47
|
+
${p("gray",ue)} `:"";r.write(`${n}${e}
|
|
48
48
|
|
|
49
|
-
`)};var
|
|
50
|
-
`),s=i.reduce((
|
|
51
|
-
`).map(s),""],
|
|
52
|
-
`),
|
|
53
|
-
`:"",
|
|
49
|
+
`)};var ss=(e)=>e,os=(e,t,r)=>{let n={hard:!0,trim:!1},i=Z(e,t,n).split(`
|
|
50
|
+
`),s=i.reduce((a,u)=>Math.max(J(u),a),0),o=i.map(r).reduce((a,u)=>Math.max(J(u),a),0),l=t-(o-s);return Z(e,l,n)},be=(e="",t="",r)=>{let n=r?.output??q.stdout,i=r?.withGuide??j.withGuide,s=r?.format??ss,o=["",...os(e,ke(n)-6,s).split(`
|
|
51
|
+
`).map(s),""],l=J(t),a=Math.max(o.reduce((d,y)=>{let h=J(y);return h>d?h:d},0),l)+2,u=o.map((d)=>`${p("gray",E)} ${d}${" ".repeat(a-J(d))}${p("gray",E)}`).join(`
|
|
52
|
+
`),f=i?`${p("gray",E)}
|
|
53
|
+
`:"",m=i?Xi:Qi;n.write(`${f}${p("green",Be)} ${p("reset",t)} ${p("gray",en.repeat(Math.max(a-l-1,1))+Yi)}
|
|
54
54
|
${u}
|
|
55
|
-
${
|
|
56
|
-
`)};var
|
|
57
|
-
`);let
|
|
58
|
-
`);
|
|
59
|
-
`);let
|
|
60
|
-
`):r.write(`${
|
|
61
|
-
`)),O(),
|
|
62
|
-
`)?
|
|
63
|
-
`).map((r)=>
|
|
64
|
-
`):e
|
|
55
|
+
${p("gray",m+en.repeat(a+2)+Zi)}
|
|
56
|
+
`)};var as=(e)=>p("magenta",e),K=({indicator:e="dots",onCancel:t,output:r=process.stdout,cancelMessage:n,errorMessage:i,frames:s=Vt?["\u25D2","\u25D0","\u25D3","\u25D1"]:["\u2022","o","O","0"],delay:o=Vt?80:120,signal:l,...a}={})=>{let u=Ki(),f,m,d=!1,y=!1,h="",g,S=performance.now(),A=ke(r),C=a?.styleFrame??as,D=(L)=>{let W=L>1?i??j.messages.error:n??j.messages.cancel;y=L===1,d&&(Ee(W,L),y&&typeof t=="function"&&t())},T=()=>D(2),R=()=>D(1),N=()=>{process.on("uncaughtExceptionMonitor",T),process.on("unhandledRejection",T),process.on("SIGINT",R),process.on("SIGTERM",R),process.on("exit",D),l&&l.addEventListener("abort",R)},O=()=>{process.removeListener("uncaughtExceptionMonitor",T),process.removeListener("unhandledRejection",T),process.removeListener("SIGINT",R),process.removeListener("SIGTERM",R),process.removeListener("exit",D),l&&l.removeEventListener("abort",R)},v=()=>{if(g===void 0)return;u&&r.write(`
|
|
57
|
+
`);let L=Z(g,A,{hard:!0,trim:!1}).split(`
|
|
58
|
+
`);L.length>1&&r.write(Pe.cursor.up(L.length-1)),r.write(Pe.cursor.to(0)),r.write(Pe.erase.down())},_=(L)=>L.replace(/\.+$/,""),U=(L)=>{let W=(performance.now()-L)/1000,re=Math.floor(W/60),ne=Math.floor(W%60);return re>0?`[${re}m ${ne}s]`:`[${ne}s]`},G=a.withGuide??j.withGuide,Fe=(L="")=>{d=!0,f=zr({output:r}),h=_(L),S=performance.now(),G&&r.write(`${p("gray",E)}
|
|
59
|
+
`);let W=0,re=0;N(),m=setInterval(()=>{if(u&&h===g)return;v(),g=h;let ne=C(s[W]),He;if(u)He=`${ne} ${h}...`;else if(e==="timer")He=`${ne} ${h} ${U(S)}`;else{let gi=".".repeat(Math.floor(re)).slice(0,3);He=`${ne} ${h}${gi}`}let hi=Z(He,A,{hard:!0,trim:!1});r.write(hi),W=W+1<s.length?W+1:0,re=re<4?re+0.125:0},o)},Ee=(L="",W=0,re=!1)=>{if(!d)return;d=!1,clearInterval(m),v();let ne=W===0?p("green",Be):W===1?p("red",rn):p("red",nn);h=L??h,re||(e==="timer"?r.write(`${ne} ${h} ${U(S)}
|
|
60
|
+
`):r.write(`${ne} ${h}
|
|
61
|
+
`)),O(),f()};return{start:Fe,stop:(L="")=>Ee(L,0),message:(L="")=>{h=_(L??h)},cancel:(L="")=>Ee(L,1),error:(L="")=>Ee(L,2),clear:()=>Ee("",0,!0),get isCancelled(){return y}}},nl={light:P("\u2500","-"),heavy:P("\u2501","="),block:P("\u2588","#")};var ls=[`${p("dim","\u2191/\u2193")} to navigate`,`${p("dim","Enter:")} confirm`],Ue=(e,t)=>e.includes(`
|
|
62
|
+
`)?e.split(`
|
|
63
|
+
`).map((r)=>t(r)).join(`
|
|
64
|
+
`):t(e),un=(e)=>{let t=(n,i)=>{if(n===void 0)return"";let s=n.label??String(n.value);switch(i){case"disabled":return`${p("gray",Ve)} ${Ue(s,(o)=>p("gray",o))}${n.hint?` ${p("dim",`(${n.hint??"disabled"})`)}`:""}`;case"selected":return`${Ue(s,(o)=>p("dim",o))}`;case"active":return`${p("green",Bt)} ${s}${n.hint?` ${p("dim",`(${n.hint})`)}`:""}`;case"cancelled":return`${Ue(s,(o)=>p(["strikethrough","dim"],o))}`;default:return`${p("dim",Ve)} ${Ue(s,(o)=>p("dim",o))}`}},r=e.showInstructions??!0;return new Xr({options:e.options,signal:e.signal,input:e.input,output:e.output,initialValue:e.initialValue,render(){let n=e.withGuide??j.withGuide,i=`${Ge(this.state)} `,s=`${sn(this.state)} `,o=le(e.output,e.message,s,i),l=`${n?`${p("gray",E)}
|
|
65
65
|
`:""}${o}
|
|
66
|
-
`;switch(this.state){case"submit":{let
|
|
67
|
-
${
|
|
68
|
-
`).length,
|
|
69
|
-
`),
|
|
70
|
-
${
|
|
71
|
-
${
|
|
72
|
-
`}}}}).prompt()};var
|
|
73
|
-
`:""}${
|
|
74
|
-
`,n=
|
|
75
|
-
${
|
|
76
|
-
${
|
|
77
|
-
`}case"submit":{let o=s?` ${
|
|
78
|
-
${
|
|
79
|
-
${
|
|
80
|
-
`}}}}).prompt();var
|
|
81
|
-
`).trimEnd()}command(t
|
|
82
|
-
`).trimEnd()}formatPositionalUsage(
|
|
83
|
-
Run "${n} --help" for usage.`),process.exit(1)}function
|
|
84
|
-
Run "${this.commandPath} --help" for usage.`),process.exit(1);let o=this.command.prompts&&n.length===0&&process.stdout.isTTY===!0,
|
|
66
|
+
`;switch(this.state){case"submit":{let a=n?`${p("gray",E)} `:"",u=le(e.output,t(this.options[this.cursor],"selected"),a);return`${l}${u}`}case"cancel":{let a=n?`${p("gray",E)} `:"",u=le(e.output,t(this.options[this.cursor],"cancelled"),a);return`${l}${u}${n?`
|
|
67
|
+
${p("gray",E)}`:""}`}default:{let a=n?`${p("cyan",E)} `:"",u=l.split(`
|
|
68
|
+
`).length,f=r?on(ls,n):n?[p("cyan",ue)]:[],m=f.join(`
|
|
69
|
+
`),d=f.length+1;return`${l}${a}${Gt({output:e.output,cursor:this.cursor,options:this.options,maxItems:e.maxItems,columnPadding:a.length,rowPadding:u+d,style:(y,h)=>t(y,y.disabled?"disabled":h?"active":"inactive")}).join(`
|
|
70
|
+
${a}`)}
|
|
71
|
+
${m}
|
|
72
|
+
`}}}}).prompt()};var il=`${p("gray",E)} `;var cn=(e)=>new Ut({validate:e.validate,placeholder:e.placeholder,defaultValue:e.defaultValue,initialValue:e.initialValue,output:e.output,signal:e.signal,input:e.input,render(){let t=e?.withGuide??j.withGuide,r=`${`${t?`${p("gray",E)}
|
|
73
|
+
`:""}${Ge(this.state)} `}${e.message}
|
|
74
|
+
`,n=e.placeholder&&e.placeholder.length>0?p("inverse",e.placeholder[0])+p("dim",e.placeholder.slice(1)):p(["inverse","hidden"],"_"),i=this.userInput?this.userInputWithCursor:n,s=this.value??"";switch(this.state){case"error":{let o=this.error?` ${p("yellow",this.error)}`:"",l=t?`${p("yellow",E)} `:"",a=t?p("yellow",ue):"";return`${r.trim()}
|
|
75
|
+
${l}${i}
|
|
76
|
+
${a}${o}
|
|
77
|
+
`}case"submit":{let o=s?` ${p("dim",s)}`:"",l=t?p("gray",E):"";return`${r}${l}${o}`}case"cancel":{let o=s?` ${p(["strikethrough","dim"],s)}`:"",l=t?p("gray",E):"";return`${r}${l}${o}${s.trim()?`
|
|
78
|
+
${l}`:""}`}default:{let o=t?`${p("cyan",E)} `:"",l=t?p("cyan",ue):"";return`${r}${o}${i}
|
|
79
|
+
${l}
|
|
80
|
+
`}}}}).prompt();var cs=!process.env.NO_COLOR&&(process.env.FORCE_COLOR||(process.stdout?.isTTY??!1)),mn=["black","blue","cyan","gray","green","magenta","red","white","yellow"],ps=["bold","dim","italic","underline","reset"],fn={bold:"\x1B[1m",dim:"\x1B[2m",italic:"\x1B[3m",reset:"\x1B[0m",underline:"\x1B[4m"},ds={black:"\x1B[30m",blue:"\x1B[34m",cyan:"\x1B[36m",gray:"\x1B[90m",green:"\x1B[32m",magenta:"\x1B[35m",red:"\x1B[31m",white:"\x1B[37m",yellow:"\x1B[33m"},ms={black:"\x1B[40m",blue:"\x1B[44m",cyan:"\x1B[46m",gray:"\x1B[100m",green:"\x1B[42m",magenta:"\x1B[45m",red:"\x1B[41m",white:"\x1B[47m",yellow:"\x1B[43m"},pn={"\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 qe(e){return(t)=>cs?`${e}${t}${fn.reset}`:t}function fs(e){for(let t=0;t<e.length;t++){let r=e.charCodeAt(t);if(r<32&&r!==27)return!1}return!0}function dn({bright:e=!1,color:t}){let r=Bun.color(t,"ansi"),n=typeof r==="string"&&r.length>0&&fs(r)?r:ds[t];if(!n)return"";return e&&n in pn?pn[n]:n}var hs=mn.reduce((e,t)=>{let r=dn({color:t});if(r)e[t]=qe(r);let n=dn({bright:!0,color:t});if(n)e[`${t}Bright`]=qe(n);return e},{}),gs=ps.reduce((e,t)=>(e[t]=qe(fn[t]),e),{}),ys=mn.reduce((e,t)=>{let r=ms[t];if(r){let n=`bg${t.charAt(0).toUpperCase()}${t.slice(1)}`;e[n]=qe(r)}return e},{}),c={...hs,...gs,...ys};function ze(e){if(_e(e))throw new X;return e}function ws(e){return e.map((t)=>t.key?`${c.bold(t.key)} ${c.dim(t.text)}`:c.dim(t.text)).join(c.dim(" \xB7 "))}function Ye(e){b.info(ws(e))}var Xe={confirm:[{key:"Enter",text:"confirm"},{key:"Esc",text:"cancel"}],multiselect:[{key:"\u2191\u2193",text:"navigate"},{key:"Space",text:"toggle"},{key:"a",text:"all"},{key:"Enter",text:"confirm"},{key:"Esc",text:"cancel"}],select:[{key:"\u2191\u2193",text:"navigate"},{key:"Enter",text:"select"},{key:"Esc",text:"cancel"}],text:[{key:"Enter",text:"submit"},{key:"Esc",text:"cancel"}]};async function de(e){return Ye(Xe.confirm),ze(await an(e))}async function ie(e){return Ye(Xe.text),ze(await cn(e))}async function me(e){return Ye(Xe.select),ze(await un({showInstructions:!1,...e}))}async function fe(e){return Ye(Xe.multiselect),ze(await ln({showInstructions:!1,...e}))}var bs=4;function ve(e,t=0){if(t>=bs)return;let r=[...e instanceof AggregateError?e.errors:[],...e.cause!==void 0?[e.cause]:[]];for(let n of r){if(b.info(c.dim(`caused by: ${n instanceof Error?n.message:String(n)}`)),n instanceof w&&n.hint)b.info(c.dim(` ${n.hint}`));if(n instanceof Error)ve(n,t+1)}}function Jt(e){return e==null?[]:Array.isArray(e)?e:[e]}function vs(e,t,r,n){var i,s=e[t],o=~n.string.indexOf(t)?r==null||r===!0?"":String(r):typeof r==="boolean"?r:~n.boolean.indexOf(t)?r==="false"?!1:r==="true"||(e._.push((i=+r,i*0===0)?i:r),!!r):(i=+r,i*0===0)?i:r;e[t]=s==null?o:Array.isArray(s)?s.concat(o):[s,o]}function Te(e,t){e=e||[],t=t||{};var r,n,i,s,o,l={_:[]},a=0,u=0,f=0,m=e.length;let d=t.alias!==void 0,y=t.unknown!==void 0,h=t.default!==void 0;if(t.alias=t.alias||{},t.string=Jt(t.string),t.boolean=Jt(t.boolean),d)for(r in t.alias){n=t.alias[r]=Jt(t.alias[r]);for(a=0;a<n.length;a++)(t.alias[n[a]]=n.concat(r)).splice(a,1)}for(a=t.boolean.length;a-- >0;){n=t.alias[t.boolean[a]]||[];for(u=n.length;u-- >0;)t.boolean.push(n[u])}for(a=t.string.length;a-- >0;){n=t.alias[t.string[a]]||[];for(u=n.length;u-- >0;)t.string.push(n[u])}if(h){for(r in t.default)if(s=typeof t.default[r],n=t.alias[r]=t.alias[r]||[],t[s]!==void 0){t[s].push(r);for(a=0;a<n.length;a++)t[s].push(n[a])}}let g=y?Object.keys(t.alias):[];for(a=0;a<m;a++){if(i=e[a],i==="--"){l._=l._.concat(e.slice(++a));break}for(u=0;u<i.length;u++)if(i.charCodeAt(u)!==45)break;if(u===0)l._.push(i);else if(i.substring(u,u+3)==="no-"){if(s=i.substring(u+3),y&&!~g.indexOf(s))return t.unknown(i);l[s]=!1}else{for(f=u+1;f<i.length;f++)if(i.charCodeAt(f)===61)break;s=i.substring(u,f),o=i.substring(++f)||(a+1===m||(""+e[a+1]).charCodeAt(0)===45||e[++a]),n=u===2?[s]:s;for(f=0;f<n.length;f++){if(s=n[f],y&&!~g.indexOf(s))return t.unknown("-".repeat(u)+s);vs(l,s,f+1<n.length||o,t)}}}if(h){for(r in t.default)if(l[r]===void 0)l[r]=t.default[r]}if(d)for(r in l){n=t.alias[r]||[];while(n.length>0)l[n.shift()]=l[r]}return l}var hn=1,Cs=2;function Ce(e){return e.replace(/([a-z])([A-Z])/g,"$1-$2").toLowerCase()}function Kt(e){let t={alias:{},boolean:[],default:{},string:[]};for(let[r,n]of Object.entries(e)){if(n.type==="boolean")t.boolean.push(r);else t.string.push(r);if(n.alias)t.alias[n.alias]=r;let i=Ce(r);if(i!==r)t.alias[i]=r;if(n.default!==void 0)t.default[r]=n.default}return t}function Ze(e,t){if(e in t)return`--${Ce(e)}`;let r=Object.entries(t).find(([,n])=>n.alias===e)?.[0];if(r)return`--${Ce(r)}`;return e.length===hn?`-${e}`:`--${Ce(e)}`}function F(e,t,r){let{alias:n}=Kt(t),i=Object.keys(e).find((s)=>!Object.hasOwn(t,s)&&!Object.hasOwn(n,s));if(i!==void 0)throw new w(`Unknown option: ${Ze(i,t)}`,r)}function gn(e,t){for(let[n,i]of Object.entries(e))if(Array.isArray(i))throw new w(`${Ze(n,t)} can only be provided once`);let r={...e};for(let[n,i]of Object.entries(t)){if(i.type!=="number")continue;let s=e[n];if(s===void 0)continue;let o=Number(s);if(typeof s==="boolean"||typeof s==="string"&&s.trim().length===0||!Number.isFinite(o)){let l=Ze(n,t);throw new w(`Invalid number for ${l}: "${s}"`,`Use ${l}=<number> with a finite numeric value`)}r[n]=o}return r}function yn(e,t){let r=Kt(t);$n(e,r,t,!0);let n=Te(e,r),i=n._[0],s=i?e.indexOf(i):-1,o=Te(s>=0?e.slice(0,s):e,r),{_:l,...a}=o;F(a,t,"Place command options after the command name");let{_:u,...f}=n,m=Object.fromEntries(Object.entries(f).filter(([y])=>(y in t)||(y in r.alias))),d=gn(m,t);return{command:i,flags:d,restArgs:s>=0?e.slice(s+1):[]}}function Wt(e,t={}){let r=Kt(t),n=xs(e,r);$n(n,r,t);let i=Te(n,r),{_:s,...o}=i;return{args:gn(o,t),rawPositionals:s}}function $n(e,t,r,n=!1){let i=new Set;for(let s of e){if(s==="--")break;let o=s.match(/^-+/)?.[0];if(!o)continue;let l=s.slice(o.length),a=l.split("=",hn)[0]??"",u=l.startsWith("no-")?[l.slice(3)]:o.length===Cs?[a]:[...a];for(let f of u){let m=Object.hasOwn(t.alias,f)?t.alias[f]??f:f;if(n&&!Object.hasOwn(r,m))continue;if(i.has(m))throw new w(`${Ze(m,r)} can only be provided once`);i.add(m)}}}function xs(e,t){let r=!0;return e.map((n)=>{if(n==="--")r=!1;if(!r)return n;return n.replace(/^--(no-[^=]+)(=.*)?$/,(i,s,o)=>{let l=t.alias[s];return l&&t.boolean.includes(l)?`--${l}${o??""}`:i})})}function qt(e,t){let r={},n=Object.entries(t);for(let[i,[s,o]]of n.entries())r[s]=o.variadic?e.slice(i):e[i];return r}function wn(e,t){for(let[r,n]of Object.entries(t)){if(!n.required)continue;let i=e[r];if(Array.isArray(i)?i.length===0:!i)return`Missing required argument: <${r}${n.variadic?"...":""}>`}return null}var Ss=(e)=>e,x={arg:c.magenta,cmd:c.cyan,default:c.bold,desc:Ss,flag:c.green,header:(e)=>c.bold(c.yellow(e)),hint:c.dim,name:(e)=>c.bold(c.cyan(e)),placeholder:c.magenta};class zt{metadata;commands;globalArgs;constructor(e,t,r){this.metadata=e;this.commands=t;this.globalArgs=r}version(){return`${x.name(this.metadata.name)} ${x.hint(this.metadata.version??"0.0.0")}`}global(){let e=[],{name:t,bin:r,version:n,description:i}=this.metadata;if(e.push(`${x.name(t)}${n?` ${x.hint(`v${n}`)}`:""}`),i)e.push(x.desc(i));e.push(""),e.push(x.header("Usage:")),e.push(` ${x.cmd(r)} ${x.placeholder("[command]")} ${x.placeholder("[options]")}`),e.push(""),e.push(x.header("Commands:"));let s=Array.from(this.commands.values()).map((o)=>[` ${x.cmd(o.name)}`,x.desc(o.description)]);return e.push(...this.formatColumns(s)),e.push(""),e.push(x.header("Options:")),e.push(...this.formatArgDefs(this.globalArgs)),e.push(""),e.push(`${x.hint("Run")} ${x.cmd(`${r} <command> --help`)} ${x.hint("for command details.")}`),e.join(`
|
|
81
|
+
`).trimEnd()}command(e,t){let r=[],n=t??this.metadata.bin,i=`${n} ${e.name}`,s=`${x.cmd(n)} ${x.cmd(e.name)}`;r.push(x.name(i)),r.push(x.desc(e.description)),r.push(""),r.push(x.header("Usage:"));let o=this.formatPositionalUsage(e.positionals);if(e.hasSubcommands())r.push(` ${s} ${x.placeholder("<subcommand>")} ${x.placeholder("[options]")}`);else{let u=[s];if(o)u.push(o);u.push(x.placeholder("[options]")),r.push(` ${u.join(" ")}`)}if(e.prompts&&Object.keys(e.positionals).length>0)r.push(` ${x.hint("Omit positional arguments to run interactively.")}`);if(r.push(""),e.hasSubcommands()){r.push(x.header("Subcommands:"));let u=e.getSubcommands().map((f)=>[` ${x.cmd(f.name)}`,x.desc(f.description)]);r.push(...this.formatColumns(u)),r.push("")}if(Object.entries(e.positionals).length>0)r.push(x.header("Arguments:")),r.push(...this.formatPositionalDefs(e.positionals)),r.push("");if(Object.entries(e.args).length>0)r.push(x.header("Options:")),r.push(...this.formatArgDefs(e.args));return r.join(`
|
|
82
|
+
`).trimEnd()}formatPositionalUsage(e){return Object.entries(e).map(([t,r])=>{let n=r.variadic?`${t}...`:t;return r.required?x.arg(`<${n}>`):x.arg(`[${n}]`)}).join(" ")}formatPositionalDefs(e){let t=Object.entries(e).map(([r,n])=>{let i=n.variadic?`${r}...`:r,s=x.desc(n.description??""),o=n.required?"":x.hint(" (optional)");return[` ${x.arg(i)}`,`${s}${o}`]});return this.formatColumns(t)}formatArgDefs(e){let t=[];for(let[r,n]of Object.entries(e)){let i=n.alias?`${x.flag(`-${n.alias}`)}, `:" ",s=n.type==="boolean"?"":` ${x.placeholder(`<${n.type==="number"?"number":"value"}>`)}`,o=`${i}${x.flag(`--${Ce(r)}`)}${s}`,l=x.desc(n.description??""),a=this.formatDefault(n),u=a===void 0?"":` (default: ${x.default(a)})`;t.push([` ${o}`,`${l}${u}`])}return this.formatColumns(t)}formatDefault(e){if(typeof e.displayDefault==="string")return e.displayDefault;if(e.default===void 0)return;if(e.type==="boolean"&&e.default===!1)return;if(e.type==="string")return`"${e.default}"`;return String(e.default)}formatColumns(e){if(e.length===0)return[];let t=Math.max(...e.map(([n])=>this.stripAnsi(n).length)),r=2;return e.map(([n,i])=>{let s=t-this.stripAnsi(n).length+r;return`${n}${" ".repeat(s)}${i}`})}stripAnsi(e){return e.replace(/\x1b\[[0-9;]*m/g,"")}}var se=new Uint32Array(65536),Rs=(e,t)=>{let r=e.length,n=t.length,i=1<<r-1,s=-1,o=0,l=r,a=r;while(a--)se[e.charCodeAt(a)]|=1<<a;for(a=0;a<n;a++){let u=se[t.charCodeAt(a)],f=u|o;if(u|=(u&s)+s^s,o|=~(u|s),s&=u,o&i)l++;if(s&i)l--;o=o<<1|1,s=s<<1|~(f|o),o&=f}a=r;while(a--)se[e.charCodeAt(a)]=0;return l},Es=(e,t)=>{let r=t.length,n=e.length,i=[],s=[],o=Math.ceil(r/32),l=Math.ceil(n/32);for(let h=0;h<o;h++)s[h]=-1,i[h]=0;let a=0;for(;a<l-1;a++){let h=0,g=-1,S=a*32,A=Math.min(32,n)+S;for(let C=S;C<A;C++)se[e.charCodeAt(C)]|=1<<C;for(let C=0;C<r;C++){let D=se[t.charCodeAt(C)],T=s[C/32|0]>>>C&1,R=i[C/32|0]>>>C&1,N=D|h,O=((D|R)&g)+g^g|D|R,v=h|~(O|g),_=g&O;if(v>>>31^T)s[C/32|0]^=1<<C;if(_>>>31^R)i[C/32|0]^=1<<C;v=v<<1|T,_=_<<1|R,g=_|~(N|v),h=v&N}for(let C=S;C<A;C++)se[e.charCodeAt(C)]=0}let u=0,f=-1,m=a*32,d=Math.min(32,n-m)+m;for(let h=m;h<d;h++)se[e.charCodeAt(h)]|=1<<h;let y=n;for(let h=0;h<r;h++){let g=se[t.charCodeAt(h)],S=s[h/32|0]>>>h&1,A=i[h/32|0]>>>h&1,C=g|u,D=((g|A)&f)+f^f|g|A,T=u|~(D|f),R=f&D;if(y+=T>>>n-1&1,y-=R>>>n-1&1,T>>>31^S)s[h/32|0]^=1<<h;if(R>>>31^A)i[h/32|0]^=1<<h;T=T<<1|S,R=R<<1|A,f=R|~(C|T),u=T&C}for(let h=m;h<d;h++)se[e.charCodeAt(h)]=0;return y},_s=(e,t)=>{if(e.length<t.length){let r=t;t=e,e=r}if(t.length===0)return e.length;if(e.length<=32)return Rs(e,t);return Es(e,t)},bn=(e,t)=>{let r=1/0,n=0;for(let i=0;i<t.length;i++){let s=_s(e,t[i]);if(s<r)r=s,n=i}return t[n]};function Qe(e,t,r,n){let i=bn(t,r);if(console.error(c.red(`Unknown ${e}: ${t}`)),i)console.error(c.yellow(`Did you mean "${i}"?`));console.error(`
|
|
83
|
+
Run "${n} --help" for usage.`),process.exit(1)}function et(e,t){if(Array.isArray(t))return[...t];if(!vn(e)||!vn(t))return t;return Object.fromEntries([...Object.entries(e),...Object.entries(t).map(([r,n])=>[r,et(Object.hasOwn(e,r)?e[r]:void 0,n)])])}function vn(e){if(e===null||typeof e!=="object")return!1;let t=Object.getPrototypeOf(e);return t===Object.prototype||t===null}var Yt="__exit__";class xe{command;env;prefix;options;selections;constructor(e,t,r,n={}){this.command=e;this.env=t;this.prefix=r;this.options=n;this.selections=n.selections??new Map}get commandPath(){return this.prefix?`${this.prefix} ${this.command.name}`:this.command.name}async route(e,t){if(t&&process.stdout.isTTY!==!0)throw new w("Interactive mode requires a terminal","Pass a command and its arguments to run non-interactively");if(t)await this.routeInteractive();else await this.routeDirect(e)}async routeDirect(e){let[t,...r]=e;if(t&&this.command.hasSubcommands()){let n=this.command.getSubcommand(t);if(n){await this.handleSubcommandRoute(n,r);return}if(!t.startsWith("-")){this.handleUnknownSubcommand(t);return}}await this.executeCommand(e)}handleUnknownSubcommand(e){let t=this.command.getSubcommands().map((r)=>r.name);Qe("subcommand",e,t,this.commandPath)}async handleSubcommandRoute(e,t){await new xe(e,this.env,this.commandPath).route(t,!1)}async executeCommand(e){if(this.isHelpRequest(e)){this.printHelp(this.command);return}if(this.command.hasSubcommands()&&!this.command.execute){this.printHelp(this.command);return}if(!this.command.execute)return;let{args:r,rawPositionals:n}=Wt(e,this.command.args),i=qt(n,this.command.positionals),s=wn(i,this.command.positionals);if(s)b.error(s),console.error(`
|
|
84
|
+
Run "${this.commandPath} --help" for usage.`),process.exit(1);let o=this.command.prompts&&n.length===0&&process.stdout.isTTY===!0,l=this.buildContext(i,r,o);await this.command.execute(l)}async routeInteractive(){if(!this.command.hasSubcommands()){await this.executeInteractive();return}await this.runSubcommandMenu()}async executeInteractive(){let{args:e}=Wt([],this.command.args),t=qt([],this.command.positionals),r=this.buildContext(t,e,!0);await this.command.execute?.(r)}async runSubcommandMenu(){while(!0){let e=await this.promptSubcommandSelectionOrBack();if(e===null)return;if(e===Yt){this.options.onExit?.();return}let t=this.command.getSubcommand(e);if(t)this.selections.set(this.commandPath,e),await this.executeSubcommand(t)}}async promptSubcommandSelectionOrBack(){try{return await this.promptSubcommandSelection()}catch(e){if(e instanceof X){if(this.options.isRoot)this.options.onExit?.();return null}throw e}}async executeSubcommand(e){try{await new xe(e,this.env,this.commandPath,{selections:this.selections}).route([],!0)}catch(t){if(t instanceof X)return;if(t instanceof w){if(b.warn(c.yellow(t.message)),t.hint)b.info(c.dim(t.hint));ve(t);return}throw t}}promptSubcommandSelection(){let{isRoot:e}=this.options,t=e?{hint:this.env.cli.exitHint,label:this.env.cli.exitLabel??"Exit",value:Yt}:{hint:"Return to previous menu",label:"Back",value:Yt};return me({message:e?this.env.cli.promptMessage??"Select:":`${this.command.name}:`,initialValue:this.selections.get(this.commandPath),options:[...this.command.getSubcommands().map((r)=>({hint:r.description,label:r.name,value:r.name})),t]})}buildContext(e,t,r){return{...this.env,args:t,interactive:r,positionals:e}}isHelpRequest(e){let t=e.indexOf("--"),r=t<0?e:e.slice(0,t);return r.includes("--help")||r.includes("-h")}printHelp(e){let t=e===this.command?this.prefix:this.commandPath;console.log(this.env.formatHelp(e,t))}}var ks={clearOnStart:!0,exitHint:"Close the CLI",exitLabel:"Exit",goodbyeMessage:"Goodbye!",name:"CLI",promptMessage:"What would you like to do?"};class Xt{commands=new Map;metadata;configManager;help;bin;globalArgs={help:{alias:"h",description:"Show help",type:"boolean"},interactive:{alias:"i",description:"Open interactive mode",type:"boolean"},version:{alias:"v",description:"Show version",type:"boolean"}};constructor(e,t){this.configManager=e,this.metadata={...ks,...t},this.bin=this.metadata.bin??this.metadata.name,this.init(),this.help=new zt({bin:this.bin,description:this.metadata.description,name:this.metadata.name,version:this.metadata.version},this.commands,this.globalArgs)}registerCommands(e){for(let t of e)t.init?.(),this.commands.set(t.name,t)}async run(e=process.argv.slice(2)){try{let{command:t,flags:r,restArgs:n}=yn(e,this.globalArgs);if(r.version&&!t)return console.log(this.help.version());if(r.interactive&&!t)return await this.runInteractive();if(!t)return console.log(this.help.global());if(t==="help")return this.printHelpFor(n[0]);await this.runDirect(t,n,!!r.help)}catch(t){this.handleError(t)}}printHelpFor(e){if(!e)return console.log(this.help.global());let t=this.commands.get(e);if(!t)return this.handleUnknownCommand(e);console.log(this.help.command(t))}async runInteractive(){this.showIntro();let e=this.createVirtualRootCommand(),t={cli:this.metadata,config:this.configManager,formatHelp:this.help.command.bind(this.help)},r=new xe(e,t,this.bin,{isRoot:!0,onExit:()=>this.exit()});try{await r.route([],!0)}catch(n){this.handleError(n)}}createVirtualRootCommand(){return{description:this.metadata.description,getSubcommand:(e)=>this.commands.get(e),getSubcommands:()=>Array.from(this.commands.values()),hasSubcommands:()=>!0,name:this.metadata.name}}exit(){We(this.metadata.goodbyeMessage),process.exit(0)}async runDirect(e,t,r){let n=this.commands.get(e);if(!n)return this.handleUnknownCommand(e);let i=t.length>0&&!t[0]?.startsWith("-");if(r&&!i)return console.log(this.help.command(n));try{let s={cli:this.metadata,config:this.configManager,formatHelp:this.help.command.bind(this.help)};await new xe(n,s,this.bin).route(t,!1)}catch(s){this.handleError(s)}}showIntro(){if(this.metadata.clearOnStart)console.clear();let e=this.metadata.version?` v${this.metadata.version}`:"";Ke(`${c.bgCyan(c.black(` ${this.metadata.name}${e} `))}`)}handleUnknownCommand(e){let t=Array.from(this.commands.keys());Qe("command",e,t,this.bin)}handleError(e){if(this.metadata.onError?.(e))return;if(e instanceof X)process.exit(0);if(e instanceof w){if(b.warn(c.yellow(e.message)),e.hint)b.info(c.dim(e.hint));ve(e),process.exit(e.exitCode)}if(e instanceof Error)b.error(c.red(e.message)),ve(e),process.exit(1);console.error(c.red("Error:"),e),process.exit(1)}}class Oe{args={};positionals={};prompts=!1;subcommands=new Map;registerSubcommands(e){for(let t of e)t.init?.(),this.subcommands.set(t.name,t)}hasSubcommands(){return this.subcommands.size>0}getSubcommand(e){return this.subcommands.get(e)}getSubcommands(){return Array.from(this.subcommands.values())}}function Q(e){return e}function M(e){return e}import z from"fs";import Sn from"path";var tt=(e)=>(e.map=(t)=>tt((r)=>t(e(r))),e.or=(t)=>tt((r)=>{try{return e(r)}catch(n){return t(r)}}),e),ee=()=>"bad banditype"();var Zt=(e)=>tt((t)=>typeof t===typeof e?t:ee()),Cn=()=>Zt(""),Se=()=>Zt(0),rt=()=>Zt(!0);var Qt=(e)=>tt((t)=>t instanceof e?t:ee());var xn=(e)=>Qt(Array).map((t)=>t.map(e));function tr(e,t){JSON.parse(e);let r=JSON.parse(JSON.stringify(t)),n=As(e),i=e.match(/^[\t ]+(?=\S)/m)?.[0]??" ",s=e.includes(`\r
|
|
85
85
|
`)?`\r
|
|
86
86
|
`:`
|
|
87
|
-
`,o=/\s/.test(
|
|
88
|
-
`));let
|
|
89
|
-
`));let
|
|
90
|
-
`)?
|
|
91
|
-
`))});return`${
|
|
92
|
-
`,
|
|
93
|
-
`,s+
|
|
94
|
-
`;if(z.writeFileSync(r,i,{flag:"wx",mode:n}),n!==void 0)z.chmodSync(r,n);z.renameSync(r,
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
`)}function ht(t){let e=t,r=0;while(e>=dn&&r<mn.length-1)e/=dn,r++;return`${r===0?String(e):e.toFixed(Zi)} ${mn[r]??"B"}`}var ts=/(?:^|\/)github\/([^/]+)\/externals$/;function ie(t){return t.match(ts)?.[1]??null}var gn="actions/runner",ps={linux:"linux-x64",osx:"osx-arm64",windows:"win-x64"},Ze=["bin"],Qe=["externals"],ds=1,ms=5000,hs=50,fs=5000,gs=50;class tr{profile;sharedPath=null;constructor(t){this.profile=t}async download(){let t=ps[this.profile.os],e=await this.getLatestVersion(),r=P(tt,"github",e);if(await this.isCompleteDownload(r))return this.sharedPath=r,{path:r,version:e};let n=P(tt,"github");await se(n,{recursive:!0});let i=await ss(P(tt,`.github-${e}-`)),s=`actions-runner-${t}-${e}.tar.gz`,o=P(i,s),a=`https://github.com/${gn}/releases/download/v${e}/${s}`;try{if(await Bun.$`curl --fail --silent --show-error --location -o ${o} ${a}`,await Bun.$`tar xzf ${o} -C ${i}`,await fn(o),!await this.isCompleteDownload(i))throw new v(`Runner v${e} archive is incomplete`);await this.publishDownload(i,r,e)}finally{await ft(i,{force:!0,recursive:!0})}return this.sharedPath=r,{path:r,version:e}}async create(t){let e=await this.ensureDownloaded(),r=[];for(let n of t){let i=P(this.profile.directory,n);if(Ct(i))throw new v(`Runner directory already exists: ${i}`,"Remove or recover this runner before creating it again");try{await this.setupRunnerDir(e,i),await this.registerRunner(i,n)}catch(s){if(!Ct(P(i,".runner")))await ft(i,{force:!0,recursive:!0});throw s}r.push({directory:i,id:n,name:n,status:"registered"})}return r}async remove(t){for(let e of t){let r=P(this.profile.directory,e);if(!Ct(P(r,".runner")))continue;let n=await this.fetchRemovalToken(r),i=await Bun.$`bash ./config.sh remove --token ${n}`.cwd(r).quiet().nothrow();if(i.exitCode!==0)throw new v(`Failed to deregister runner ${e}`,i.stderr.toString().trim()||"Runner files have been preserved; retry removal")}}async start(t){for(let e of t){let r=P(this.profile.directory,e),n=await this.readPidFile(r);if(n&&this.isProcessRunning(n))continue;let i=es("bash",["./run.sh"],{cwd:Xe(r),detached:!0,stdio:"ignore"});if(await rs(i,"spawn"),i.pid===void 0)throw new v(`Failed to start runner ${e}`);i.unref(),await this.writePidFile(r,i.pid)}}async stop(t){for(let e of t){let r=P(this.profile.directory,e),n=await this.readPidFile(r);if(n&&this.isProcessRunning(n)){try{process.kill(-n,"SIGTERM")}catch(s){if(s.code!=="ESRCH")throw s;try{process.kill(n,"SIGTERM")}catch(o){if(o.code!=="ESRCH")throw o}}let i=Date.now()+ms;while(this.isProcessRunning(n)){if(Date.now()>=i)throw new v(`Runner ${e} did not stop`,"Wait for its current job to finish and retry");await Bun.sleep(hs)}}await this.removePidFile(r)}}async list(){let t=this.profile.directory;if(!Ct(t))return[];let e=await hn(t,{withFileTypes:!0}),r=[];for(let n of e){if(!n.isDirectory())continue;let i=P(t,n.name);r.push(await this.getRunnerStatus(i,n.name))}return r}async currentVersion(t){let e=P(this.profile.directory,t);return this.detectVersion(e)}async logFiles(t){return ne(P(this.profile.directory,t,re))}async update(t){let e=await this.ensureDownloaded();for(let r of t){let n=P(this.profile.directory,r);await this.removeRunnerBinaries(n),await this.setupRunnerDir(e,n)}}async getRunnerStatus(t,e){let r=await this.readRunnerName(t),n=await this.readPidFile(t),i=n!==null&&this.isProcessRunning(n),s="unknown";if(i)s="running";else if(Ct(P(t,".runner")))s="registered";return{directory:t,id:e,name:r,pid:i?n:void 0,status:s}}async ensureDownloaded(){if(this.sharedPath)return this.sharedPath;let{path:t}=await this.download();return t}async isCompleteDownload(t){try{return(await Promise.all(["bin","externals","config.sh","run.sh"].map((r)=>ls(P(t,r))))).every((r,n)=>n<Ze.length+Qe.length?r.isDirectory():r.isFile())}catch(e){if(e.code==="ENOENT")return!1;throw e}}async publishDownload(t,e,r){let n=P(tt,`.github-${r}.lock`),i=Date.now()+fs;while(!0)try{await se(n);break}catch(s){if(s.code!=="EEXIST")throw s;if(Date.now()>=i)throw new v(`Runner download is locked: ${n}`,"Retry after other Hydra downloads finish; remove a stale lock only when no download is running");await Bun.sleep(gs)}try{if(await this.isCompleteDownload(e))return;await ft(e,{force:!0,recursive:!0}),await as(t,e)}finally{await ft(n,{force:!0,recursive:!0})}}async setupRunnerDir(t,e){await se(e,{recursive:!0});for(let r of Ze)await this.hardLinkDir(P(t,r),P(e,r));for(let r of Qe){let n=Xe(t,r),i=P(e,r);if(!Ct(i))await us(n,i)}for(let r of["*.sh","*.sh.template"]){let n=await Array.fromAsync(new Bun.Glob(r).scan(t));for(let i of n)await ns(P(t,i),P(e,i))}}async registerRunner(t,e){let r=await this.fetchRegistrationToken(),n=["--url",this.profile.url,"--token",r,"--name",e,"--unattended","--disableupdate"];if(this.profile.labels)n.push("--labels",this.profile.labels);if(this.profile.runnerGroup)n.push("--runnergroup",this.profile.runnerGroup);await Bun.$`bash ./config.sh ${n}`.cwd(t).quiet()}async fetchRegistrationToken(){return this.fetchRunnerToken(mt(this.profile.url),"registration-token")}async fetchRemovalToken(t){let e=await Ye(P(t,".runner"),"utf-8"),n=JSON.parse(this.stripBom(e)).gitHubUrl;if(!n)throw Error("Cannot determine GitHub URL from runner config");return this.fetchRunnerToken(mt(n),"remove-token")}async fetchRunnerToken(t,e){let r=t.kind==="repo"?`repos/${t.owner}/${t.repo}/actions/runners/${e}`:`orgs/${t.org}/actions/runners/${e}`,n=t.kind==="repo"?"gh needs admin access to the repository":"gh needs the admin:org scope",i=await Bun.$`gh api ${r} --method POST --jq .token`.quiet().nothrow();if(i.exitCode!==0){let o=i.stderr.toString().trim();throw Error(`gh api ${r} failed for ${Ke(t)} (${n})${o?`: ${o}`:""}`)}let s=i.stdout.toString().trim();if(!s)throw Error(`Failed to fetch ${e} for ${Ke(t)}`);return s}async getLatestVersion(){return(await Bun.$`gh api repos/${gn}/releases/latest --jq .tag_name`.quiet()).stdout.toString().trim().replace(/^v/,"")}pidFilePath(t){return Xe(t,".pid")}async writePidFile(t,e){await cs(this.pidFilePath(t),String(e))}async readPidFile(t){try{let e=await Ye(this.pidFilePath(t),"utf-8"),r=Number(e.trim());return Number.isSafeInteger(r)&&r>ds?r:null}catch{return null}}async removePidFile(t){try{await fn(this.pidFilePath(t))}catch{}}async readRunnerName(t){try{let e=await Ye(P(t,".runner"),"utf-8");return JSON.parse(this.stripBom(e)).agentName??"unknown"}catch{return"unknown"}}async hardLinkDir(t,e){await se(e,{recursive:!0});let r=await hn(t,{withFileTypes:!0});for(let n of r){let i=P(t,n.name),s=P(e,n.name);if(n.isDirectory())await this.hardLinkDir(i,s);else await is(i,s)}}async detectVersion(t){try{let e=await os(P(t,"externals"));return ie(e)}catch{return null}}async removeRunnerBinaries(t){for(let e of Ze)await ft(P(t,e),{force:!0,recursive:!0});for(let e of Qe)await ft(P(t,e),{force:!0});for(let e of["*.sh","*.sh.template"]){let r=await Array.fromAsync(new Bun.Glob(e).scan(t));for(let n of r)await ft(P(t,n),{force:!0})}}stripBom(t){return t.replace(/^\uFEFF/,"")}isProcessRunning(t){try{return process.kill(t,0),!0}catch(e){return e.code==="EPERM"}}}import{existsSync as er}from"fs";import{readdir as rr,readFile as ys,readlink as $s,rm as nr,stat as vs}from"fs/promises";import{join as lt}from"path";var ws="_work",bs="externals",Cs=".pid",xs="github",Ss=["runner","worker"],Rs=24,$n=3600000,yn=10,Es=1,Is=Qt().map((t)=>Number.isSafeInteger(t)&&t>Es?t:bt());function oe(t){return{auto:t?.auto??an,intervalHours:t?.intervalHours??ln,lastRun:t?.lastRun,olderThanDays:t?.olderThanDays??ee,targets:t?.targets??un}}function Ps(t,e){return t-e*Rs*$n}function ir(t,e){if(!t.lastRun)return!0;let r=Date.parse(t.lastRun);if(Number.isNaN(r))return!0;return e-r>=t.intervalHours*$n}function _s(t,e){let r=Je(t),n=new Set;for(let i of Ss){let s=r.find((o)=>o.type===i);if(s)n.add(s.path)}return r.filter((i)=>!n.has(i.path)&&i.mtime.getTime()<e)}function As(t){return[...t].sort(Ns)[0]??null}function Ts(t,e){let r=As(t),n=new Set(e);return t.filter((i)=>i!==r&&!n.has(i))}function ae(t){return Object.values(t).reduce((e,r)=>e+r.freedBytes,0)}async function Ds(t){let e;try{e=await ys(lt(t,Cs),"utf-8")}catch(n){if(n.code==="ENOENT")return!1;throw n}let r;try{r=Is(Number(e.trim()))}catch{return!1}try{return process.kill(r,0),!0}catch(n){let i=n.code;if(i==="EPERM")return!0;if(i==="ESRCH")return!1;throw n}}async function sr(t){if(!er(t))return 0;let e=await rr(t,{withFileTypes:!0}),r=0;for(let n of e){if(n.isSymbolicLink())continue;let i=lt(t,n.name);if(n.isDirectory()){r+=await sr(i);continue}if(n.isFile()){let s=await vs(i);r+=s.size}}return r}async function le(t){let e=Ps(t.now,t.olderThanDays),r={};for(let n of t.targets)switch(n){case"logs":r.logs=await Os(t.entries,e,t.dryRun);break;case"work":r.work=await ks(t.entries,t.dryRun);break;case"shared":r.shared=await Ls(t.entries,t.dryRun,t.sharedDir);break}return r}async function Os(t,e,r){let n={freedBytes:0,removed:0,skipped:[]};for(let i of t){let s=await ne(lt(i.directory,re));for(let o of _s(s,e)){if(!r)await nr(o.path,{force:!0});n.removed++,n.freedBytes+=o.size}}return n}async function ks(t,e){let r={freedBytes:0,removed:0,skipped:[]};for(let n of t){if(await Ds(n.directory)){r.skipped.push(n.id);continue}let i=lt(n.directory,ws);if(!er(i))continue;let s=await rr(i);if(s.length===0)continue;if(r.freedBytes+=await sr(i),r.removed+=s.length,e)continue;for(let o of s)await nr(lt(i,o),{force:!0,recursive:!0})}return r}async function Ls(t,e,r){let n={freedBytes:0,removed:0,skipped:[]},i=r??lt(tt,xs);if(!er(i))return n;let o=(await rr(i,{withFileTypes:!0})).filter((l)=>l.isDirectory()).map((l)=>l.name),a=await Fs(t);for(let l of Ts(o,a)){let u=lt(i,l);if(n.freedBytes+=await sr(u),n.removed++,!e)await nr(u,{force:!0,recursive:!0})}return n}async function Fs(t){let e=[];for(let r of t)try{let n=await $s(lt(r.directory,bs)),i=ie(n);if(i)e.push(i)}catch{}return e}function Ns(t,e){let r=t.split(".").map((s)=>Number.parseInt(s,yn)),n=e.split(".").map((s)=>Number.parseInt(s,yn)),i=Math.max(r.length,n.length);for(let s=0;s<i;s++){let o=r[s]??0,a=n[s]??0,l=Number.isNaN(o)?0:o,u=Number.isNaN(a)?0:a;if(l!==u)return u-l}return 0}function U(t){switch(t.provider){case"github":return new tr(t);default:throw Error(`Runner provider "${t.provider}" is not yet implemented`)}}var wn=0,Ms={logs:"file(s)",shared:"version(s)",work:"item(s)"},vn=Q({days:{alias:"d",default:wn,description:"Override log retention",displayDefault:`config or ${ee} days`,type:"number"},dryRun:{alias:"n",default:!1,description:"Preview cleanup without deleting",type:"boolean"},logs:{alias:"l",default:!1,description:"Remove old diagnostic logs",type:"boolean"},shared:{alias:"s",default:!1,description:"Remove unused runner versions",type:"boolean"},work:{alias:"w",default:!1,description:"Clear stopped runners' workspaces",type:"boolean"},yes:{alias:"y",default:!1,description:"Skip workspace confirmation",type:"boolean"}});class ue extends T{name="cleanup";description="Remove logs, workspaces, and old runner versions";args=vn;async execute(t){N(t.args,vn,"Run 'hydra cleanup --help' for supported options");let e=t.config.get("profiles");if(Object.keys(e).length===0)throw new v("No profiles configured","Run hydra init to set up a profile");let r=oe(t.config.get("cleanup")),n=this.explicitTargets(t),i=n.length>0?n:r.targets,s=t.args.days>wn?t.args.days:r.olderThanDays;if(i.includes("work")&&!t.args.dryRun&&!t.args.yes){if(!await ct({message:"Delete _work contents (checkouts and caches) of stopped runners?"}))i=i.filter((l)=>l!=="work")}let o=await le({dryRun:t.args.dryRun,entries:t.config.get("runners")??[],now:Date.now(),olderThanDays:s,targets:i});if(this.printReport(o,t.args.dryRun),t.args.dryRun)return;t.config.set("cleanup",{...t.config.get("cleanup"),lastRun:new Date().toISOString()})}explicitTargets(t){let e=[];if(t.args.logs)e.push("logs");if(t.args.work)e.push("work");if(t.args.shared)e.push("shared");return e}printReport(t,e){let r=[];for(let[s,o]of Object.entries(t)){if(!o)continue;let a=Ms[s],l=ht(o.freedBytes),u=e?`would remove ${o.removed} ${a}, ${l}`:`removed ${o.removed} ${a}, freed ${l}`;if(r.push(`${d.bold(s)}: ${o.removed>0?u:d.dim("nothing to do")}`),o.skipped.length>0)r.push(d.dim(` skipped running runner(s): ${o.skipped.join(", ")}`))}if(r.length===0){w.info(d.dim("Nothing to clean."));return}let n=ae(t),i=e?"Would free":"Freed";r.push(d.dim(`${i} ${ht(n)} total`)),w.info(r.join(`
|
|
98
|
-
`
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
87
|
+
`,o=/\s/.test(e[n.start+1]??"")||n.entries.some((u)=>/:\s+$/.test(e.slice(u.start,u.node.start)))?" ":"";function l(u,f){let m=e.slice(u.start,u.end);if(JSON.stringify(u.value)===JSON.stringify(f))return m;let d=Array.isArray(f);if(f===null||typeof f!=="object"||u.value===null||typeof u.value!=="object"||Array.isArray(u.value)!==d)return a(f,u.start,m.includes(`
|
|
88
|
+
`));let y=d?f.map((v,_)=>String(_)):[...new Set([...u.entries.map((v)=>v.key).filter((v)=>Object.hasOwn(f,v)),...Object.keys(f)])];if(y.length===0)return d?"[]":"{}";let h=u.entries[0],g=u.entries.at(-1);if(!h||!g)return a(f,u.start,m.includes(`
|
|
89
|
+
`));let S=(v)=>d?f[Number(v)]:f[v];if(y.length===u.entries.length&&y.every((v,_)=>v===u.entries[_]?.key)){let v=u.start,_="";for(let U of u.entries)_+=e.slice(v,U.node.start)+l(U.node,S(U.key)),v=U.node.end;return _+e.slice(v,u.end)}let A=e.slice(u.start+1,h.start),C=e.slice(g.node.end,u.end-1),D=u.entries[1],T=D?e.slice(h.node.end,D.start):`,${A.includes(`
|
|
90
|
+
`)?A:o}`,R=d?"":e.slice(h.start,h.node.start).match(/:\s*$/)?.[0]??": ",N=new Map(u.entries.map((v)=>[v.key,v])),O=y.map((v)=>{let _=N.get(v);if(_)return e.slice(_.start,_.node.start)+l(_.node,S(v));return(d?"":JSON.stringify(v)+R)+a(S(v),h.start,A.includes(`
|
|
91
|
+
`))});return`${d?"[":"{"}${A}${O.join(T)}${C}${d?"]":"}"}`}function a(u,f,m){if(!m)return er(u,o);let d=e.lastIndexOf(`
|
|
92
|
+
`,f-1)+1,y=e.slice(d,f).match(/^[\t ]*/)?.[0]??"";return JSON.stringify(u,null,i).replaceAll(`
|
|
93
|
+
`,s+y)}return e.slice(0,n.start)+l(n,r)+e.slice(n.end)}function er(e,t){if(Array.isArray(e))return`[${e.map((i)=>er(i,t)).join(`,${t}`)}]`;if(e===null||typeof e!=="object")return JSON.stringify(e);let r=Object.entries(e);if(r.length===0)return"{}";let n=r.map(([i,s])=>`${JSON.stringify(i)}:${t}${er(s,t)}`);return`{${t}${n.join(`,${t}`)}${t}}`}function As(e){let t=e.matchAll(/"(?:[^"\\]|\\.)*"|[^\s{}[\],:]+|[{}[\],:]/g),r=t.next().value;function n(){if(!r)throw Error("Unexpected end of JSON");let s=r;return r=t.next().value,s}function i(){let s=n(),o=[],l=s[0]==="{";if(!l&&s[0]!=="[")return{end:s.index+s[0].length,entries:o,start:s.index,value:JSON.parse(s[0])};let a=l?"}":"]";while(r&&r[0]!==a){let f=r.index,m=l?JSON.parse(n()[0]):String(o.length);if(l)n();if(o.push({key:m,node:i(),start:f}),r?.[0]===",")n()}let u=n().index+1;return{end:u,entries:o,start:s.index,value:JSON.parse(e.slice(s.index,u))}}return i()}var Is=2,Ps=Qt(Object);class rr{config;configPath;defaultConfig;loadError;constructor(e,t){this.configPath=Sn.resolve(e),this.defaultConfig=t,this.config=this.loadConfig()}get path(){return this.configPath}get(e){return this.config[e]}set(e,t){this.config={...this.config,[e]:t},this.save()}delete(e){delete this.config[e],this.save()}exists(){return z.existsSync(this.configPath)}getAll(){return structuredClone(this.config)}save(e){if(this.loadError)throw Error(`Cannot overwrite unreadable config at ${this.configPath}; repair the file and try again`,{cause:this.loadError});if(e)this.config={...e};this.ensureDirectoryExists();let t=this.exists()?z.realpathSync(this.configPath):this.configPath,r=`${t}.${crypto.randomUUID()}.tmp`;try{let n=this.exists()?z.statSync(this.configPath).mode:void 0,i=this.exists()?tr(z.readFileSync(this.configPath,"utf-8"),this.config):`${JSON.stringify(this.config,null,Is)}
|
|
94
|
+
`;if(z.writeFileSync(r,i,{flag:"wx",mode:n}),n!==void 0)z.chmodSync(r,n);z.renameSync(r,t)}catch(n){throw Error(`Failed to save config to ${this.configPath}`,{cause:n})}finally{z.rmSync(r,{force:!0})}}ensureDirectoryExists(){let e=Sn.dirname(this.configPath);if(!z.existsSync(e))try{z.mkdirSync(e,{recursive:!0})}catch(t){throw Error(`Failed to create config directory at ${e}`,{cause:t})}}loadConfig(){if(!z.existsSync(this.configPath))return structuredClone(this.defaultConfig);try{let e=z.readFileSync(this.configPath,"utf-8"),t=JSON.parse(e);if(Array.isArray(t))throw Error("Config must be an object");let r=Ps(t);return et(structuredClone(this.defaultConfig),r)}catch(e){return this.loadError=e,console.warn(`Config file corrupted at ${this.configPath}. Using defaults.`),structuredClone(this.defaultConfig)}}}var Rn="0.12.0";class I extends Oe{}var nt="hydra";var En=".hydra/config.json",te=".hydra/shared",De=".hydra/runners",Ne="default",_n={profiles:{}},kn=!1,An=24,it=7,In=["logs","shared"],Pn=!1,Tn=!0,On=!0,Dn=!0,Nn=120,Ln=["github.com","api.github.com","objects.githubusercontent.com","pipelines.actions.githubusercontent.com"];import{spawn as lo}from"child_process";import{once as uo}from"events";import{existsSync as ye}from"fs";import{cp as co,link as po,mkdir as mt,mkdtemp as mo,readdir as Yn,readFile as mr,readlink as fo,rename as ho,rm as $e,stat as Xn,symlink as go,unlink as Zn,writeFile as yo}from"fs/promises";import{join as k,resolve as fr}from"path";var Os=new Set(["github.com","www.github.com"]);var Fn=/\.git$/;function he(e){let t;try{t=new URL(e)}catch{throw Error(`Invalid GitHub URL: ${e}`)}if(t.protocol!=="https:"&&t.protocol!=="http:")throw Error(`Invalid GitHub URL (expected http(s)): ${e}`);if(!Os.has(t.hostname))throw Error(`Invalid GitHub URL (host must be github.com): ${e}`);let r=t.pathname.split("/").filter(Boolean);if(r.length===1){let n=r[0]?.replace(Fn,"");if(!n)throw Error(`Invalid GitHub URL: ${e}`);return{kind:"org",org:n}}if(r.length===2){let n=r[0],i=r[1]?.replace(Fn,"");if(!n||!i)throw Error(`Invalid GitHub URL: ${e}`);return{kind:"repo",owner:n,repo:i}}throw Error(`Invalid GitHub URL (expected github.com/<owner>/<repo> or github.com/<org>): ${e}`)}function nr(e){return e.kind==="repo"?`${e.owner}/${e.repo}`:e.org}import{chmod as Ns,mkdir as Ls,readFile as Fs,rename as Hs,rm as Ms,stat as js,writeFile as Vn}from"fs/promises";import{dirname as Us,join as lr,resolve as ot}from"path";import{isAbsolute as Ds}from"path";var ir="# Generated by Hydra; hydra update rewrites this file.";function Hn(e){return`'${e.replaceAll("'","'\\''")}'`}function Mn(){return`#!/usr/bin/env bash
|
|
95
|
+
${ir}
|
|
96
|
+
# Disabled in the Hydra profile; kept so runners started before the change still find it.
|
|
97
|
+
exit 0
|
|
98
|
+
`}function jn(e){return`#!/usr/bin/env bash
|
|
99
|
+
${ir}
|
|
100
|
+
# Waits until every host resolves, then lets the job start; fails the job on timeout.
|
|
101
|
+
set +e
|
|
102
|
+
|
|
103
|
+
hosts=(${e.hosts.map(Hn).join(" ")})
|
|
104
|
+
timeout_seconds=${e.timeoutSeconds}
|
|
105
|
+
|
|
106
|
+
if command -v dscacheutil >/dev/null 2>&1; then
|
|
107
|
+
resolves() { dscacheutil -q host -a name "$1" 2>/dev/null | grep -q -e '^ip_address:' -e '^ipv6_address:'; }
|
|
108
|
+
elif command -v getent >/dev/null 2>&1; then
|
|
109
|
+
resolves() { getent hosts "$1" >/dev/null 2>&1; }
|
|
110
|
+
else
|
|
111
|
+
echo "::warning::Hydra skipped the DNS check: neither dscacheutil nor getent is available"
|
|
112
|
+
exit 0
|
|
113
|
+
fi
|
|
114
|
+
|
|
115
|
+
pending=("\${hosts[@]}")
|
|
116
|
+
started=$SECONDS
|
|
117
|
+
waited=false
|
|
118
|
+
while :; do
|
|
119
|
+
unresolved=()
|
|
120
|
+
for host in "\${pending[@]}"; do
|
|
121
|
+
resolves "$host" || unresolved+=("$host")
|
|
122
|
+
done
|
|
123
|
+
elapsed=$((SECONDS - started))
|
|
124
|
+
if [ "\${#unresolved[@]}" -eq 0 ]; then
|
|
125
|
+
if [ "$waited" = true ]; then
|
|
126
|
+
echo "::warning::DNS recovered after $elapsed s"
|
|
127
|
+
fi
|
|
128
|
+
exit 0
|
|
129
|
+
fi
|
|
130
|
+
if [ "$elapsed" -ge "$timeout_seconds" ]; then
|
|
131
|
+
echo "::error::DNS did not recover within $timeout_seconds s; unresolved: \${unresolved[*]}"
|
|
132
|
+
exit 1
|
|
133
|
+
fi
|
|
134
|
+
if [ "$waited" = false ]; then
|
|
135
|
+
echo "Waiting up to $timeout_seconds s for DNS: \${unresolved[*]}"
|
|
136
|
+
fi
|
|
137
|
+
waited=true
|
|
138
|
+
pending=("\${unresolved[@]}")
|
|
139
|
+
sleep 1
|
|
140
|
+
done
|
|
141
|
+
`}function Un(e,t){if(!Ds(e)||e==="/")throw Error(`Refusing to generate a workspace cleanup hook for runner directory "${e}"`);return`#!/usr/bin/env bash
|
|
142
|
+
${ir}
|
|
143
|
+
# Removes this job's workspace and temporary files from this runner's _work directory.
|
|
144
|
+
# Always exits 0 so a cleanup problem never fails a finished job; problems become warnings.
|
|
145
|
+
set +e
|
|
146
|
+
trap 'exit 0' EXIT
|
|
147
|
+
shopt -s dotglob nullglob
|
|
148
|
+
|
|
149
|
+
runner_root=${Hn(e)}
|
|
150
|
+
keep_tool_cache=${t.keepToolCache}
|
|
151
|
+
|
|
152
|
+
note() { echo "Hydra cleanup: $*"; }
|
|
153
|
+
warn() { echo "::warning::Hydra cleanup: $*"; }
|
|
154
|
+
|
|
155
|
+
remove_entry() {
|
|
156
|
+
local errors
|
|
157
|
+
errors=$(rm -rf -- "$1" 2>&1 >/dev/null)
|
|
158
|
+
if [ -e "$1" ] || [ -L "$1" ]; then
|
|
159
|
+
warn "could not remove $1: \${errors%%$'\\n'*}"
|
|
160
|
+
return 1
|
|
161
|
+
fi
|
|
162
|
+
return 0
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
clear_dir() {
|
|
166
|
+
local dir=$1 entry name kept removed=0
|
|
167
|
+
shift
|
|
168
|
+
if [ -L "$dir" ]; then
|
|
169
|
+
warn "refusing to clear $dir: it is a symbolic link"
|
|
170
|
+
return
|
|
171
|
+
fi
|
|
172
|
+
[ -d "$dir" ] || return
|
|
173
|
+
for entry in "$dir"/*; do
|
|
174
|
+
name=\${entry##*/}
|
|
175
|
+
for kept in "$@"; do
|
|
176
|
+
[ "$name" = "$kept" ] && continue 2
|
|
177
|
+
done
|
|
178
|
+
remove_entry "$entry" && removed=$((removed + 1))
|
|
179
|
+
done
|
|
180
|
+
[ "$removed" -eq 0 ] || note "cleared $removed item(s) from $dir"
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
clean_job_workspace() {
|
|
184
|
+
local candidate=$1 resolved relative top
|
|
185
|
+
case "$candidate" in
|
|
186
|
+
/*) ;;
|
|
187
|
+
"")
|
|
188
|
+
warn "the job workspace is not set; leaving it in place"
|
|
189
|
+
return
|
|
190
|
+
;;
|
|
191
|
+
*)
|
|
192
|
+
warn "refusing to remove $candidate: it is not an absolute path"
|
|
193
|
+
return
|
|
194
|
+
;;
|
|
195
|
+
esac
|
|
196
|
+
resolved=$(cd -P -- "$candidate" 2>/dev/null && pwd -P)
|
|
197
|
+
if [ -z "$resolved" ]; then
|
|
198
|
+
note "the job workspace $candidate is already gone"
|
|
199
|
+
return
|
|
200
|
+
fi
|
|
201
|
+
case "$resolved" in
|
|
202
|
+
"$work_root"/?*) ;;
|
|
203
|
+
*)
|
|
204
|
+
warn "refusing to remove $candidate: it resolves to $resolved, which is not inside $work_root"
|
|
205
|
+
return
|
|
206
|
+
;;
|
|
207
|
+
esac
|
|
208
|
+
relative=\${resolved#"$work_root"/}
|
|
209
|
+
top=\${relative%%/*}
|
|
210
|
+
case "$top" in
|
|
211
|
+
"" | _*)
|
|
212
|
+
warn "refusing to remove $work_root/$top: it belongs to the runner"
|
|
213
|
+
return
|
|
214
|
+
;;
|
|
215
|
+
esac
|
|
216
|
+
remove_entry "$work_root/$top" && note "removed $work_root/$top"
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if [ -z "$runner_root" ] || [ "$runner_root" = / ]; then
|
|
220
|
+
warn "the runner directory is not set; nothing cleaned"
|
|
221
|
+
exit 0
|
|
222
|
+
fi
|
|
223
|
+
if [ -L "$runner_root/_work" ]; then
|
|
224
|
+
warn "refusing to clean $runner_root/_work: it is a symbolic link"
|
|
225
|
+
exit 0
|
|
226
|
+
fi
|
|
227
|
+
work_root=$(cd -P -- "$runner_root/_work" 2>/dev/null && pwd -P)
|
|
228
|
+
if [ -z "$work_root" ] || [ "$work_root" = / ]; then
|
|
229
|
+
note "no _work directory to clean"
|
|
230
|
+
exit 0
|
|
231
|
+
fi
|
|
232
|
+
cd -- "$work_root" || exit 0
|
|
233
|
+
|
|
234
|
+
clean_job_workspace "\${RUNNER_WORKSPACE:-\${GITHUB_WORKSPACE:-}}"
|
|
235
|
+
clear_dir "$work_root/_temp" _runner_file_commands _github_workflow
|
|
236
|
+
[ "$keep_tool_cache" = true ] || clear_dir "$work_root/_tool"
|
|
237
|
+
`}var Vs="hydra-hooks",Bn=".env",at={started:{envKey:"ACTIONS_RUNNER_HOOK_JOB_STARTED",file:"job-started.sh"},completed:{envKey:"ACTIONS_RUNNER_HOOK_JOB_COMPLETED",file:"job-completed.sh"}},Bs=Object.keys(at),Gs=493,Js=253,Ks=/^[A-Za-z0-9](?:[A-Za-z0-9.-]*[A-Za-z0-9])?$/,ar="Fix the profile's jobHooks in .hydra/config.json; see the Hydra reference for the allowed values",sr={dns:["enabled","hosts","timeoutSeconds"],workspaceCleanup:["enabled","keepToolCache"]},Ws=Cn().map((e)=>e.length<=Js&&Ks.test(e)?e:ee()),qs=xn(Ws).map((e)=>e.length>0?e:ee()),zs=Se().map((e)=>Number.isSafeInteger(e)&&e>0?e:ee());function Gn(){return{dns:{enabled:Dn,hosts:[...Ln],timeoutSeconds:Nn},workspaceCleanup:{enabled:Pn,keepToolCache:On}}}function lt(){let e=Gn();return{...e,workspaceCleanup:{...e.workspaceCleanup,enabled:Tn}}}function oe(e){let t=Gn(),r=or(e,"jobHooks",Object.keys(sr)),n=or(r.dns,"jobHooks.dns",sr.dns),i=or(r.workspaceCleanup,"jobHooks.workspaceCleanup",sr.workspaceCleanup);return{dns:{enabled:Le(rt(),n.enabled,t.dns.enabled,"jobHooks.dns.enabled","a boolean"),hosts:Le(qs,n.hosts,t.dns.hosts,"jobHooks.dns.hosts","a non-empty list of hostnames"),timeoutSeconds:Le(zs,n.timeoutSeconds,t.dns.timeoutSeconds,"jobHooks.dns.timeoutSeconds","a positive whole number of seconds")},workspaceCleanup:{enabled:Le(rt(),i.enabled,t.workspaceCleanup.enabled,"jobHooks.workspaceCleanup.enabled","a boolean"),keepToolCache:Le(rt(),i.keepToolCache,t.workspaceCleanup.keepToolCache,"jobHooks.workspaceCleanup.keepToolCache","a boolean")}}}function Jn(e,t){return lr(ot(e),Vs,at[t].file)}function Kn(e,t,r){switch(e){case"started":return r.dns.enabled?jn(r.dns):null;case"completed":return r.workspaceCleanup.enabled?Un(ot(t),r.workspaceCleanup):null}}function Ys(e,t){let r=new Map(t.map((o)=>[o.key,o])),n=new Set,i=e.split(`
|
|
238
|
+
`);if(i.at(-1)==="")i.pop();let s=[];for(let o of i){let l=o.indexOf("="),a=l>0?r.get(o.slice(0,l)):void 0;if(!a){s.push(o);continue}if(a.value===null){if(o.slice(l+1)!==a.managedPath)s.push(o);continue}if(n.has(a.key))continue;n.add(a.key),s.push(`${a.key}=${a.value}`)}for(let o of t)if(o.value!==null&&!n.has(o.key))s.push(`${o.key}=${o.value}`);return s.length>0?`${s.join(`
|
|
239
|
+
`)}
|
|
240
|
+
`:""}async function ut(e,t){let r=[],n=!1;for(let l of Bs){let a=Jn(e,l),u=Kn(l,e,t),f=await st(a),m=u??(f===null?null:Mn());if(m!==null&&m!==f)await eo(a,m),n=!0;r.push({key:at[l].envKey,managedPath:a,value:u===null?null:a})}let i=lr(ot(e),Bn),s=await st(i)??"",o=Ys(s,r);if(o===s)return{envChanged:!1,scriptsChanged:n};return await Vn(i,o),{envChanged:!0,scriptsChanged:n}}async function ur(e,t){let r=lr(ot(e),Bn),n=await st(r)??"",i=(s)=>Xs(Jn(e,s),Zs(n,at[s].envKey),Kn(s,e,t));return{envModifiedAt:await Qs(r),states:{completed:await i("completed"),started:await i("started")}}}async function Xs(e,t,r){if(r===null){if(t===void 0)return"off";return t===e?"outdated":"foreign"}if(t===void 0)return"missing";if(t!==e)return"foreign";return await st(e)===r?"configured":"outdated"}function Zs(e,t){let r=`${t}=`;return e.split(`
|
|
241
|
+
`).filter((n)=>n.startsWith(r)).map((n)=>n.slice(r.length)).at(-1)}async function Qs(e){try{return(await js(e)).mtime}catch(t){if(t.code==="ENOENT")return null;throw t}}function or(e,t,r){if(e===void 0)return{};if(typeof e!=="object"||e===null||Array.isArray(e))throw new w(`Invalid ${t}: expected an object`,ar);let n=Object.keys(e).find((i)=>!r.includes(i));if(n!==void 0)throw new w(`Unknown ${t} setting: ${n}`,ar);return e}function Le(e,t,r,n,i){if(t===void 0)return r;try{return e(t)}catch{throw new w(`Invalid ${n}: expected ${i}`,ar)}}async function st(e){try{return await Fs(e,"utf-8")}catch(t){if(t.code==="ENOENT")return null;throw t}}async function eo(e,t){let r=`${e}.${process.pid}.tmp`;await Ls(Us(e),{recursive:!0});try{await Vn(r,t),await Ns(r,Gs),await Hs(r,e)}finally{await Ms(r,{force:!0})}}import{existsSync as to}from"fs";import{readdir as ro,stat as no}from"fs/promises";import{join as io}from"path";var ct="_diag",Wn={runner:/^Runner_.+\.log$/,worker:/^Worker_.+\.log$/},qn=1024,zn=["B","KB","MB","GB"],so=1;function oo(e){if(Wn.worker.test(e))return"worker";if(Wn.runner.test(e))return"runner";return null}function cr(e){return[...e].sort((t,r)=>r.mtime.getTime()-t.mtime.getTime())}async function pt(e){if(!to(e))return[];let t=await ro(e,{withFileTypes:!0}),r=[];for(let n of t){if(!n.isFile())continue;let i=oo(n.name);if(!i)continue;let s=io(e,n.name),o=await no(s);r.push({mtime:o.mtime,name:n.name,path:s,size:o.size,type:i})}return cr(r)}function pr(e,t){let r=e.find((i)=>i.type===t);if(r)return{fallback:!1,file:r};let n=e[0];return n?{fallback:!0,file:n}:null}function dr(e,t){if(t<=0)return"";let r=e.endsWith(`
|
|
242
|
+
`)?e.slice(0,-1):e;if(r==="")return"";return r.split(`
|
|
243
|
+
`).slice(-t).join(`
|
|
244
|
+
`)}function ge(e){let t=e,r=0;while(t>=qn&&r<zn.length-1)t/=qn,r++;return`${r===0?String(t):t.toFixed(so)} ${zn[r]??"B"}`}var ao=/(?:^|\/)github\/([^/]+)\/externals$/;function dt(e){return e.match(ao)?.[1]??null}var Qn="actions/runner",$o={linux:"linux-x64",osx:"osx-arm64",windows:"win-x64"},hr=["bin"],gr=["externals"],wo=1,bo=5000,vo=50,Co=5000,xo=50;class yr{profile;sharedPath=null;constructor(e){this.profile=e}async download(){let e=$o[this.profile.os],t=await this.getLatestVersion(),r=k(te,"github",t);if(await this.isCompleteDownload(r))return this.sharedPath=r,{path:r,version:t};let n=k(te,"github");await mt(n,{recursive:!0});let i=await mo(k(te,`.github-${t}-`)),s=`actions-runner-${e}-${t}.tar.gz`,o=k(i,s),l=`https://github.com/${Qn}/releases/download/v${t}/${s}`;try{if(await Bun.$`curl --fail --silent --show-error --location -o ${o} ${l}`,await Bun.$`tar xzf ${o} -C ${i}`,await Zn(o),!await this.isCompleteDownload(i))throw new w(`Runner v${t} archive is incomplete`);await this.publishDownload(i,r,t)}finally{await $e(i,{force:!0,recursive:!0})}return this.sharedPath=r,{path:r,version:t}}async create(e){let t=oe(this.profile.jobHooks),r=await this.ensureDownloaded(),n=[];for(let i of e){let s=k(this.profile.directory,i);if(ye(s))throw new w(`Runner directory already exists: ${s}`,"Remove or recover this runner before creating it again");try{await this.setupRunnerDir(r,s),await this.registerRunner(s,i,t)}catch(o){if(!ye(k(s,".runner")))await $e(s,{force:!0,recursive:!0});throw o}n.push({directory:s,id:i,name:i,status:"registered"})}return n}async remove(e){for(let t of e){let r=k(this.profile.directory,t);if(!ye(k(r,".runner")))continue;let n=await this.fetchRemovalToken(r),i=await Bun.$`bash ./config.sh remove --token ${n}`.cwd(r).quiet().nothrow();if(i.exitCode!==0)throw new w(`Failed to deregister runner ${t}`,i.stderr.toString().trim()||"Runner files have been preserved; retry removal")}}async start(e){for(let t of e){let r=k(this.profile.directory,t),n=await this.readPidFile(r);if(n&&this.isProcessRunning(n))continue;let i=lo("bash",["./run.sh"],{cwd:fr(r),detached:!0,stdio:"ignore"});if(await uo(i,"spawn"),i.pid===void 0)throw new w(`Failed to start runner ${t}`);i.unref(),await this.writePidFile(r,i.pid)}}async stop(e){for(let t of e){let r=k(this.profile.directory,t),n=await this.readPidFile(r);if(n&&this.isProcessRunning(n)){try{process.kill(-n,"SIGTERM")}catch(s){if(s.code!=="ESRCH")throw s;try{process.kill(n,"SIGTERM")}catch(o){if(o.code!=="ESRCH")throw o}}let i=Date.now()+bo;while(this.isProcessRunning(n)){if(Date.now()>=i)throw new w(`Runner ${t} did not stop`,"Wait for its current job to finish and retry");await Bun.sleep(vo)}}await this.removePidFile(r)}}async list(){let e=this.profile.directory;if(!ye(e))return[];let t=await Yn(e,{withFileTypes:!0}),r=[];for(let n of t){if(!n.isDirectory())continue;let i=k(e,n.name);r.push(await this.getRunnerStatus(i,n.name))}return r}async currentVersion(e){let t=k(this.profile.directory,e);return this.detectVersion(t)}async logFiles(e){return pt(k(this.profile.directory,e,ct))}async update(e){let t=oe(this.profile.jobHooks),r=await this.ensureDownloaded();for(let n of e){let i=k(this.profile.directory,n);await this.removeRunnerBinaries(i),await this.setupRunnerDir(r,i),await ut(i,t)}}async configureJobHooks(e){let t=oe(this.profile.jobHooks),r=k(this.profile.directory,e);if(!ye(k(r,".runner")))return{envChanged:!1,scriptsChanged:!1};return ut(r,t)}async getRunnerStatus(e,t){let r=await this.readRunnerName(e),n=await this.readPidFile(e),i=n!==null&&this.isProcessRunning(n),s="unknown";if(i)s="running";else if(ye(k(e,".runner")))s="registered";let o=i?await this.readStartTime(e):void 0;return{directory:e,id:t,name:r,pid:i?n:void 0,startedAt:o,status:s}}async ensureDownloaded(){if(this.sharedPath)return this.sharedPath;let{path:e}=await this.download();return e}async isCompleteDownload(e){try{return(await Promise.all(["bin","externals","config.sh","run.sh"].map((r)=>Xn(k(e,r))))).every((r,n)=>n<hr.length+gr.length?r.isDirectory():r.isFile())}catch(t){if(t.code==="ENOENT")return!1;throw t}}async publishDownload(e,t,r){let n=k(te,`.github-${r}.lock`),i=Date.now()+Co;while(!0)try{await mt(n);break}catch(s){if(s.code!=="EEXIST")throw s;if(Date.now()>=i)throw new w(`Runner download is locked: ${n}`,"Retry after other Hydra downloads finish; remove a stale lock only when no download is running");await Bun.sleep(xo)}try{if(await this.isCompleteDownload(t))return;await $e(t,{force:!0,recursive:!0}),await ho(e,t)}finally{await $e(n,{force:!0,recursive:!0})}}async setupRunnerDir(e,t){await mt(t,{recursive:!0});for(let r of hr)await this.hardLinkDir(k(e,r),k(t,r));for(let r of gr){let n=fr(e,r),i=k(t,r);if(!ye(i))await go(n,i)}for(let r of["*.sh","*.sh.template"]){let n=await Array.fromAsync(new Bun.Glob(r).scan(e));for(let i of n)await co(k(e,i),k(t,i))}}async registerRunner(e,t,r){let n=await this.fetchRegistrationToken(),i=["--url",this.profile.url,"--token",n,"--name",t,"--unattended","--disableupdate"];if(this.profile.labels)i.push("--labels",this.profile.labels);if(this.profile.runnerGroup)i.push("--runnergroup",this.profile.runnerGroup);await Bun.$`bash ./config.sh ${i}`.cwd(e).quiet(),await ut(e,r)}async fetchRegistrationToken(){return this.fetchRunnerToken(he(this.profile.url),"registration-token")}async fetchRemovalToken(e){let t=await mr(k(e,".runner"),"utf-8"),n=JSON.parse(this.stripBom(t)).gitHubUrl;if(!n)throw Error("Cannot determine GitHub URL from runner config");return this.fetchRunnerToken(he(n),"remove-token")}async fetchRunnerToken(e,t){let r=e.kind==="repo"?`repos/${e.owner}/${e.repo}/actions/runners/${t}`:`orgs/${e.org}/actions/runners/${t}`,n=e.kind==="repo"?"gh needs admin access to the repository":"gh needs the admin:org scope",i=await Bun.$`gh api ${r} --method POST --jq .token`.quiet().nothrow();if(i.exitCode!==0){let o=i.stderr.toString().trim();throw Error(`gh api ${r} failed for ${nr(e)} (${n})${o?`: ${o}`:""}`)}let s=i.stdout.toString().trim();if(!s)throw Error(`Failed to fetch ${t} for ${nr(e)}`);return s}async getLatestVersion(){return(await Bun.$`gh api repos/${Qn}/releases/latest --jq .tag_name`.quiet()).stdout.toString().trim().replace(/^v/,"")}pidFilePath(e){return fr(e,".pid")}async writePidFile(e,t){await yo(this.pidFilePath(e),String(t))}async readPidFile(e){try{let t=await mr(this.pidFilePath(e),"utf-8"),r=Number(t.trim());return Number.isSafeInteger(r)&&r>wo?r:null}catch{return null}}async readStartTime(e){try{return(await Xn(this.pidFilePath(e))).mtime}catch{return}}async removePidFile(e){try{await Zn(this.pidFilePath(e))}catch{}}async readRunnerName(e){try{let t=await mr(k(e,".runner"),"utf-8");return JSON.parse(this.stripBom(t)).agentName??"unknown"}catch{return"unknown"}}async hardLinkDir(e,t){await mt(t,{recursive:!0});let r=await Yn(e,{withFileTypes:!0});for(let n of r){let i=k(e,n.name),s=k(t,n.name);if(n.isDirectory())await this.hardLinkDir(i,s);else await po(i,s)}}async detectVersion(e){try{let t=await fo(k(e,"externals"));return dt(t)}catch{return null}}async removeRunnerBinaries(e){for(let t of hr)await $e(k(e,t),{force:!0,recursive:!0});for(let t of gr)await $e(k(e,t),{force:!0});for(let t of["*.sh","*.sh.template"]){let r=await Array.fromAsync(new Bun.Glob(t).scan(e));for(let n of r)await $e(k(e,n),{force:!0})}}stripBom(e){return e.replace(/^\uFEFF/,"")}isProcessRunning(e){try{return process.kill(e,0),!0}catch(t){return t.code==="EPERM"}}}import{existsSync as $r}from"fs";import{readdir as wr,readFile as So,readlink as Ro,rm as br,stat as Eo}from"fs/promises";import{join as ce}from"path";var _o="_work",ko="externals",Ao=".pid",Io="github",Po=["runner","worker"],To=24,ti=3600000,ei=10,Oo=1,Do=Se().map((e)=>Number.isSafeInteger(e)&&e>Oo?e:ee());function ft(e){return{auto:e?.auto??kn,intervalHours:e?.intervalHours??An,lastRun:e?.lastRun,olderThanDays:e?.olderThanDays??it,targets:e?.targets??In}}function No(e,t){return e-t*To*ti}function vr(e,t){if(!e.lastRun)return!0;let r=Date.parse(e.lastRun);if(Number.isNaN(r))return!0;return t-r>=e.intervalHours*ti}function Lo(e,t){let r=cr(e),n=new Set;for(let i of Po){let s=r.find((o)=>o.type===i);if(s)n.add(s.path)}return r.filter((i)=>!n.has(i.path)&&i.mtime.getTime()<t)}function Fo(e){return[...e].sort(Go)[0]??null}function Ho(e,t){let r=Fo(e),n=new Set(t);return e.filter((i)=>i!==r&&!n.has(i))}function ht(e){return Object.values(e).reduce((t,r)=>t+r.freedBytes,0)}async function Mo(e){let t;try{t=await So(ce(e,Ao),"utf-8")}catch(n){if(n.code==="ENOENT")return!1;throw n}let r;try{r=Do(Number(t.trim()))}catch{return!1}try{return process.kill(r,0),!0}catch(n){let i=n.code;if(i==="EPERM")return!0;if(i==="ESRCH")return!1;throw n}}async function Cr(e){if(!$r(e))return 0;let t=await wr(e,{withFileTypes:!0}),r=0;for(let n of t){if(n.isSymbolicLink())continue;let i=ce(e,n.name);if(n.isDirectory()){r+=await Cr(i);continue}if(n.isFile()){let s=await Eo(i);r+=s.size}}return r}async function gt(e){let t=No(e.now,e.olderThanDays),r={};for(let n of e.targets)switch(n){case"logs":r.logs=await jo(e.entries,t,e.dryRun);break;case"work":r.work=await Uo(e.entries,e.dryRun);break;case"shared":r.shared=await Vo(e.entries,e.dryRun,e.sharedDir);break}return r}async function jo(e,t,r){let n={freedBytes:0,removed:0,skipped:[]};for(let i of e){let s=await pt(ce(i.directory,ct));for(let o of Lo(s,t)){if(!r)await br(o.path,{force:!0});n.removed++,n.freedBytes+=o.size}}return n}async function Uo(e,t){let r={freedBytes:0,removed:0,skipped:[]};for(let n of e){if(await Mo(n.directory)){r.skipped.push(n.id);continue}let i=ce(n.directory,_o);if(!$r(i))continue;let s=await wr(i);if(s.length===0)continue;if(r.freedBytes+=await Cr(i),r.removed+=s.length,t)continue;for(let o of s)await br(ce(i,o),{force:!0,recursive:!0})}return r}async function Vo(e,t,r){let n={freedBytes:0,removed:0,skipped:[]},i=r??ce(te,Io);if(!$r(i))return n;let o=(await wr(i,{withFileTypes:!0})).filter((a)=>a.isDirectory()).map((a)=>a.name),l=await Bo(e);for(let a of Ho(o,l)){let u=ce(i,a);if(n.freedBytes+=await Cr(u),n.removed++,!t)await br(u,{force:!0,recursive:!0})}return n}async function Bo(e){let t=[];for(let r of e)try{let n=await Ro(ce(r.directory,ko)),i=dt(n);if(i)t.push(i)}catch{}return t}function Go(e,t){let r=e.split(".").map((s)=>Number.parseInt(s,ei)),n=t.split(".").map((s)=>Number.parseInt(s,ei)),i=Math.max(r.length,n.length);for(let s=0;s<i;s++){let o=r[s]??0,l=n[s]??0,a=Number.isNaN(o)?0:o,u=Number.isNaN(l)?0:l;if(a!==u)return u-a}return 0}import{readFile as Jo}from"fs/promises";var Ko=1e4,Wo="/etc/resolv.conf",qo=/^\s*nameserver\[\d+\]\s*:\s*(\S+)/gm,zo=/^\s*nameserver\s+(\S+)/gm,Yo=/^(?:ip|ipv6)_address:\s*(\S+)/gm,Xo=/^(\S+)\s/gm;async function xr(){let[e,t]=Bun.which("scutil")?[await ri(["scutil","--dns"]),qo]:[await Zo(),zo];return ii(ni(e,t))}function Sr(){return Bun.which("dscacheutil")!==null||Bun.which("getent")!==null}async function Rr(e){let[t,r]=Bun.which("dscacheutil")?[["dscacheutil","-q","host","-a","name",e],Yo]:[["getent","hosts",e],Xo],n=performance.now(),i=await ri(t);return{addresses:ii(ni(i,r)),durationMs:Math.round(performance.now()-n),host:e}}async function ri(e){let t=Bun.spawn(e,{stderr:"ignore",stdin:"ignore",stdout:"pipe",timeout:Ko}),[r]=await Promise.all([new Response(t.stdout).text(),t.exited]);return r}async function Zo(){try{return await Jo(Wo,"utf-8")}catch(e){if(e.code==="ENOENT")return"";throw e}}function ni(e,t){return[...e.matchAll(t)].flatMap((r)=>r[1]?[r[1]]:[])}function ii(e){return[...new Set(e)]}function H(e){switch(e.provider){case"github":return new yr(e);default:throw Error(`Runner provider "${e.provider}" is not yet implemented`)}}var oi=0,Qo={logs:"file(s)",shared:"version(s)",work:"item(s)"},si=Q({days:{alias:"d",default:oi,description:"Override log retention",displayDefault:`config or ${it} days`,type:"number"},dryRun:{alias:"n",default:!1,description:"Preview cleanup without deleting",type:"boolean"},logs:{alias:"l",default:!1,description:"Remove old diagnostic logs",type:"boolean"},shared:{alias:"s",default:!1,description:"Remove unused runner versions",type:"boolean"},work:{alias:"w",default:!1,description:"Clear stopped runners' workspaces",type:"boolean"},yes:{alias:"y",default:!1,description:"Skip workspace confirmation",type:"boolean"}});class yt extends I{name="cleanup";description="Remove logs, workspaces, and old runner versions";args=si;async execute(e){F(e.args,si,"Run 'hydra cleanup --help' for supported options");let t=e.config.get("profiles");if(Object.keys(t).length===0)throw new w("No profiles configured","Run hydra init to set up a profile");let r=ft(e.config.get("cleanup")),n=this.explicitTargets(e),i=n.length>0?n:r.targets,s=e.args.days>oi?e.args.days:r.olderThanDays;if(i.includes("work")&&!e.args.dryRun&&!e.args.yes){if(!await de({message:"Delete _work contents (checkouts and caches) of stopped runners?"}))i=i.filter((a)=>a!=="work")}let o=await gt({dryRun:e.args.dryRun,entries:e.config.get("runners")??[],now:Date.now(),olderThanDays:s,targets:i});if(this.printReport(o,e.args.dryRun),e.args.dryRun)return;e.config.set("cleanup",{...e.config.get("cleanup"),lastRun:new Date().toISOString()})}explicitTargets(e){let t=[];if(e.args.logs)t.push("logs");if(e.args.work)t.push("work");if(e.args.shared)t.push("shared");return t}printReport(e,t){let r=[];for(let[s,o]of Object.entries(e)){if(!o)continue;let l=Qo[s],a=ge(o.freedBytes),u=t?`would remove ${o.removed} ${l}, ${a}`:`removed ${o.removed} ${l}, freed ${a}`;if(r.push(`${c.bold(s)}: ${o.removed>0?u:c.dim("nothing to do")}`),o.skipped.length>0)r.push(c.dim(` skipped running runner(s): ${o.skipped.join(", ")}`))}if(r.length===0){b.info(c.dim("Nothing to clean."));return}let n=ht(e),i=t?"Would free":"Freed";r.push(c.dim(`${i} ${ge(n)} total`)),b.info(r.join(`
|
|
245
|
+
`))}}async function $t(e){try{let t=ft(e.config.get("cleanup"));if(!t.auto)return;if(!vr(t,Date.now()))return;let r=await gt({dryRun:!1,entries:e.config.get("runners")??[],now:Date.now(),olderThanDays:t.olderThanDays,targets:t.targets});e.config.set("cleanup",{...e.config.get("cleanup"),lastRun:new Date().toISOString()});let n=ht(r);if(n>0)b.info(c.dim(`Auto cleanup freed ${ge(n)}`))}catch(t){let r=t instanceof Error?t.message:String(t);b.warn(c.yellow(`Auto cleanup failed: ${r}`))}}import{existsSync as li}from"fs";import{join as ui}from"path";var ai="Runner count must be a positive safe integer",ea=Se().map((e)=>Number.isSafeInteger(e)&&e>0?e:ee());function we(e){try{return ea(typeof e==="string"?Number(e):e)}catch{throw new w(ai)}}function wt(e){try{we(e);return}catch{return ai}}function Re(e,t){if(e.length===0)return t;for(let r of e)if(!t.includes(r))throw new w(`Runner "${r}" not found`);return e}function B(e,t){let r=e.get("profiles");if(Object.keys(r).length===0)throw new w("No profiles configured","Run hydra init to set up a profile");let n=t??e.get("defaultProfile")??Ne,i=Object.hasOwn(r,n)?r[n]:void 0;if(!i)throw new w(`Profile "${n}" not found`);return{name:n,profile:i}}async function Y(e){let t=e.get("profiles"),r=Object.keys(t);if(r.length===0)throw new w("No profiles configured","Run hydra init to set up a profile");if(r.length===1)return r[0];return me({message:"Select profile",options:r.map((n)=>({label:n,value:n,hint:t[n]?.url}))})}var ta=M({profile:{description:"Profile to use"},count:{description:"Runner count override"}});class bt extends I{name="create";description="Download and register runners";positionals=ta;async execute(e){F(e.args,this.args,"Run 'hydra create --help' for supported options");let{name:t,profile:r}=e.interactive?B(e.config,await Y(e.config)):B(e.config,e.positionals.profile),n=e.interactive?await this.promptCount(r.numberOfMachines):we(e.positionals.count??r.numberOfMachines),i=e.config.get("runners")??[],s=i.filter((h)=>h.profile===t),o=n-s.length;if(o<=0){b.warn(`Already have ${s.length} runner(s) for profile "${t}". Nothing to create.`);return}let l=H(r),a=K();a.start("Downloading runner binary...");let{version:u}=await l.download();a.stop(`Runner v${u} ready`);let f=new Set(i.map((h)=>h.id)),m=1;for(let h=0;h<o;){let g=`${r.name}-${m++}`;if(f.has(g))continue;h++;let S={createdAt:new Date().toISOString(),directory:ui(r.directory,g),id:g,name:g,profile:t,provider:r.provider,url:r.url},A=li(S.directory);a.start(`Registering ${g}...`);try{await l.create([g])}finally{if(!A&&li(ui(S.directory,".runner")))i.push(S),e.config.set("runners",i)}a.stop(`${c.green("+")} ${g}`)}let y=t===e.config.get("defaultProfile")?"":` ${t}`;b.info(`${c.green("Created")} ${o} runner(s) for "${t}". Run ${c.green(`hydra start${y}`)} to start them.`)}async promptCount(e){let t=await ie({initialValue:String(e),message:"Number of runners to create",validate:wt});return we(t)}}var ra={configured:{paint:c.green,text:"configured"},foreign:{paint:c.yellow,text:"set to another script"},missing:{paint:c.yellow,text:"missing"},off:{paint:c.dim,text:"off"},outdated:{paint:c.yellow,text:"outdated"}},ci=new Set(["missing","outdated"]);class vt extends I{name="doctor";description="Check DNS and each runner's job hooks";async execute(e){F(e.args,this.args,"Run 'hydra doctor --help' for supported options");let t=e.config.get("profiles");if(Object.keys(t).length===0)throw new w("No profiles configured","Run hydra init to set up a profile");let r=e.config.get("runners")??[],n=await xr(),i=[`${c.bold("DNS servers")} ${n.length>0?n.join(", "):c.dim("unknown")}`],s=!1,o=!1;for(let[l,a]of Object.entries(t)){let u=oe(a.jobHooks);i.push("",`${c.bold(l)} ${c.dim(a.url)}`),i.push(...await this.dnsLines(u.dns)),i.push(this.cleanupLine(u));let f=await this.runnerLines(a,u,r.filter((m)=>m.profile===l));i.push(...f.lines),s||=f.needsUpdate,o||=f.needsRestart}if(s)i.push("",c.yellow(`Run ${c.green("hydra update")} to write the missing hooks.`));if(o)i.push("",c.yellow('Restart runners marked "restart" once they are idle: hydra stop <profile> && hydra start <profile>'));b.info(i.join(`
|
|
246
|
+
`))}async dnsLines(e){let t=[` DNS wait: ${e.enabled?`on, up to ${e.timeoutSeconds} s`:c.dim("off")}`];if(!Sr())return[...t,c.yellow(" No dscacheutil or getent to look hosts up with")];for(let r of e.hosts){let n=await Rr(r);t.push(n.addresses.length>0?` ${c.green("\u2713")} ${r} ${c.dim(`${n.durationMs} ms \xB7 ${n.addresses.join(", ")}`)}`:` ${c.red("\u2717")} ${r} ${c.red(`no address after ${n.durationMs} ms`)}`)}return t}cleanupLine(e){let{enabled:t,keepToolCache:r}=e.workspaceCleanup;return` Workspace cleanup: ${t?`on, ${r?"keeps _tool":"empties _tool"}`:c.dim("off")}`}async runnerLines(e,t,r){if(r.length===0)return{lines:[c.dim(" No runners.")],needsRestart:!1,needsUpdate:!1};let n=new Map((await H(e).list()).map((a)=>[a.id,a])),i=[...r].sort((a,u)=>a.id.localeCompare(u.id,void 0,{numeric:!0})),s=[],o=!1,l=!1;for(let a of i){let u=n.get(a.id),{envModifiedAt:f,states:m}=await ur(a.directory,t),d=this.loadedBefore(u,f);o||=d,l||=ci.has(m.started)||ci.has(m.completed),s.push([` ${a.id}`,c.dim(u?.status??"unknown"),`DNS wait ${this.stateLabel(m.started)}`,`cleanup ${this.stateLabel(m.completed)}`,d?c.yellow("restart"):""].filter(Boolean).join(" "))}return{lines:s,needsRestart:o,needsUpdate:l}}stateLabel(e){let{paint:t,text:r}=ra[e];return t(r)}loadedBefore(e,t){if(e?.status!=="running"||!e.startedAt||!t)return!1;return t>e.startedAt}}import{existsSync as pi}from"fs";import{mkdir as di}from"fs/promises";var mi=1,na=M({url:{description:"GitHub repository or organization URL"}}),ia=Q({force:{alias:"f",default:!1,description:"Overwrite an existing profile",type:"boolean"},labels:{alias:"l",description:"Set comma-separated runner labels",type:"string"},name:{alias:"n",description:"Set the runner name prefix",type:"string"},profile:{alias:"p",description:"Set the profile name",type:"string"},runners:{alias:"c",description:"Set the runner count",type:"number"}});class Ct extends I{name="init";description="Set up a runner profile";positionals=na;args=ia;prompts=!0;async execute(e){F(e.args,this.args,"Run 'hydra init --help' for supported options");let t=e.interactive?await this.promptProfileName(e):e.args.profile??Ne,r=!e.config.exists();if(!r&&Object.hasOwn(e.config.get("profiles"),t)&&!e.args.force){if(!e.interactive)throw new w(`Profile "${t}" already exists. Use --force to overwrite.`);if(!await de({initialValue:!1,message:`Profile "${t}" already exists. Overwrite?`}))return}let i=e.interactive?await this.runInitForm():this.buildProfileFromArgs(e);await this.ensureDirectories();let s={...r?{}:e.config.get("profiles"),[t]:i};if(e.config.set("profiles",s),!e.config.get("defaultProfile"))e.config.set("defaultProfile",t);be([`${c.dim("Profile:")} ${t}`,`${c.dim("URL:")} ${i.url}`,`${c.dim("Runners:")} ${i.numberOfMachines}`,`${c.dim("Name:")} ${i.name}`,i.labels?`${c.dim("Labels:")} ${i.labels}`:"","",`Run ${c.green(`${nt} create${t!==e.config.get("defaultProfile")?` ${t}`:""}`)} to provision runners.`].filter(Boolean).join(`
|
|
247
|
+
`),c.green(r?"Hydra initialized":`Profile "${t}" added`))}async promptProfileName(e){return ie({initialValue:e.args.profile??Ne,message:"Profile name"})}async ensureDirectories(){if(!pi(te))await di(te,{recursive:!0});if(!pi(De))await di(De,{recursive:!0})}async runInitForm(){let e=await Je({url:()=>ie({message:"GitHub repository or organization URL",placeholder:"https://github.com/owner/repo",validate:(t)=>{if(!t)return"URL is required";try{he(t)}catch{return"Must be a GitHub repository or organization URL (https://github.com/<owner>/<repo> or https://github.com/<org>)"}return}}),name:()=>ie({initialValue:"runner",message:"Base name for runners",placeholder:"runner"}),runners:()=>ie({initialValue:String(mi),message:"Number of runners",placeholder:"1",validate:wt}),labels:()=>ie({message:"Additional labels (comma-separated, optional)",placeholder:"self-hosted,macOS,ARM64"})},{onCancel:()=>{throw new X}});return{directory:De,jobHooks:lt(),labels:e.labels||void 0,name:e.name,numberOfMachines:we(e.runners),os:this.detectOs(),overwrite:!1,provider:"github",run:!1,url:e.url}}buildProfileFromArgs(e){if(!e.positionals.url)throw new w("URL is required","Usage: hydra init <url>");return this.validateUrl(e.positionals.url),{directory:De,jobHooks:lt(),labels:e.args.labels,name:e.args.name??"runner",numberOfMachines:we(e.args.runners??mi),os:this.detectOs(),overwrite:!1,provider:"github",run:!1,url:e.positionals.url}}validateUrl(e){try{he(e)}catch(t){throw new w(t instanceof Error?t.message:`Invalid GitHub URL: ${e}`,"Expected https://github.com/<owner>/<repo> or https://github.com/<org>")}}detectOs(){let e=process.platform;if(e==="darwin")return"osx";if(e==="win32")return"windows";return"linux"}}import{existsSync as sa}from"fs";import{readFile as oa}from"fs/promises";var aa=100,la={runner:"runner daemon log",worker:"job log"},ua=Q({lines:{alias:"n",default:aa,description:"Number of lines to show",type:"number"},list:{alias:"l",default:!1,description:"List log files instead of showing one",type:"boolean"},open:{alias:"o",default:!1,description:"Open the log file",type:"boolean"},runner:{alias:"r",default:!1,description:"Show the runner log instead of a job log",type:"boolean"}}),ca=M({id:{description:"Runner ID"}});class xt extends I{name="logs";description="Show recent runner logs";args=ua;positionals=ca;async execute(e){let t=e.config.get("runners")??[];if(t.length===0)throw new w("No runners found","Run hydra create to provision runners");let r=await this.resolveEntry(e,t),n=e.config.get("profiles"),i=Object.hasOwn(n,r.profile)?n[r.profile]:void 0;if(!i)throw new w(`Profile "${r.profile}" not found for runner "${r.id}"`,"Run hydra init to set it up");if(!sa(r.directory))throw new w(`Runner directory not found: ${r.directory}`,"Run hydra create to provision this runner");let o=await H(i).logFiles(r.id);if(o.length===0)this.noLogs(r.id);if(e.args.list){this.printList(r,o);return}let l=this.selectFile(e,r,o);if(e.args.open){await this.openFile(l);return}await this.printTail(r,l,e.args.lines)}async resolveEntry(e,t){let r=e.positionals.id;if(r){let i=t.find((s)=>s.id===r);if(!i)throw new w(`Runner "${r}" not found`,`Known runners: ${t.map((s)=>s.id).join(", ")}`);return i}if(e.interactive)return me({message:"Select runner",options:t.map((i)=>({hint:i.profile,label:i.name,value:i}))});let n=t[0];if(t.length===1&&n)return n;throw new w("Multiple runners found",`Pass a runner id: hydra logs <id> (${t.map((i)=>i.id).join(", ")})`)}selectFile(e,t,r){if(e.args.runner){let i=r.find((s)=>s.type==="runner");if(!i)throw new w(`No runner daemon logs found for "${t.id}"`,"Start the runner first: hydra start");return i}let n=pr(r,"worker");if(!n)this.noLogs(t.id);if(n.fallback)b.warn(c.yellow("No job (Worker) logs yet; showing the runner daemon log instead"));return n.file}noLogs(e){throw new w(`No logs found for runner "${e}"`,"Logs appear once the runner has started; run hydra start")}printList(e,t){let r=[`${c.bold(e.name)} ${c.dim(e.directory)}`];for(let n of t){let i=n.type==="worker"?c.green("worker"):c.blue("runner");r.push(` ${i} ${n.name} ${c.dim(`${n.mtime.toISOString()} \xB7 ${ge(n.size)}`)}`)}b.info(r.join(`
|
|
248
|
+
`))}async printTail(e,t,r){let n=await oa(t.path,"utf-8"),i=[`${c.bold(e.name)} ${c.dim(la[t.type])}`,c.dim(t.path),c.dim(`Last modified: ${t.mtime.toISOString()}`)];b.info(i.join(`
|
|
249
|
+
`)),console.log(dr(n,r))}async openFile(e){let t=(process.env.EDITOR??"").split(" ").filter(Boolean),r=t[0];if(r){await Bun.spawn([r,...t.slice(1),e.path],{stderr:"inherit",stdin:"inherit",stdout:"inherit"}).exited;return}if(process.platform==="darwin"){await Bun.$`open ${e.path}`.quiet();return}throw new w("No editor configured","Set $EDITOR to open log files")}}var pa=M({name:{description:"Profile name to set as default"}});class Er extends I{name="default";description="Set the default profile";positionals=pa;async execute(e){F(e.args,this.args,"Run 'hydra profile default --help' for supported options");let t=e.interactive?await Y(e.config):e.positionals.name;if(!t)throw new w("Profile name is required","Usage: hydra profile default <name>");let{name:r}=B(e.config,t);if(e.config.get("defaultProfile")===r){b.info(`"${r}" is already the default profile.`);return}e.config.set("defaultProfile",r),b.info(`${c.green("Default profile set to")} "${r}".`)}}class _r extends I{name="list";description="List profiles";async execute(e){let t=e.config.get("profiles"),r=Object.keys(t);if(r.length===0)throw new w("No profiles configured","Run hydra init to set up a profile");let n=e.config.get("runners")??[],i=e.config.get("defaultProfile");for(let s of r){let o=t[s],a=await H(o).list(),u=n.filter((g)=>g.profile===s),f=new Set(a.filter((g)=>g.status==="running").map((g)=>g.id)),m=u.filter((g)=>f.has(g.id)).length,d=s===i,y=[`${c.dim("URL:")} ${o.url}`,`${c.dim("Provider:")} ${o.provider}`,`${c.dim("OS:")} ${o.os}`,`${c.dim("Name:")} ${o.name}`,`${c.dim("Runners:")} ${u.length}/${o.numberOfMachines} created, ${c.green(`${m} running`)}`];if(o.labels)y.push(`${c.dim("Labels:")} ${o.labels}`);let h=d?`${s} ${c.dim("(default)")}`:s;be(y.join(`
|
|
250
|
+
`),h)}b.info(c.dim(`${r.length} profile(s)`))}}import{rm as da}from"fs/promises";import{join as ma}from"path";var fa=M({name:{description:"Profile name to remove"}}),ha=Q({yes:{alias:"y",default:!1,description:"Skip runner removal confirmation",type:"boolean"}});class kr extends I{name="remove";description="Delete a profile and its runners";positionals=fa;args=ha;async execute(e){F(e.args,this.args,"Run 'hydra profile remove --help' for supported options");let t=e.interactive?await Y(e.config):e.positionals.name;if(!t)throw new w("Profile name is required","Usage: hydra profile remove <name>");let{profile:r}=B(e.config,t),n=e.config.get("runners")??[],i=n.filter((a)=>a.profile===t);if(i.length>0){if(!e.args.yes){if(!process.stdout.isTTY)throw new w("Runner removal requires confirmation","Pass --yes to remove the profile and its runners without a prompt");if(!await de({initialValue:!0,message:`This will stop and remove ${i.length} runner(s). Continue?`}))return}await this.removeRunners(e,r,i.map((u)=>u.id));let a=n.filter((u)=>u.profile!==t);e.config.set("runners",a)}let{[t]:s,...o}=e.config.get("profiles");if(e.config.set("profiles",o),e.config.get("defaultProfile")===t){let a=Object.keys(o);e.config.set("defaultProfile",a[0])}b.info(`${c.green("Removed")} profile "${t}".`)}async removeRunners(e,t,r){let n=H(t),i=K();for(let s of r)i.start(`Stopping and removing ${s}...`),await n.stop([s]),await n.remove([s]),await da(ma(t.directory,s),{force:!0,recursive:!0}),e.config.set("runners",(e.config.get("runners")??[]).filter((o)=>o.id!==s)),i.stop(`${c.red("-")} ${s}`)}}class St extends I{name="profile";description="Manage profiles";init(){this.registerSubcommands([new _r,new kr,new Er])}}import{rm as ga}from"fs/promises";import{join as ya}from"path";var $a=M({profile:{description:"Profile to use"},ids:{description:"Runner IDs (all by default)",variadic:!0}});class Rt extends I{name="remove";description="Deregister and delete runners";positionals=$a;async execute(e){F(e.args,this.args,"Run 'hydra remove --help' for supported options");let{name:t,profile:r}=e.interactive?B(e.config,await Y(e.config)):B(e.config,e.positionals.profile),i=(e.config.get("runners")??[]).filter((a)=>a.profile===t);if(i.length===0)throw new w(`No runners found for profile "${t}"`,"Run hydra create to provision runners");let s=e.interactive?await this.promptRunnerSelection(i.map((a)=>a.id)):Re(e.positionals.ids??[],i.map((a)=>a.id)),o=H(r),l=K();for(let a of s)l.start(`Stopping and removing ${a}...`),await o.stop([a]),await o.remove([a]),await ga(ya(r.directory,a),{force:!0,recursive:!0}),e.config.set("runners",(e.config.get("runners")??[]).filter((u)=>u.id!==a)),l.stop(`${c.red("-")} ${a}`);b.info(`${c.green("Removed")} ${s.length} runner(s).`)}async promptRunnerSelection(e){return fe({message:"Select runners to remove",options:e.map((t)=>({label:t,value:t})),required:!0})}}var wa=M({profile:{description:"Profile to use"},ids:{description:"Runner IDs (all by default)",variadic:!0}});class Et extends I{name="start";description="Start runners";positionals=wa;async execute(e){F(e.args,this.args,"Run 'hydra start --help' for supported options");let{name:t,profile:r}=e.interactive?B(e.config,await Y(e.config)):B(e.config,e.positionals.profile),i=(e.config.get("runners")??[]).filter((d)=>d.profile===t);if(i.length===0)throw new w(`No runners found for profile "${t}"`,"Run hydra create to provision runners");let s=H(r),o=K();o.start("Fetching runner status...");let l=await s.list();o.stop("Runner status fetched");let a=new Set(l.filter((d)=>d.status==="running").map((d)=>d.id)),u=i.map((d)=>d.id).filter((d)=>!a.has(d)),f=e.interactive?await this.promptRunnerSelection(u):Re(e.positionals.ids??[],i.map((d)=>d.id)),m=0;for(let d of f){if(a.has(d)){o.start(""),o.stop(`${c.dim("\u25CF")} ${d} ${c.dim("already running")}`);continue}o.start(`Starting ${d}...`),await s.start([d]),o.stop(`${c.green("\u25B6")} ${d}`),m++}b.info(`${c.green("Started")} ${m} runner(s).`),await $t(e)}async promptRunnerSelection(e){if(e.length===0)throw new w("All runners are already running");return fe({message:"Select runners to start",options:e.map((t)=>({label:t,value:t})),required:!0})}}var ba={registered:c.yellow,running:c.green,stopped:c.red,unknown:c.dim};class _t extends I{name="status";description="Show status across profiles";async execute(e){let t=e.config.get("profiles"),r=Object.keys(t);if(r.length===0)throw new w("No profiles configured","Run hydra init to set up a profile");let n=e.config.get("runners")??[],i=0,s=0,o=[];for(let l of r){let a=t[l],f=await H(a).list();if(o.length>0)o.push("");o.push(`${c.bold(l)} ${c.dim(a.url)}`);let m=new Set(n.filter((y)=>y.profile===l).map((y)=>y.id)),d=f.filter((y)=>m.has(y.id)).sort((y,h)=>y.name.localeCompare(h.name,void 0,{numeric:!0}));this.formatRunners(d,o),i+=d.length,s+=d.filter((y)=>y.status==="running").length}o.push(""),o.push(c.dim(`${i} runner(s) total, ${s} running`)),b.info(o.join(`
|
|
251
|
+
`))}formatRunners(e,t){if(e.length===0){t.push(c.dim(" No runners."));return}for(let r of e){let n=ba[r.status]??c.dim,i=r.pid?c.dim(` (pid: ${r.pid})`):"";t.push(` ${n("\u25CF")} ${r.name} ${n(r.status)}${i}`)}}}var va=M({profile:{description:"Profile to use"},ids:{description:"Runner IDs (all by default)",variadic:!0}});class kt extends I{name="stop";description="Stop runners";positionals=va;async execute(e){F(e.args,this.args,"Run 'hydra stop --help' for supported options");let{name:t,profile:r}=e.interactive?B(e.config,await Y(e.config)):B(e.config,e.positionals.profile),i=(e.config.get("runners")??[]).filter((d)=>d.profile===t);if(i.length===0)throw new w(`No runners found for profile "${t}"`,"Run hydra create to provision runners");let s=H(r),o=K();o.start("Fetching runner status...");let l=await s.list();o.stop("Runner status fetched");let a=new Set(l.filter((d)=>d.status==="running").map((d)=>d.id)),u=i.map((d)=>d.id).filter((d)=>a.has(d)),f=e.interactive?await this.promptRunnerSelection(u):Re(e.positionals.ids??[],i.map((d)=>d.id)),m=0;for(let d of f){if(!a.has(d)){o.start(""),o.stop(`${c.dim("\u25CF")} ${d} ${c.dim("not running")}`);continue}o.start(`Stopping ${d}...`),await s.stop([d]),o.stop(`${c.red("\u25A0")} ${d}`),m++}b.info(`${c.green("Stopped")} ${m} runner(s).`)}async promptRunnerSelection(e){if(e.length===0)throw new w("No runners are currently running");return fe({message:"Select runners to stop",options:e.map((t)=>({label:t,value:t})),required:!0})}}class At extends I{name="update";description="Update runner binaries and job hooks";async execute(e){F(e.args,this.args,"Run 'hydra update --help' for supported options");let t=e.config.get("profiles"),r=e.config.get("runners")??[];if(r.length===0)throw new w("No runners found","Run hydra create to provision runners");let n=K(),i=Map.groupBy(r,(a)=>a.profile);for(let a of i.keys()){let u=Object.hasOwn(t,a)?t[a]:void 0;if(u)oe(u.jobHooks)}let s=0,o=0,l=new Map;for(let[a,u]of i){let f=Object.hasOwn(t,a)?t[a]:void 0;if(!f)continue;let m=H(f);n.start(`Checking for updates for "${a}"...`);let{version:d}=await m.download();n.stop(`Latest: v${d}`);let y=await m.list(),h=new Set(y.filter((g)=>g.status==="running").map((g)=>g.id));for(let g of u){let S=h.has(g.id);if(await m.currentVersion(g.id)===d){let C=await m.configureJobHooks(g.id);if(!C.envChanged&&!C.scriptsChanged)continue;if(o++,C.envChanged&&S){let D=l.get(a)??{ids:[],total:u.length};l.set(a,{...D,ids:[...D.ids,g.id]})}n.start(""),n.stop(`${c.green("\u2713")} ${g.id} ${c.dim("(job hooks)")}`);continue}if(n.start(`Updating ${g.id}...`),S)await m.stop([g.id]);if(await m.update([g.id]),S)await m.start([g.id]);s++;let A=S?` ${c.dim("(restarted)")}`:"";n.stop(`${c.green("\u2713")} ${g.id}${A}`)}}if(b.info(s>0?`${c.green("Updated")} ${s} runner(s).`:"All runners are already on the latest version."),o>0)b.info(`${c.green("Refreshed")} job hooks on ${o} runner(s).`);this.printRestartHint(l),await $t(e)}printRestartHint(e){if(e.size===0)return;let t=["Running runners load job hook settings only when they start. Restart them once they are idle:"];for(let[r,{ids:n,total:i}]of e){let s=n.length===i?r:`${r} ${n.join(" ")}`;t.push(` ${c.green(`hydra stop ${s} && hydra start ${s}`)}`)}b.warn(t.join(`
|
|
252
|
+
`))}}class fi extends Xt{constructor(){super(new rr(En,_n),{bin:nt,clearOnStart:!0,description:"Manage GitHub Actions runners on this machine.",exitLabel:"Exit",goodbyeMessage:"Heads down.",name:"HYDRA",promptMessage:"What do you need?",version:Rn})}init(){this.registerCommands([new Ct,new bt,new Et,new kt,new _t,new xt,new vt,new yt,new Rt,new At,new St])}}var Ca=new fi;Ca.run();
|
package/package.json
CHANGED
package/schema.json
CHANGED
|
@@ -45,6 +45,60 @@
|
|
|
45
45
|
"description": "Directory where runners will be created",
|
|
46
46
|
"type": "string"
|
|
47
47
|
},
|
|
48
|
+
"jobHooks": {
|
|
49
|
+
"additionalProperties": false,
|
|
50
|
+
"description": "Scripts Hydra installs as each runner's job hooks; hydra create and hydra update write them",
|
|
51
|
+
"properties": {
|
|
52
|
+
"dns": {
|
|
53
|
+
"additionalProperties": false,
|
|
54
|
+
"description": "Job-started hook that waits for DNS before every job and fails the job if it does not recover",
|
|
55
|
+
"properties": {
|
|
56
|
+
"enabled": { "default": true, "description": "Install the DNS wait hook", "type": "boolean" },
|
|
57
|
+
"hosts": {
|
|
58
|
+
"default": [
|
|
59
|
+
"github.com",
|
|
60
|
+
"api.github.com",
|
|
61
|
+
"objects.githubusercontent.com",
|
|
62
|
+
"pipelines.actions.githubusercontent.com"
|
|
63
|
+
],
|
|
64
|
+
"description": "Hosts that must all resolve before the job starts",
|
|
65
|
+
"items": {
|
|
66
|
+
"format": "hostname",
|
|
67
|
+
"pattern": "^[A-Za-z0-9](?:[A-Za-z0-9.-]*[A-Za-z0-9])?$",
|
|
68
|
+
"type": "string"
|
|
69
|
+
},
|
|
70
|
+
"minItems": 1,
|
|
71
|
+
"type": "array"
|
|
72
|
+
},
|
|
73
|
+
"timeoutSeconds": {
|
|
74
|
+
"default": 120,
|
|
75
|
+
"description": "Seconds to wait for DNS before failing the job",
|
|
76
|
+
"minimum": 1,
|
|
77
|
+
"type": "integer"
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
"type": "object"
|
|
81
|
+
},
|
|
82
|
+
"workspaceCleanup": {
|
|
83
|
+
"additionalProperties": false,
|
|
84
|
+
"description": "Job-completed hook that removes the job's workspace and _temp contents from the runner's _work directory",
|
|
85
|
+
"properties": {
|
|
86
|
+
"enabled": {
|
|
87
|
+
"default": false,
|
|
88
|
+
"description": "Install the cleanup hook; hydra init turns it on for new profiles",
|
|
89
|
+
"type": "boolean"
|
|
90
|
+
},
|
|
91
|
+
"keepToolCache": {
|
|
92
|
+
"default": true,
|
|
93
|
+
"description": "Keep _work/_tool; set to false to empty the tool cache after every job as well",
|
|
94
|
+
"type": "boolean"
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
"type": "object"
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
"type": "object"
|
|
101
|
+
},
|
|
48
102
|
"labels": { "description": "Comma-separated list of labels for the runners", "type": "string" },
|
|
49
103
|
"name": { "default": "runner", "description": "Base name for the runners", "type": "string" },
|
|
50
104
|
"numberOfMachines": {
|