@amr-m-abdelgawad/devctl 0.13.1 → 0.13.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/devctl.js +9 -2
  2. package/package.json +1 -1
package/dist/devctl.js CHANGED
@@ -51,7 +51,7 @@ shutdown:
51
51
  stop_services_on_exit: true
52
52
  grace_seconds: 10
53
53
  `}var Ht;var cu=E(()=>{Ee();Ht=[{id:"repo",title:"Repository",prompt:"Repository root"},{id:"name",title:"Environment",prompt:"Environment / project name"},{id:"project",title:"Google project",prompt:"Google Cloud project"},{id:"auth",title:"Authentication",prompt:"Run gcloud ADC login now? (y/N)"},{id:"sa",title:"Service account",prompt:"Service account email to record (optional)"},{id:"audience",title:"IAP",prompt:"IAP audience to record on a sample route (optional)"},{id:"port",title:"Ports",prompt:"Proxy listen port"},{id:"profile",title:"Profiles",prompt:"Default profile name (optional)"},{id:"write",title:"Validation",prompt:"Write starter configuration"}]});import{existsSync as xm,mkdirSync as bm,statSync as jI,writeFileSync as qI}from"fs";import{createInterface as BI}from"readline/promises";import{stdin as zI,stdout as WI}from"process";import{basename as uu,dirname as wm,join as io,resolve as VI}from"path";function km(e,t){let n=e===""?process.cwd():e;if(t==="")return{repo:n,cfgPath:io(n,hn,ir)};let r=VI(t);if(!(xm(r)?jI(r).isDirectory():!/\.ya?ml$/i.test(r))){let i=wm(r);return{repo:uu(i)===hn?wm(i):i,cfgPath:r}}if(uu(r)===hn)return{repo:wm(r),cfgPath:io(r,ir)};return{repo:r,cfgPath:io(r,hn,ir)}}function Rw(e,t=uu(e),n="",r="",o=!1){let i=io(e,hn,ir);if(!o&&xm(i))throw C(G,`configuration already exists at ${i}; not overwriting`);return du(e,{...Do(e,n),name:t,profile:r}),i}async function Pw(e,t,n="",r=!1){let{repo:o,cfgPath:i}=km(t,n);if(!r&&xm(i)){gn(`Found existing configuration at ${i}; nothing written.`),gn("Pass --force to overwrite it, or edit it directly.");return}let s=BI({input:zI,output:WI}),a=async(g,h)=>{let y=h!==""?` [${h}]`:"",v=(await s.question(`${g}${y}: `)).trim();return v===""?h:v},l=0,c=()=>(l+=1,l);if(gn(`devctl setup \u2014 ${Ht.length} steps`),gn(""),n==="")await a(`${c()}. Repository root`,o);let u=Do(o,""),d=await a(`${c()}. ${Ht[1]?.prompt}`,uu(o));u.name=d;let f=await e.detectGoogle(""),m=await a(`${c()}. ${Ht[2]?.prompt}`,f.projectID);if(u.project=m,gn(`${c()}. ${Ht[3]?.title}`),!f.adcAvailable){if(gn(" ADC is not available. Run: gcloud auth application-default login"),(await a(" Run login now? (y/N)","n")).toLowerCase()==="y")try{await e.loginGoogle()}catch(h){gn(` login failed: ${h instanceof Error?h.message:String(h)}`)}}else if(gn(" \u2713 Application Default Credentials available"),f.userEmail!=="")gn(` user: ${f.userEmail}`);u.sa=await a(`${c()}. ${Ht[4]?.prompt}`,""),u.audience=await a(`${c()}. ${Ht[5]?.prompt}`,"");let p=await a(`${c()}. ${Ht[6]?.prompt}`,String(Oa));u.proxyPort=lu(p),u.profile=await a(`${c()}. ${Ht[7]?.prompt}`,""),s.close(),du(o,u),gn(""),gn(`Wrote starter configuration to ${i}`),gn(`${c()}. Validation`);try{let g=e.load(o,""),h=await e.runDoctor.execute(g);gn(e.formatDoctor(h))}catch(g){gn(` configuration is not valid yet: ${g instanceof Error?g.message:String(g)}`),gn(" edit .devctl/config.yaml and run `devctl config validate`")}}function du(e,t){let n=io(e,".devctl");bm(io(n,"services"),{recursive:!0}),bm(io(n,"profiles"),{recursive:!0}),bm(io(n,"proxy"),{recursive:!0}),qI(io(n,"config.yaml"),Ew(t))}function gn(e){process.stdout.write(`${e}
54
- `)}var pu=E(()=>{Ee();cu();te()});function qa(e,t){return{vars:{...e.vars,...t.vars},defaults:{...e.defaults,...t.defaults},required:sN([...e.required,...t.required])}}function Fn(e){return Object.keys(e.environments??{}).sort()}function yn(e){return Fn(e).length>0}function Pi(e){if(e.default_environment!==""&&e.environments?.[e.default_environment])return e.default_environment;return Fn(e)[0]??""}function Ut(e,t){let n=Fn(e);if(n.length===0)return"";if(t&&n.includes(t))return t;return Pi(e)}function iN(e,t){if(t==="")return;return e.environments?.[t]}function sr(e,t){let n=e.environment??{vars:{},required:[],defaults:{}},r=Ut(e,t),o=iN(e,r);if(!o)return Ow(n);return qa(n,o)}function gu(e){let t=e.environment??{vars:{},required:[],defaults:{}},n=[Ow(t)];for(let r of Fn(e)){let o=e.environments?.[r];if(o)n.push(qa(t,o))}return n}function Ow(e){return{vars:{...e.vars},defaults:{...e.defaults},required:[...e.required]}}function sN(e){let t={},n=[];for(let r of e)if(r!==""&&!t[r])t[r]=!0,n.push(r);return n}function jx(){return/\$\{(?:env\.)?([A-Za-z_][A-Za-z0-9_]*)\}/g}function Nm(e){return[...e.matchAll(jx())].map((t)=>t[1]??"")}function qx(e,t){let n=[];return{value:e.replace(jx(),(o,i)=>{let s=t[i];if(s===void 0||s==="")return n.push(i),"";return s}),missing:n}}function Cu(e){let t=e.trim();if(t===""||Nm(t).length===0)return;return t}function Bx(e){return/^\$\{(?:env\.)?[A-Za-z_][A-Za-z0-9_]*\}$/.test(e.trim())}function Ms(e){let t=[],n=e;for(;;){let r=n.indexOf("${");if(r<0)return t;let o=n.slice(r).indexOf("}");if(o<0)return t;t.push(n.slice(r+2,r+o)),n=n.slice(r+o+1)}}function Vx(e){if(e.cache.jwt)return!0;let t=XN;for(let n of Object.values(e.outputs)){let r=n.split(".").pop()??n;if(t.includes(r))return!0}return!1}function QN(e){let t=[];for(let n of Ms(e)){let r=Is(n);if(r&&!t.includes(r.recipe))t.push(r.recipe)}return t}function Is(e){let t=e.split(".");if(t[0]!=="http"||t.length!==3||!t[1]||!t[2])return;return{recipe:t[1],output:t[2]}}function Om(e){return Dm([...Object.values(e.vars),...Object.values(e.defaults)])}function e6(e){return Dm(Object.values(e))}function Dm(e){let t=[];for(let n of e)for(let r of QN(n))if(!t.includes(r))t.push(r);return t}function za(e){return[e.request.url,e.request.body,...Object.values(e.request.headers),...Object.values(e.request.form),...Object.values(e.request.auth.headers??{})]}function _u(e){return Dm(za(e))}function t6(e){let t=[];for(let n of za(e))for(let r of Ms(n)){let o=r.split("."),i=o[0]==="services"?o[1]:void 0;if(i&&!t.includes(i))t.push(i)}return t}function n6(e,t){let n=[],r=(o)=>{if(n.includes(o))return;let i=e.http[o];if(!i)return;n.push(o);for(let s of _u(i))r(s)};for(let o of t)r(o);return n}function Ns(e,t,n={}){return n6(e,[...Om(t),...e6(n)])}function $m(e,t,n={}){let r=[];for(let o of Ns(e,t,n)){let i=e.http[o];if(i){for(let s of t6(i))if(e.services[s]&&!r.includes(s))r.push(s)}}return r.sort(),r.map((o)=>({service:o,condition:e.services[o]?.health.type!==""?"service_healthy":"service_started"}))}function Os(e,t,n={}){let r=e.services[t];if(!r)return[];let o=[...r.dependencies],i=new Set(o.map((a)=>kt(a))),s=[...o];for(let a of gu(r))for(let l of $m(e,a,n)){let c=kt(l);if(!i.has(c)&&c!==t)i.add(c),s.push(l)}return s}function Hm(e,t,n){let s={},a=[],l=[],c=(u)=>{let d=s[u]??0;if(d===2)return;if(d===1){a.push(n([...l,u]));return}s[u]=1,l.push(u);for(let f of t(u))c(f);l.pop(),s[u]=2};for(let u of e)c(u);return a}function Gx(e){return Hm(Object.keys(e.http),(t)=>{let n=e.http[t];return n?_u(n).filter((r)=>Boolean(e.http[r])):[]},(t)=>`http recipe cycle: ${t.join(" \u2192 ")}`)}function Kx(e,t,n){let r=e.http[t];if(!r)return!1;if(Qc(n))return!0;return r.outputs[n]!==void 0}function Fm(e){if(e==="token")return!1;let t=e.split(".");if(t.length===1)return zx.test(t[0]??"");return t.length===2&&t[0]==="env"&&zx.test(t[1]??"")}function Zx(e){let t=e.split(".");return t.length===2&&t[0]==="env"?t[1]??"":t[0]??""}function Yx(e){return za(e).some((t)=>Ms(t).includes("token"))}function Tu(e){let t=e.request.auth.type.toLowerCase();return t==="iap"||t==="service_account"}var zx,Wx,XN;var ao=E(()=>{Ee();zx=/^[A-Za-z_][A-Za-z0-9_]*$/,Wx=["access_token","id_token","token"],XN=["access_token","id_token","token","refresh_token","client_secret"]});function Ho(e){return Object.entries(e.provenance).sort(([t],[n])=>t.localeCompare(n)).flatMap(([t,n])=>{let r=n[n.length-1];if(!r)return[];return[{path:t,value:U6(e,t),...r,shadowed:n.slice(0,-1)}]})}function U6(e,t){let n=e;for(let r of t.split(".")){if(typeof n!=="object"||n===null)return;n=n[r]}return n}function Ga(e){if(oo(e))return ar({kind:Di,serviceAccount:e.service_account});if(kw(e)&&(e.type!==""||e.mode!==""))return ar({kind:Pu});let t=(e.type||e.mode).toLowerCase();if(t!=="")return ar({kind:t,providerConfig:e.config??{}});return ar({kind:Oi})}function Cr(e){if(e.type.toLowerCase()==="none")return ar({kind:Oi});let t=e.identity.type.toLowerCase(),n=e.identity.service_account||e.service_account;if(t==="service"||t==="service_account"||e.type==="service_account")return ar({kind:Di,serviceAccount:n});if(e.type.toLowerCase()==="iap"&&t==="")return ar({kind:Oi});if(e.type===""&&t==="")return ar({kind:Oi});return ar({kind:Pu})}function Ym(e){return e.kind===Pu||e.kind===Di}function Sr(e){if(e.tokenKey!=="")return e.tokenKey;if(e.kind===Di)return`sa:${e.serviceAccount}`;if(e.email!=="")return`user:${e.email}`;return"user"}function i1(e){let t=new Map,n=(r,o)=>{t.set(`${r}\x00${o}`,{identity:r,audience:o})};for(let r of Object.values(e.services)){let o=Ga(r.identity);if(!Ym(o))continue;n(Sr(o),"")}for(let r of e.proxy.routes){let o=Cr(r.auth);if(o.kind===Oi)continue;n(Sr(o),r.auth.audience)}return[...t.values()].sort((r,o)=>{let i=r.identity.localeCompare(o.identity);if(i!==0)return i;return r.audience.localeCompare(o.audience)})}function s1(e,t,n){return e.some((r)=>r.identity===t&&r.audience===n)}function ar(e={}){return{kind:Oi,email:"",serviceAccount:"",project:"",projectSource:"",adcAvailable:!1,providerConfig:{},tokenKey:"",...e}}function Jm(e){let t=new Map,n=(r,o)=>{if(r!=="")t.set(r,(t.get(r)??!1)||o)};for(let r of Object.values(e.services))if(oo(r.identity)&&r.identity.service_account!=="")n(r.identity.service_account,!0);for(let r of e.proxy.routes){let o=r.auth.type.toLowerCase(),i=r.auth.identity.type.toLowerCase(),s=r.auth.identity.service_account||r.auth.service_account;if(i==="service"||i==="service_account"||o==="service_account")n(s,o!=="none")}return[...t.entries()].map(([r,o])=>({email:r,active:o})).sort((r,o)=>r.email.localeCompare(o.email))}function Br(e){return Jm(e).filter((t)=>t.active).map((t)=>t.email)}function a1(e,t,n){let r=[];for(let o of t){let i=e.services[o];if(!i)continue;let s=Ga(i.identity);if(oo(i.identity)&&i.identity.service_account===""){r.push({name:o,message:"service account identity is not configured"});continue}if((Ym(s)||i.capabilities.some((l)=>l==="google_api"||l==="iap"||l==="service_identity"))&&!n)r.push({name:o,message:"ADC unavailable"})}return r}function Ka(e){if(Br(e).length>0)return!0;if(e.proxy.routes.some((t)=>t.auth.type.toLowerCase()==="iap"))return!0;return Object.values(e.services).some((t)=>{if(Ym(Ga(t.identity)))return!0;return t.capabilities.some((n)=>n==="google_api"||n==="iap"||n==="service_identity")})}function Xm(){return{name:"user",accepts:(e)=>!oo(e),resolve:async(e,t)=>{if(e.type===""&&e.mode==="")return ar({kind:Oi});return{...await t(),kind:Pu}}}}function Qm(){return{name:"service_account",accepts:(e)=>oo(e),resolve:async(e,t)=>{if(e.service_account==="")throw C(zc,"service account identity is not configured");return{...await t(),kind:Di,serviceAccount:e.service_account}}}}async function l1(e,t,n){if(n)for(let r=n.length-1;r>=0;r-=1){let o=n[r];if(o?.accepts(e))return o.resolve(e,t)}if(oo(e))return Qm().resolve(e,t);return Xm().resolve(e,t)}var Pu="user",Di="service_account",Oi="none";var Un=E(()=>{Ee();te()});function jn(){return"devctl 0.13.1"}var Zt="0.13.1",Qa=2;function P1(e){return e}function lr(e){return e}function Wr(e){if(e.state===dt&&e.health===bn)return cr;if(e.state===dt&&e.health===Qe)return zr;return e.state}function ji(e){return{name:e,state:Yt,health:Jt,pid:0,ports:{},restarts:0,last_error:"",identity:"",startTime:void 0,cpuPercent:void 0,memoryKB:void 0,profile:"",env_source:"daemon",env:"",started_env:"",orphaned:!1}}function M1(e,t,n=(r)=>e.services[r]?.dependencies??[]){let r={},o={};for(let l of Object.keys(t))r[l]=0;for(let l of Object.keys(t)){let c=n(l);for(let u of c){let d=kt(u);if(!t[d])continue;o[d]=[...o[d]??[],l],r[l]=(r[l]??0)+1}}let i=[],s=Object.keys(t).length,a={};while(s>0){let l=Object.entries(r).filter(([c,u])=>u===0&&!a[c]).map(([c])=>c).sort();if(l.length===0)throw C(Bc,"dependency cycle detected while planning startup");i.push(l);for(let c of l){a[c]=!0,s-=1;for(let u of o[c]??[])r[u]=(r[u]??0)-1}}return i}function gh(e,t){if(!e.services[t])throw C(ki,`unknown service "${t}"`)}function Us(e,t,n){let r=n!==""?e.profiles[n]?.environment??{}:{},o=(c)=>Os(e,c,r),i={},s=(c)=>{if(i[c])return;gh(e,c),i[c]=!0;for(let u of o(c))s(kt(u))};for(let c of t)s(c);let a=M1(e,i,o),l=[];return a.forEach((c,u)=>{for(let d of c)l.push({name:d,wave:u+1,dependencies:[...o(d)]})}),{profile:n,steps:l,waves:a,blockers:[]}}function qi(e,t){let n={};for(let[i,s]of Object.entries(e.services))for(let a of s.dependencies){let l=kt(a);n[l]=[...n[l]??[],i]}let r={},o=(i)=>{if(r[i])return;gh(e,i),r[i]=!0;for(let s of n[i]??[])o(s)};for(let i of t)o(i);return Object.keys(r)}function I1(e,t){let n=[...M1(e,t)].reverse().map((o)=>[...o].sort()),r=[];return n.forEach((o,i)=>{for(let s of o)r.push({name:s,wave:i+1,dependencies:[...e.services[s]?.dependencies??[]]})}),{steps:r,waves:n,profile:"",blockers:[]}}function qu(e,t){let n=Object.fromEntries(qi(e,t).map((r)=>[r,!0]));return I1(e,n)}function Bu(e,t){let n={};for(let r of t)gh(e,r),n[r]=!0;return I1(e,n)}function N1(e){let t="";if(e.profile!=="")t+=`Profile: ${e.profile}
54
+ `)}var pu=E(()=>{Ee();cu();te()});function qa(e,t){return{vars:{...e.vars,...t.vars},defaults:{...e.defaults,...t.defaults},required:sN([...e.required,...t.required])}}function Fn(e){return Object.keys(e.environments??{}).sort()}function yn(e){return Fn(e).length>0}function Pi(e){if(e.default_environment!==""&&e.environments?.[e.default_environment])return e.default_environment;return Fn(e)[0]??""}function Ut(e,t){let n=Fn(e);if(n.length===0)return"";if(t&&n.includes(t))return t;return Pi(e)}function iN(e,t){if(t==="")return;return e.environments?.[t]}function sr(e,t){let n=e.environment??{vars:{},required:[],defaults:{}},r=Ut(e,t),o=iN(e,r);if(!o)return Ow(n);return qa(n,o)}function gu(e){let t=e.environment??{vars:{},required:[],defaults:{}},n=[Ow(t)];for(let r of Fn(e)){let o=e.environments?.[r];if(o)n.push(qa(t,o))}return n}function Ow(e){return{vars:{...e.vars},defaults:{...e.defaults},required:[...e.required]}}function sN(e){let t={},n=[];for(let r of e)if(r!==""&&!t[r])t[r]=!0,n.push(r);return n}function jx(){return/\$\{(?:env\.)?([A-Za-z_][A-Za-z0-9_]*)\}/g}function Nm(e){return[...e.matchAll(jx())].map((t)=>t[1]??"")}function qx(e,t){let n=[];return{value:e.replace(jx(),(o,i)=>{let s=t[i];if(s===void 0||s==="")return n.push(i),"";return s}),missing:n}}function Cu(e){let t=e.trim();if(t===""||Nm(t).length===0)return;return t}function Bx(e){return/^\$\{(?:env\.)?[A-Za-z_][A-Za-z0-9_]*\}$/.test(e.trim())}function Ms(e){let t=[],n=e;for(;;){let r=n.indexOf("${");if(r<0)return t;let o=n.slice(r).indexOf("}");if(o<0)return t;t.push(n.slice(r+2,r+o)),n=n.slice(r+o+1)}}function Vx(e){if(e.cache.jwt)return!0;let t=XN;for(let n of Object.values(e.outputs)){let r=n.split(".").pop()??n;if(t.includes(r))return!0}return!1}function QN(e){let t=[];for(let n of Ms(e)){let r=Is(n);if(r&&!t.includes(r.recipe))t.push(r.recipe)}return t}function Is(e){let t=e.split(".");if(t[0]!=="http"||t.length!==3||!t[1]||!t[2])return;return{recipe:t[1],output:t[2]}}function Om(e){return Dm([...Object.values(e.vars),...Object.values(e.defaults)])}function e6(e){return Dm(Object.values(e))}function Dm(e){let t=[];for(let n of e)for(let r of QN(n))if(!t.includes(r))t.push(r);return t}function za(e){return[e.request.url,e.request.body,...Object.values(e.request.headers),...Object.values(e.request.form),...Object.values(e.request.auth.headers??{})]}function _u(e){return Dm(za(e))}function t6(e){let t=[];for(let n of za(e))for(let r of Ms(n)){let o=r.split("."),i=o[0]==="services"?o[1]:void 0;if(i&&!t.includes(i))t.push(i)}return t}function n6(e,t){let n=[],r=(o)=>{if(n.includes(o))return;let i=e.http[o];if(!i)return;n.push(o);for(let s of _u(i))r(s)};for(let o of t)r(o);return n}function Ns(e,t,n={}){return n6(e,[...Om(t),...e6(n)])}function $m(e,t,n={}){let r=[];for(let o of Ns(e,t,n)){let i=e.http[o];if(i){for(let s of t6(i))if(e.services[s]&&!r.includes(s))r.push(s)}}return r.sort(),r.map((o)=>({service:o,condition:e.services[o]?.health.type!==""?"service_healthy":"service_started"}))}function Os(e,t,n={}){let r=e.services[t];if(!r)return[];let o=[...r.dependencies],i=new Set(o.map((a)=>kt(a))),s=[...o];for(let a of gu(r))for(let l of $m(e,a,n)){let c=kt(l);if(!i.has(c)&&c!==t)i.add(c),s.push(l)}return s}function Hm(e,t,n){let s={},a=[],l=[],c=(u)=>{let d=s[u]??0;if(d===2)return;if(d===1){a.push(n([...l,u]));return}s[u]=1,l.push(u);for(let f of t(u))c(f);l.pop(),s[u]=2};for(let u of e)c(u);return a}function Gx(e){return Hm(Object.keys(e.http),(t)=>{let n=e.http[t];return n?_u(n).filter((r)=>Boolean(e.http[r])):[]},(t)=>`http recipe cycle: ${t.join(" \u2192 ")}`)}function Kx(e,t,n){let r=e.http[t];if(!r)return!1;if(Qc(n))return!0;return r.outputs[n]!==void 0}function Fm(e){if(e==="token")return!1;let t=e.split(".");if(t.length===1)return zx.test(t[0]??"");return t.length===2&&t[0]==="env"&&zx.test(t[1]??"")}function Zx(e){let t=e.split(".");return t.length===2&&t[0]==="env"?t[1]??"":t[0]??""}function Yx(e){return za(e).some((t)=>Ms(t).includes("token"))}function Tu(e){let t=e.request.auth.type.toLowerCase();return t==="iap"||t==="service_account"}var zx,Wx,XN;var ao=E(()=>{Ee();zx=/^[A-Za-z_][A-Za-z0-9_]*$/,Wx=["access_token","id_token","token"],XN=["access_token","id_token","token","refresh_token","client_secret"]});function Ho(e){return Object.entries(e.provenance).sort(([t],[n])=>t.localeCompare(n)).flatMap(([t,n])=>{let r=n[n.length-1];if(!r)return[];return[{path:t,value:U6(e,t),...r,shadowed:n.slice(0,-1)}]})}function U6(e,t){let n=e;for(let r of t.split(".")){if(typeof n!=="object"||n===null)return;n=n[r]}return n}function Ga(e){if(oo(e))return ar({kind:Di,serviceAccount:e.service_account});if(kw(e)&&(e.type!==""||e.mode!==""))return ar({kind:Pu});let t=(e.type||e.mode).toLowerCase();if(t!=="")return ar({kind:t,providerConfig:e.config??{}});return ar({kind:Oi})}function Cr(e){if(e.type.toLowerCase()==="none")return ar({kind:Oi});let t=e.identity.type.toLowerCase(),n=e.identity.service_account||e.service_account;if(t==="service"||t==="service_account"||e.type==="service_account")return ar({kind:Di,serviceAccount:n});if(e.type.toLowerCase()==="iap"&&t==="")return ar({kind:Oi});if(e.type===""&&t==="")return ar({kind:Oi});return ar({kind:Pu})}function Ym(e){return e.kind===Pu||e.kind===Di}function Sr(e){if(e.tokenKey!=="")return e.tokenKey;if(e.kind===Di)return`sa:${e.serviceAccount}`;if(e.email!=="")return`user:${e.email}`;return"user"}function i1(e){let t=new Map,n=(r,o)=>{t.set(`${r}\x00${o}`,{identity:r,audience:o})};for(let r of Object.values(e.services)){let o=Ga(r.identity);if(!Ym(o))continue;n(Sr(o),"")}for(let r of e.proxy.routes){let o=Cr(r.auth);if(o.kind===Oi)continue;n(Sr(o),r.auth.audience)}return[...t.values()].sort((r,o)=>{let i=r.identity.localeCompare(o.identity);if(i!==0)return i;return r.audience.localeCompare(o.audience)})}function s1(e,t,n){return e.some((r)=>r.identity===t&&r.audience===n)}function ar(e={}){return{kind:Oi,email:"",serviceAccount:"",project:"",projectSource:"",adcAvailable:!1,providerConfig:{},tokenKey:"",...e}}function Jm(e){let t=new Map,n=(r,o)=>{if(r!=="")t.set(r,(t.get(r)??!1)||o)};for(let r of Object.values(e.services))if(oo(r.identity)&&r.identity.service_account!=="")n(r.identity.service_account,!0);for(let r of e.proxy.routes){let o=r.auth.type.toLowerCase(),i=r.auth.identity.type.toLowerCase(),s=r.auth.identity.service_account||r.auth.service_account;if(i==="service"||i==="service_account"||o==="service_account")n(s,o!=="none")}return[...t.entries()].map(([r,o])=>({email:r,active:o})).sort((r,o)=>r.email.localeCompare(o.email))}function Br(e){return Jm(e).filter((t)=>t.active).map((t)=>t.email)}function a1(e,t,n){let r=[];for(let o of t){let i=e.services[o];if(!i)continue;let s=Ga(i.identity);if(oo(i.identity)&&i.identity.service_account===""){r.push({name:o,message:"service account identity is not configured"});continue}if((Ym(s)||i.capabilities.some((l)=>l==="google_api"||l==="iap"||l==="service_identity"))&&!n)r.push({name:o,message:"ADC unavailable"})}return r}function Ka(e){if(Br(e).length>0)return!0;if(e.proxy.routes.some((t)=>t.auth.type.toLowerCase()==="iap"))return!0;return Object.values(e.services).some((t)=>{if(Ym(Ga(t.identity)))return!0;return t.capabilities.some((n)=>n==="google_api"||n==="iap"||n==="service_identity")})}function Xm(){return{name:"user",accepts:(e)=>!oo(e),resolve:async(e,t)=>{if(e.type===""&&e.mode==="")return ar({kind:Oi});return{...await t(),kind:Pu}}}}function Qm(){return{name:"service_account",accepts:(e)=>oo(e),resolve:async(e,t)=>{if(e.service_account==="")throw C(zc,"service account identity is not configured");return{...await t(),kind:Di,serviceAccount:e.service_account}}}}async function l1(e,t,n){if(n)for(let r=n.length-1;r>=0;r-=1){let o=n[r];if(o?.accepts(e))return o.resolve(e,t)}if(oo(e))return Qm().resolve(e,t);return Xm().resolve(e,t)}var Pu="user",Di="service_account",Oi="none";var Un=E(()=>{Ee();te()});function jn(){return"devctl 0.13.2"}var Zt="0.13.2",Qa=2;function P1(e){return e}function lr(e){return e}function Wr(e){if(e.state===dt&&e.health===bn)return cr;if(e.state===dt&&e.health===Qe)return zr;return e.state}function ji(e){return{name:e,state:Yt,health:Jt,pid:0,ports:{},restarts:0,last_error:"",identity:"",startTime:void 0,cpuPercent:void 0,memoryKB:void 0,profile:"",env_source:"daemon",env:"",started_env:"",orphaned:!1}}function M1(e,t,n=(r)=>e.services[r]?.dependencies??[]){let r={},o={};for(let l of Object.keys(t))r[l]=0;for(let l of Object.keys(t)){let c=n(l);for(let u of c){let d=kt(u);if(!t[d])continue;o[d]=[...o[d]??[],l],r[l]=(r[l]??0)+1}}let i=[],s=Object.keys(t).length,a={};while(s>0){let l=Object.entries(r).filter(([c,u])=>u===0&&!a[c]).map(([c])=>c).sort();if(l.length===0)throw C(Bc,"dependency cycle detected while planning startup");i.push(l);for(let c of l){a[c]=!0,s-=1;for(let u of o[c]??[])r[u]=(r[u]??0)-1}}return i}function gh(e,t){if(!e.services[t])throw C(ki,`unknown service "${t}"`)}function Us(e,t,n){let r=n!==""?e.profiles[n]?.environment??{}:{},o=(c)=>Os(e,c,r),i={},s=(c)=>{if(i[c])return;gh(e,c),i[c]=!0;for(let u of o(c))s(kt(u))};for(let c of t)s(c);let a=M1(e,i,o),l=[];return a.forEach((c,u)=>{for(let d of c)l.push({name:d,wave:u+1,dependencies:[...o(d)]})}),{profile:n,steps:l,waves:a,blockers:[]}}function qi(e,t){let n={};for(let[i,s]of Object.entries(e.services))for(let a of s.dependencies){let l=kt(a);n[l]=[...n[l]??[],i]}let r={},o=(i)=>{if(r[i])return;gh(e,i),r[i]=!0;for(let s of n[i]??[])o(s)};for(let i of t)o(i);return Object.keys(r)}function I1(e,t){let n=[...M1(e,t)].reverse().map((o)=>[...o].sort()),r=[];return n.forEach((o,i)=>{for(let s of o)r.push({name:s,wave:i+1,dependencies:[...e.services[s]?.dependencies??[]]})}),{steps:r,waves:n,profile:"",blockers:[]}}function qu(e,t){let n=Object.fromEntries(qi(e,t).map((r)=>[r,!0]));return I1(e,n)}function Bu(e,t){let n={};for(let r of t)gh(e,r),n[r]=!0;return I1(e,n)}function N1(e){let t="";if(e.profile!=="")t+=`Profile: ${e.profile}
55
55
 
56
56
  `;t+=`Plan:
57
57
 
@@ -2017,10 +2017,17 @@ The release workflow then:
2017
2017
  2. Tests that exact tarball on supported macOS, Linux/glibc, Linux/musl, and Windows runners.
2018
2018
  3. Generates \`SHA256SUMS\` and GitHub build-provenance attestations.
2019
2019
  4. Publishes the npm version with provenance if it is not already present.
2020
- 5. Finalizes the GitHub Release and opens a Homebrew checksum update pull request.
2020
+ 5. Writes GitHub Release notes from that version's \`CHANGELOG.md\` section (headline, Fixed/Added entries, upgrade commands, trust notice) instead of auto-generated PR titles, then finalizes the GitHub Release and opens a Homebrew checksum update pull request.
2021
2021
 
2022
2022
  Publication is idempotent: rerunning a completed or partially completed workflow skips an npm version that is already on the registry and continues release finalization.
2023
2023
 
2024
+ GitHub's uploads API can return \`HTTP 500: Error saving asset\` when several large binaries are sent at once. The publish job creates the draft without assets, then uploads each file sequentially with retries. If a tag's Release run still fails after that:
2025
+
2026
+ 1. Re-run the failed jobs on that tag workflow, or
2027
+ 2. After this retry logic is on \`main\`, run **Release \u2192 Run workflow** and pass the existing tag (for example \`v0.13.1\`). That rebuilds from the tag using the workflow on the branch you dispatched from, so a publish fix does not require moving the tag.
2028
+
2029
+ A draft left with only some assets is expected after a mid-upload 500; the next successful run replaces them with \`--clobber\`.
2030
+
2024
2031
  ## Trust model
2025
2032
 
2026
2033
  npm provenance shows that the JavaScript package was published by this repository's workflow. GitHub attestations and \`SHA256SUMS\` establish the origin and integrity of standalone release files. They are free, but they do not replace Apple Developer ID notarization or Windows Authenticode signing; standalone binaries remain explicitly unsigned.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@amr-m-abdelgawad/devctl",
3
- "version": "0.13.1",
3
+ "version": "0.13.2",
4
4
  "description": "Local development orchestrator",
5
5
  "license": "MIT",
6
6
  "type": "module",