@amr-m-abdelgawad/devctl 0.3.0 → 0.3.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 +209 -137
  2. package/package.json +1 -1
package/dist/devctl.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env bun
2
2
  // @bun
3
- var VC=Object.defineProperty;var zC=(e)=>e;function YC(e,t){this[e]=zC.bind(null,t)}var JC=(e,t)=>{for(var r in t)VC(e,r,{get:t[r],enumerable:!0,configurable:!0,set:YC.bind(t,r)})};var D=(e,t)=>()=>(e&&(t=e(e=0)),t);var XC=import.meta.require;function xm(e,t=""){if(t==="MetadataLookupWarning")return!0;return e.includes("MetadataLookupWarning")||e.startsWith("received unexpected error =")}function zs(){if(ym)return;ym=!0;let e=process.emitWarning.bind(process);process.emitWarning=(t,...r)=>{let n=ZC(r[0]),o=QC(t);if(xm(o,n))return;e(t,...r)}}function bm(){let e=process.stderr.write.bind(process.stderr);return process.stderr.write=(t,r,n)=>{let o=eP(t);if(xm(o)){let i=typeof r==="function"?r:typeof n==="function"?n:void 0;if(typeof i==="function")i();return!0}if(typeof r==="function")return e(t,r);return e(t,r,n)},()=>{process.stderr.write=e}}function QC(e){if(typeof e==="string")return e;if(e instanceof Error)return e.message;return String(e)}function ZC(e){if(typeof e==="string")return e;if(e&&typeof e==="object"&&"type"in e&&typeof e.type==="string")return e.type;return""}function eP(e){if(typeof e==="string")return e;if(e instanceof Uint8Array)return new TextDecoder().decode(e);return""}var ym=!1;import{createHash as tP}from"crypto";import{resolve as rP}from"path";function ar(e){let t=rP(e),r=process.platform==="win32"?t.toLowerCase():t;return tP("sha256").update(r).digest("hex").slice(0,nP)}var nP=16;var Ys=()=>{};function Yl(e){let t=e.indexOf("Z-"),r=t>=0?e.slice(0,t+1):e,n=r.indexOf("T");if(n<0||!r.endsWith("Z"))return;let o=r.slice(0,n),i=r.slice(n+1,-1).replace(/-/g,":"),s=new Date(`${o}T${i}Z`);return Number.isNaN(s.getTime())?void 0:s}function sd(e=process.platform){return e==="darwin"}function wP(e=process.platform){return sd(e)?"cmd":"ctrl"}function Lo(e,t=process.platform){return`${wP(t)}+${e}`}function SP(e=process.platform){return sd(e)?"command":"ctrl"}function It(e,t=process.platform){return`${SP(t)}+${e}`}function Ri(e){return e.replace(/\bcmd\b/gi,"command")}function Or(e,t=process.platform){let r=sd(t),n=Boolean(e.meta||e.super),o=Boolean(e.ctrl);return r?n&&!o:o&&!n}function Zr(e=process.platform){return Lo("c",e)}function kP(e=process.platform){return{leader:Lo("x",e),command_list:Lo("p",e),command:"/",help:"?",search:"f",quit:"q",services:"s",logs:"l",auth:"a",proxy:"p",doctor:"d",config:"c",setup:"u",refresh:"r",restart:"R",find:"f",fullscreen:"z",confirm:"enter",cancel:"esc",select:"space",interrupt:"escape",copy:Zr(e)}}function Em(){return{theme:"devctl",leader_timeout:2000,font_size:14,keybinds:{...CP},scroll_speed:3,scroll_acceleration:{enabled:!1},diff_style:"auto",cursor:{style:"block",blinking:!0},mouse:!0,attention:{enabled:!1,notifications:!0,sound:!0,volume:0.4,sound_pack:"default",sounds:{}},log_timestamps:!0,log_metadata:!0,mcp_enabled:!1}}function PP(e){if(e===""||e==="none")return[];return e.split(",").flatMap((t)=>{let r=t.trim();if(r===""||r==="none"||/\s/.test(r))return[];let n=r.toLowerCase().split("+"),o=n[n.length-1]??"";return[{name:o==="return"?"return":o,ctrl:n.includes("ctrl")||n.includes("control"),shift:n.includes("shift"),meta:n.includes("meta")||n.includes("cmd")||n.includes("super"),alt:n.includes("alt")||n.includes("option")}]})}function mn(e,t){return PP(t).some((n)=>(e.name??"").toLowerCase()===n.name&&Boolean(e.ctrl)===n.ctrl&&Boolean(e.shift)===n.shift&&Boolean(e.meta||e.super)===n.meta&&Boolean(e.option??e.alt)===n.alt)}var id=2000,Do=14,_m="DEVCTL_TUI_CONFIG",CP;var Zs=D(()=>{CP=kP()});function Se(e,t,r){return{type:e,timestamp:new Date().toISOString(),service:t,payload:r}}class dd{subs=new Map;nextID=0;buffer=[];maxBuf;constructor(e){this.maxBuf=e>0?e:1024}subscribe(e,t=[]){this.nextID+=1;let r=this.nextID;return this.subs.set(r,{types:new Set(t),handler:e}),()=>{this.subs.delete(r)}}publish(e){let t=e.timestamp===""?{...e,timestamp:new Date().toISOString()}:e;if(this.buffer.length>=this.maxBuf)this.buffer=this.buffer.slice(1);this.buffer.push(t);for(let r of this.subs.values())if(r.types.size===0||r.types.has(t.type))r.handler(t)}recent(){return[...this.buffer]}}var Lm="ServiceStarted",sa="ServiceStopped",aa="ServiceFailed",Dm="ServiceStateChanged",Im="ServiceHealthChanged",Mo="LogReceived",Oo="TokenRefreshed",Gn="TokenRefreshFailed",cd="AuthenticationChanged",Mm="ProxyStarted",Om="ProxyStopped",ld="ProxyRequest",ca="ConfigurationChanged",_i="ConfigurationReloadFailed",$m="SessionRecovered";var Fm=5000,Nm=500,ud=16384,Hm=65536,la="TRACE",da="DEBUG",ua="INFO",Ei="WARN",Kn="ERROR",$o="FATAL",pa="UNKNOWN",pd;var Ti=D(()=>{pd={["TRACE"]:0,["DEBUG"]:1,["INFO"]:2,["WARN"]:3,["ERROR"]:4,["FATAL"]:5,["UNKNOWN"]:2}});function jm(){return{name:"default",parse:(e)=>{let t=gd(e);if(t)return t;return{level:vd(e),request_id:yd(e)||void 0}}}}function Ai(e,t){for(let r of t){let n=e[r];if(typeof n==="string"&&n.trim()!=="")return n}return}function Um(e){for(let t of IP){let r=e[t];if(typeof r==="number"){let n=UP[r];if(n)return n;continue}if(typeof r==="string"&&r.trim()!=="")return r.toUpperCase()}return}function Li(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}function Zt(e){if(typeof e==="string"&&e.trim()!=="")return e;if(typeof e==="number"&&Number.isFinite(e))return String(e);return}function Fo(e){if(typeof e==="number"&&Number.isFinite(e))return e;if(typeof e==="string"&&e.trim()!==""){let t=Number(e);if(Number.isFinite(t))return t}return}function $r(e,t){for(let r of t)if(e[r]!==void 0&&e[r]!==null)return e[r];return}function md(e){return e.filter((t)=>t!==void 0&&t!=="").join(" ")}function WP(e){if(e>=OP)return Kn;if(e>=MP)return Ei;return}function GP(e){if(Li(e))return e;return{}}function Wm(e){let t=Zt($r(e,["http.method","method","request_method"])),r=Zt($r(e,["http.route","url.path","path","route","request_uri"])),n=Zt($r(e,["http.status_code","status","status_code"])),o=md([t,r,n]);return o===""?void 0:o}function Gm(e){if(!Li(e))return Zt(e);if(typeof e.stringValue==="string")return e.stringValue.trim()===""?void 0:e.stringValue;return Zt(e.intValue??e.doubleValue??e.boolValue)}function KP(e){if(typeof e==="string"&&e.trim()!=="")return e;return Gm(e)}function Km(e){if(!Array.isArray(e)||e.length===0)return!1;return e.every((t)=>Li(t)&&typeof t.key==="string"&&t.value!==void 0)}function qP(e){let t={};if(!Km(e))return t;for(let r of e){let n=r.key;if(typeof n==="string"&&n!==""){let o=Gm(r.value);if(o!==void 0)t[n]=o}}return t}function VP(e){let t=Um(e);if(t)return t;let r=Fo(e.severityNumber);if(r===void 0)return;if(r>=BP)return $o;if(r>=jP)return Kn;if(r>=HP)return Ei;if(r>=NP)return ua;if(r>=FP)return da;if(r>=$P)return la;return}function zP(e){if(e.timeUnixNano!==void 0||e.observedTimeUnixNano!==void 0)return!0;if(Li(e.body)&&(e.body.stringValue!==void 0||e.body.intValue!==void 0||e.body.doubleValue!==void 0))return!0;return Km(e.attributes)}function YP(e,t){if(!zP(e))return;let r=qP(e.attributes),n=KP(e.body)??Wm(r)??"log",o=Ai(e,fd)??Zt(r.trace_id??r.traceId);return{message:n,level:VP(e),request_id:o,raw:t}}function JP(e,t){let r=Zt(e.metric_name);if(r===void 0)return;let n=Zt(e.metric_type),o=Fo(e.value);if(n===void 0&&o===void 0)return;let i=Zt(e.unit),s=o===void 0?void 0:i===void 0?String(o):`${o} ${i}`;return{message:md([r,n,s,Wm(GP(e.attributes))]),raw:t}}function XP(e){let t=e.trim().split(/\s+/);return{method:t[0],uri:t[1]}}function QP(e,t){if(Ai(e,Bm)!==void 0)return;let r=Zt(e.request),n=r===void 0?{}:XP(r),o=Zt($r(e,["request_method","method","http_method","verb"]))??n.method,i=Zt($r(e,["request_uri","uri","url","path","request_path"]))??n.uri,s=Fo($r(e,["status","status_code","statusCode","http_status"])),a=Zt($r(e,["remote_ip","remote_addr","client_ip"]));if(o===void 0||i===void 0)return;if(s===void 0&&a===void 0&&r===void 0)return;let c=Fo($r(e,["body_bytes_sent","bytes_sent","bytes"])),l=Fo($r(e,["request_time","duration"])),d=Fo($r(e,["request_time_ms","latency_ms","duration_ms"])),u=c===void 0?void 0:`${c}B`,f=d!==void 0?`${d}ms`:l===void 0?void 0:`${l}s`;return{message:md([a,o,i,s===void 0?void 0:String(s),u,f]),level:s===void 0?void 0:WP(s),request_id:Ai(e,fd),raw:t}}function ZP(e,t){let r=Ai(e,Bm),n=Um(e),o=Ai(e,fd);if(r===void 0&&n===void 0&&o===void 0)return;return{message:r??t,level:n,request_id:o,raw:t}}function hd(e){if(e.length<=ud)return e;return e.slice(0,ud)}function gd(e){let t=e.trim();if(!t.startsWith("{")||!t.endsWith("}"))return;if(t.length>Hm)return;let r;try{r=JSON.parse(t)}catch{return}if(!Li(r))return;return YP(r,t)??JP(r,t)??QP(r,t)??ZP(r,t)}function vd(e){return tR.find((r)=>r.re.test(e))?.level??pa}function yd(e){return eR.exec(e)?.[1]??""}var Bm,IP,fd,MP=400,OP=500,$P=1,FP=5,NP=9,HP=13,jP=17,BP=21,UP,eR,tR;var qm=D(()=>{Ti();Bm=["message","msg","text","log","event"],IP=["level","severity","severityText","levelname","loglevel","lvl"],fd=["request_id","requestId","trace_id","traceId","correlation_id","correlationId"],UP={10:la,20:da,30:ua,40:Ei,50:Kn,60:$o};eR=/(?:x-devctl-request-id|request[_-]?id)[=: ]+([A-Za-z0-9-]+)/i,tR=[{re:/\b(fatal|critical)\b/i,level:$o},{re:/\b(error|err)\b/i,level:Kn},{re:/\b(warn|warning)\b/i,level:Ei},{re:/\b(debug|dbg)\b/i,level:da},{re:/\b(trace)\b/i,level:la},{re:/\b(info|information)\b/i,level:ua}]});function Di(e){let t=rR(e);if(t===void 0)return;return new RegExp(t)}function rR(e){if(e.length===0||e.length>200)return;let t={raw:e,i:0,out:[],quantifiers:0,depth:0,lastAtom:"none",groupQuant:[]};if(!Vm(t)||t.i!==e.length||t.depth!==0)return;return t.out.join("")}function Vm(e){while(e.i<e.raw.length){if(e.raw[e.i]===")")return!0;if(!nR(e))return!1}return!0}function nR(e){let t=e.raw[e.i]??"";if(t==="|")return fa(e,"|","none");if(t==="^")return fa(e,"^","none");if(t==="$")return fa(e,"$","none");if(t===".")return fa(e,".","atom");if(t==="(")return oR(e);if(t==="[")return sR(e);if(t==="*"||t==="+"||t==="?"||t==="{")return iR(e);if(t==="\\")return zm(e);return e.out.push(RegExp.escape(t)),e.i+=1,e.lastAtom="atom",!0}function fa(e,t,r){return e.out.push(t),e.i+=1,e.lastAtom=r,!0}function oR(e){if(e.i+=1,e.depth+=1,e.depth>2)return!1;if(e.groupQuant.push(!1),e.out.push("(?:"),!Vm(e)||e.raw[e.i]!==")")return!1;e.out.push(")"),e.i+=1,e.depth-=1;let t=e.groupQuant.pop()===!0;return e.lastAtom=t?"quantified-group":"atom",!0}function iR(e){let t=e.raw[e.i]??"";if(t==="{"||e.lastAtom==="none"||e.lastAtom==="quant"||e.lastAtom==="quantified-group")return!1;if(e.quantifiers>=8)return!1;if(t==="*")e.out.push("*");else if(t==="+")e.out.push("+");else if(t==="?")e.out.push("?");else return!1;e.i+=1,e.quantifiers+=1;let r=e.groupQuant.length-1;if(r>=0)e.groupQuant[r]=!0;if(e.raw[e.i]==="?")e.out.push("?"),e.i+=1;return e.lastAtom="quant",!0}function sR(e){if(e.i+=1,e.out.push("["),e.raw[e.i]==="^")e.out.push("^"),e.i+=1;if(e.raw[e.i]==="]")e.out.push(RegExp.escape("]")),e.i+=1;while(e.i<e.raw.length&&e.raw[e.i]!=="]"){let t=e.raw[e.i]??"";if(t==="\\"){if(!zm(e))return!1}else if(t==="-")e.out.push("-"),e.i+=1;else e.out.push(RegExp.escape(t)),e.i+=1}if(e.raw[e.i]!=="]")return!1;return e.out.push("]"),e.i+=1,e.lastAtom="atom",!0}function zm(e){let t=e.raw[e.i+1];if(t===void 0)return!1;if(t==="d")e.out.push("\\d");else if(t==="D")e.out.push("\\D");else if(t==="w")e.out.push("\\w");else if(t==="W")e.out.push("\\W");else if(t==="s")e.out.push("\\s");else if(t==="S")e.out.push("\\S");else if(t==="n")e.out.push("\\n");else if(t==="t")e.out.push("\\t");else if(t==="r")e.out.push("\\r");else if(t==="b")e.out.push("\\b");else if(t==="B")e.out.push("\\B");else e.out.push(RegExp.escape(t));return e.i+=2,e.lastAtom="atom",!0}function No(e,t){if(e.services&&e.services.length>0&&!e.services.includes(t.service))return!1;if(e.level&&(pd[t.level]??2)<(pd[e.level]??2))return!1;if(e.source&&t.source!==e.source)return!1;if(e.since&&t.timestamp<e.since)return!1;if(e.until&&t.timestamp>e.until)return!1;return!0}function xd(e){if(!e.search)return()=>!0;if(e.regex){let r=Di(e.search);if(r)return(n)=>r.test(n.message)||n.raw!==void 0&&r.test(n.raw)}let t=e.search.toLowerCase();return(r)=>r.message.toLowerCase().includes(t)||r.raw!==void 0&&r.raw.toLowerCase().includes(t)}function bd(e){let t=xd(e);return(r)=>No(e,r)&&t(r)}var Ym=D(()=>{Ti()});function Jm(e){if(!Number.isInteger(e)||(e??0)<=0)return Nm;return Math.min(e,Fm)}var Xm=D(()=>{Ti()});var ma=D(()=>{Ti();qm();Ym();Xm()});function I(e,t){return new Ve(e,t)}function Pe(e,t,r){let n=r instanceof Error?r:Error(String(r));return new Ve(e,t,{cause:n})}function Te(e,t,r,n=""){return new Ve(e,t,{hint:r,service:n})}function va(e,t){return e instanceof Ve&&e.kind===t}function ch(e){if(e instanceof Ve)return e.exitCode();return 1}function lh(e){if(e instanceof Ve)return{error:A(e),kind:e.kind,hint:e.hint,service:e.service};return{error:A(e)}}function dh(e){if(typeof e==="string")return I("general",e);if(e.kind)return new Ve(e.kind,e.error.replace(`${e.kind}: `,""),{hint:e.hint,service:e.service});return I("general",e.error)}function A(e){if(e instanceof Ve){if(e.hint!=="")return`${ih(e)} \u2014 ${e.hint}`;return ih(e)}if(e instanceof Error)return e.message;return String(e)}function ih(e){let t=`${e.kind}: `;if(e.message.startsWith(t)){let r=e.message.slice(t.length),n=e.causeError?`: ${e.causeError.message}`:"";if(n!==""&&r.endsWith(n))return r.slice(0,r.length-n.length);return r}return e.message}var sh=1,Q="configuration",Vn="configuration_missing",Ho="service_not_found",ah="dependency",He="process_start",hn="authentication",zn="authorization",Fr="token",ha="impersonation",ga="iap",Nr="proxy",Ed="health_check",lt="general",Ve;var te=D(()=>{Ve=class Ve extends Error{kind;hint;service;causeError;constructor(e,t,r){super(r?.cause?`${e}: ${t}: ${r.cause.message}`:`${e}: ${t}`);this.name="DevctlError",this.kind=e,this.hint=r?.hint??"",this.service=r?.service??"",this.causeError=r?.cause}exitCode(){switch(this.kind){case"configuration":case"configuration_missing":case"service_not_found":case"dependency":return 2;case"authentication":case"token":case"iap":return 3;case"authorization":case"impersonation":return 4;case"process_start":return 5;case"health_check":return 6;case"proxy":return 7;default:return 1}}}});function ka(e){return e>0?e:300}function Ld(){return{enabled:!1,paths:[],debounce_ms:300,ignore:[...Mi]}}function Dd(){return{enabled:void 0,host:"",port:""}}function Oi(){return{type:"",identity:{type:"",service_account:""},audience:"",service_account:"",client_id:"",client_secret:""}}function Bo(){return{args:[],shell:!1}}function Id(){return{vars:{},required:[],defaults:{}}}function Md(){return{type:"",url:"",address:"",command:Bo(),interval_seconds:0,timeout_seconds:0,start_period_seconds:0,unhealthy_threshold:3,healthy_reset_threshold:10}}function zt(e){return typeof e==="string"?e:e.service}function gn(e){return typeof e==="string"?"service_started":e.condition||"service_started"}function Pa(e){return gn(e)==="service_healthy"?`${zt(e)} (healthy)`:zt(e)}function Od(){return{type:"",mode:"",service_account:"",config:{}}}function vn(){return{extends:"",description:"",command:Bo(),shell:!1,working_dir:"",dependencies:[],ports:[],environment:Id(),health:Md(),identity:Od(),logs:{stdout:!1,stderr:!1},restart:{policy:"",max_retries:0,backoff_seconds:0},startup:{wait_for_healthy:!1,timeout_seconds:0},capabilities:[],proxy:[],expose:Dd(),container:void 0,watch:Ld(),hooks:{pre_start:Bo(),post_start:Bo()}}}function Jn(){return{version:1,project:{name:""},google:{project_id:"",region:""},profiles:{},templates:{},services:{},tasks:{},proxy:{enabled:!1,gateway:!1,listen:{host:Ca,port:0},token_endpoint:{enabled:!1,host:"",port:0},routes:[]},logs:{max_memory_events:LR,persistence:{enabled:!0,directory:"~/.devctl/logs",retention_days:DR,max_session_logs:0}},auth:{refresh_threshold_seconds:mh},shutdown:{grace_seconds:fh},ui:{theme:"system",keymap:{}},secrets:{extra_markers:[],extra_patterns:[]},doctor:{tools:[]},plugins:[],environment:{sources:[],secrets:{}},provenance:{},repoRoot:"",configPath:""}}function yn(e){return e.args.length===0||e.args.length===1&&(e.args[0]??"").trim()===""}function Fi(e){if(e.type!=="")return e.type;return e.mode}function en(e){let t=Fi(e).toLowerCase();return t==="service"||t==="service_account"}function hh(e){let t=Fi(e).toLowerCase();return t==="user"||t===""}function Ra(e){if(e.policy!==""){if(e.policy==="always"||e.policy==="on_failure"||e.policy==="never")return e.policy;return e.policy}if(e.enabled===!0)return"on_failure";return"never"}function xn(e){let t=e.host===""?Ca:e.host;if(e.port===0)return t;return`${t}:${e.port}`}function gh(e){if(e.refresh_threshold_seconds<=0)return mh;return e.refresh_threshold_seconds}function _a(e){if(e.stop_services_on_exit===void 0)return!0;return e.stop_services_on_exit}function bn(e){if(e.grace_seconds<=0)return fh;return e.grace_seconds}function vh(e){if(!e.logs.stdout&&!e.logs.stderr)return!0;return e.logs.stdout}function yh(e){if(!e.logs.stdout&&!e.logs.stderr)return!0;return e.logs.stderr}function Xn(e,t){return e.find((r)=>r.name===t)}function xh(e){return e[0]}var Uo=1,ba="never",wa="on_failure",Sa="always",Mi,$i=8080,fh=10,mh=300,LR=50000,DR=14,Ca="127.0.0.1";var ht=D(()=>{Mi=["**/node_modules/**","**/.git/**"]});var Nt=".devctl",lr="config.yaml";import{basename as IR}from"path";function wn(e,t=""){return{name:IR(e),project:t,profile:"",sa:"",audience:"",proxyPort:$i}}function Ea(e,t=$i){let r=Number(e);return Number.isFinite(r)&&r>0?r:t}function bh(e){let t=e;return`# yaml-language-server: $schema=https://raw.githubusercontent.com/amr-m-abdelgawad/devctl/main/schema/devctl.config.schema.json
3
+ var ZC=Object.defineProperty;var eP=(e)=>e;function tP(e,t){this[e]=eP.bind(null,t)}var rP=(e,t)=>{for(var r in t)ZC(e,r,{get:t[r],enumerable:!0,configurable:!0,set:tP.bind(t,r)})};var I=(e,t)=>()=>(e&&(t=e(e=0)),t);var nP=import.meta.require;function wm(e,t=""){if(t==="MetadataLookupWarning")return!0;return e.includes("MetadataLookupWarning")||e.startsWith("received unexpected error =")}function Js(){if(xm)return;xm=!0;let e=process.emitWarning.bind(process);process.emitWarning=(t,...r)=>{let n=iP(r[0]),o=oP(t);if(wm(o,n))return;e(t,...r)}}function Sm(){let e=process.stderr.write.bind(process.stderr);return process.stderr.write=(t,r,n)=>{let o=sP(t);if(wm(o)){let i=typeof r==="function"?r:typeof n==="function"?n:void 0;if(typeof i==="function")i();return!0}if(typeof r==="function")return e(t,r);return e(t,r,n)},()=>{process.stderr.write=e}}function oP(e){if(typeof e==="string")return e;if(e instanceof Error)return e.message;return String(e)}function iP(e){if(typeof e==="string")return e;if(e&&typeof e==="object"&&"type"in e&&typeof e.type==="string")return e.type;return""}function sP(e){if(typeof e==="string")return e;if(e instanceof Uint8Array)return new TextDecoder().decode(e);return""}var xm=!1;import{createHash as aP}from"crypto";import{resolve as cP}from"path";function ar(e){let t=cP(e),r=process.platform==="win32"?t.toLowerCase():t;return aP("sha256").update(r).digest("hex").slice(0,lP)}var lP=16;var Xs=()=>{};function Jl(e){let t=e.indexOf("Z-"),r=t>=0?e.slice(0,t+1):e,n=r.indexOf("T");if(n<0||!r.endsWith("Z"))return;let o=r.slice(0,n),i=r.slice(n+1,-1).replace(/-/g,":"),s=new Date(`${o}T${i}Z`);return Number.isNaN(s.getTime())?void 0:s}function ad(e=process.platform){return e==="darwin"}function EP(e=process.platform){return ad(e)?"cmd":"ctrl"}function Ao(e,t=process.platform){return`${EP(t)}+${e}`}function TP(e=process.platform){return ad(e)?"command":"ctrl"}function It(e,t=process.platform){return`${TP(t)}+${e}`}function Ei(e){return e.replace(/\bcmd\b/gi,"command")}function Or(e,t=process.platform){let r=ad(t),n=Boolean(e.meta||e.super),o=Boolean(e.ctrl);return r?n&&!o:o&&!n}function Zr(e=process.platform){return Ao("c",e)}function AP(e=process.platform){return{leader:Ao("x",e),command_list:Ao("p",e),command:"/",help:"?",search:"f",quit:"q",services:"s",logs:"l",auth:"a",proxy:"p",doctor:"d",config:"c",setup:"u",refresh:"r",restart:"R",find:"f",fullscreen:"z",confirm:"enter",cancel:"esc",select:"space",interrupt:"escape",copy:Zr(e)}}function Lm(){return{theme:"devctl",leader_timeout:2000,font_size:14,keybinds:{...LP},scroll_speed:3,scroll_acceleration:{enabled:!1},diff_style:"auto",cursor:{style:"block",blinking:!0},mouse:!0,attention:{enabled:!1,notifications:!0,sound:!0,volume:0.4,sound_pack:"default",sounds:{}},log_timestamps:!0,log_metadata:!0,mcp_enabled:!1}}function DP(e){if(e===""||e==="none")return[];return e.split(",").flatMap((t)=>{let r=t.trim();if(r===""||r==="none"||/\s/.test(r))return[];let n=r.toLowerCase().split("+"),o=n[n.length-1]??"";return[{name:o==="return"?"return":o,ctrl:n.includes("ctrl")||n.includes("control"),shift:n.includes("shift"),meta:n.includes("meta")||n.includes("cmd")||n.includes("super"),alt:n.includes("alt")||n.includes("option")}]})}function mn(e,t){return DP(t).some((n)=>(e.name??"").toLowerCase()===n.name&&Boolean(e.ctrl)===n.ctrl&&Boolean(e.shift)===n.shift&&Boolean(e.meta||e.super)===n.meta&&Boolean(e.option??e.alt)===n.alt)}var sd=2000,Lo=14,Am="DEVCTL_TUI_CONFIG",LP;var ta=I(()=>{LP=AP()});function Se(e,t,r){return{type:e,timestamp:new Date().toISOString(),service:t,payload:r}}class ud{subs=new Map;nextID=0;buffer=[];maxBuf;constructor(e){this.maxBuf=e>0?e:1024}subscribe(e,t=[]){this.nextID+=1;let r=this.nextID;return this.subs.set(r,{types:new Set(t),handler:e}),()=>{this.subs.delete(r)}}publish(e){let t=e.timestamp===""?{...e,timestamp:new Date().toISOString()}:e;if(this.buffer.length>=this.maxBuf)this.buffer=this.buffer.slice(1);this.buffer.push(t);for(let r of this.subs.values())if(r.types.size===0||r.types.has(t.type))r.handler(t)}recent(){return[...this.buffer]}}var Mm="ServiceStarted",ca="ServiceStopped",la="ServiceFailed",Om="ServiceStateChanged",$m="ServiceHealthChanged",Io="LogReceived",Mo="TokenRefreshed",Gn="TokenRefreshFailed",ld="AuthenticationChanged",Fm="ProxyStarted",Nm="ProxyStopped",dd="ProxyRequest",da="ConfigurationChanged",Ti="ConfigurationReloadFailed",Hm="SessionRecovered";var jm=5000,Bm=500,pd=16384,Um=65536,ua="TRACE",pa="DEBUG",fa="INFO",Ai="WARN",Kn="ERROR",Oo="FATAL",ma="UNKNOWN",fd;var Li=I(()=>{fd={["TRACE"]:0,["DEBUG"]:1,["INFO"]:2,["WARN"]:3,["ERROR"]:4,["FATAL"]:5,["UNKNOWN"]:2}});function Wm(){return{name:"default",parse:(e)=>{let t=vd(e);if(t)return t;return{level:yd(e),request_id:bd(e)||void 0}}}}function Di(e,t){for(let r of t){let n=e[r];if(typeof n==="string"&&n.trim()!=="")return n}return}function Km(e){for(let t of jP){let r=e[t];if(typeof r==="number"){let n=YP[r];if(n)return n;continue}if(typeof r==="string"&&r.trim()!=="")return r.toUpperCase()}return}function Ii(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}function Zt(e){if(typeof e==="string"&&e.trim()!=="")return e;if(typeof e==="number"&&Number.isFinite(e))return String(e);return}function $o(e){if(typeof e==="number"&&Number.isFinite(e))return e;if(typeof e==="string"&&e.trim()!==""){let t=Number(e);if(Number.isFinite(t))return t}return}function $r(e,t){for(let r of t)if(e[r]!==void 0&&e[r]!==null)return e[r];return}function hd(e){return e.filter((t)=>t!==void 0&&t!=="").join(" ")}function JP(e){if(e>=UP)return Kn;if(e>=BP)return Ai;return}function XP(e){if(Ii(e))return e;return{}}function qm(e){let t=Zt($r(e,["http.method","method","request_method"])),r=Zt($r(e,["http.route","url.path","path","route","request_uri"])),n=Zt($r(e,["http.status_code","status","status_code"])),o=hd([t,r,n]);return o===""?void 0:o}function Vm(e){if(!Ii(e))return Zt(e);if(typeof e.stringValue==="string")return e.stringValue.trim()===""?void 0:e.stringValue;return Zt(e.intValue??e.doubleValue??e.boolValue)}function QP(e){if(typeof e==="string"&&e.trim()!=="")return e;return Vm(e)}function zm(e){if(!Array.isArray(e)||e.length===0)return!1;return e.every((t)=>Ii(t)&&typeof t.key==="string"&&t.value!==void 0)}function ZP(e){let t={};if(!zm(e))return t;for(let r of e){let n=r.key;if(typeof n==="string"&&n!==""){let o=Vm(r.value);if(o!==void 0)t[n]=o}}return t}function e_(e){let t=Km(e);if(t)return t;let r=$o(e.severityNumber);if(r===void 0)return;if(r>=zP)return Oo;if(r>=VP)return Kn;if(r>=qP)return Ai;if(r>=KP)return fa;if(r>=GP)return pa;if(r>=WP)return ua;return}function t_(e){if(e.timeUnixNano!==void 0||e.observedTimeUnixNano!==void 0)return!0;if(Ii(e.body)&&(e.body.stringValue!==void 0||e.body.intValue!==void 0||e.body.doubleValue!==void 0))return!0;return zm(e.attributes)}function r_(e,t){if(!t_(e))return;let r=ZP(e.attributes),n=QP(e.body)??qm(r)??"log",o=Di(e,md)??Zt(r.trace_id??r.traceId);return{message:n,level:e_(e),request_id:o,raw:t}}function n_(e,t){let r=Zt(e.metric_name);if(r===void 0)return;let n=Zt(e.metric_type),o=$o(e.value);if(n===void 0&&o===void 0)return;let i=Zt(e.unit),s=o===void 0?void 0:i===void 0?String(o):`${o} ${i}`;return{message:hd([r,n,s,qm(XP(e.attributes))]),raw:t}}function o_(e){let t=e.trim().split(/\s+/);return{method:t[0],uri:t[1]}}function i_(e,t){if(Di(e,Gm)!==void 0)return;let r=Zt(e.request),n=r===void 0?{}:o_(r),o=Zt($r(e,["request_method","method","http_method","verb"]))??n.method,i=Zt($r(e,["request_uri","uri","url","path","request_path"]))??n.uri,s=$o($r(e,["status","status_code","statusCode","http_status"])),a=Zt($r(e,["remote_ip","remote_addr","client_ip"]));if(o===void 0||i===void 0)return;if(s===void 0&&a===void 0&&r===void 0)return;let c=$o($r(e,["body_bytes_sent","bytes_sent","bytes"])),l=$o($r(e,["request_time","duration"])),d=$o($r(e,["request_time_ms","latency_ms","duration_ms"])),u=c===void 0?void 0:`${c}B`,f=d!==void 0?`${d}ms`:l===void 0?void 0:`${l}s`;return{message:hd([a,o,i,s===void 0?void 0:String(s),u,f]),level:s===void 0?void 0:JP(s),request_id:Di(e,md),raw:t}}function s_(e,t){let r=Di(e,Gm),n=Km(e),o=Di(e,md);if(r===void 0&&n===void 0&&o===void 0)return;return{message:r??t,level:n,request_id:o,raw:t}}function gd(e){if(e.length<=pd)return e;return e.slice(0,pd)}function vd(e){let t=e.trim();if(!t.startsWith("{")||!t.endsWith("}"))return;if(t.length>Um)return;let r;try{r=JSON.parse(t)}catch{return}if(!Ii(r))return;return r_(r,t)??n_(r,t)??i_(r,t)??s_(r,t)}function yd(e){return c_.find((r)=>r.re.test(e))?.level??ma}function bd(e){return a_.exec(e)?.[1]??""}var Gm,jP,md,BP=400,UP=500,WP=1,GP=5,KP=9,qP=13,VP=17,zP=21,YP,a_,c_;var Ym=I(()=>{Li();Gm=["message","msg","text","log","event"],jP=["level","severity","severityText","levelname","loglevel","lvl"],md=["request_id","requestId","trace_id","traceId","correlation_id","correlationId"],YP={10:ua,20:pa,30:fa,40:Ai,50:Kn,60:Oo};a_=/(?:x-devctl-request-id|request[_-]?id)[=: ]+([A-Za-z0-9-]+)/i,c_=[{re:/\b(fatal|critical)\b/i,level:Oo},{re:/\b(error|err)\b/i,level:Kn},{re:/\b(warn|warning)\b/i,level:Ai},{re:/\b(debug|dbg)\b/i,level:pa},{re:/\b(trace)\b/i,level:ua},{re:/\b(info|information)\b/i,level:fa}]});function Mi(e){let t=l_(e);if(t===void 0)return;return new RegExp(t)}function l_(e){if(e.length===0||e.length>200)return;let t={raw:e,i:0,out:[],quantifiers:0,depth:0,lastAtom:"none",groupQuant:[]};if(!Jm(t)||t.i!==e.length||t.depth!==0)return;return t.out.join("")}function Jm(e){while(e.i<e.raw.length){if(e.raw[e.i]===")")return!0;if(!d_(e))return!1}return!0}function d_(e){let t=e.raw[e.i]??"";if(t==="|")return ha(e,"|","none");if(t==="^")return ha(e,"^","none");if(t==="$")return ha(e,"$","none");if(t===".")return ha(e,".","atom");if(t==="(")return u_(e);if(t==="[")return f_(e);if(t==="*"||t==="+"||t==="?"||t==="{")return p_(e);if(t==="\\")return Xm(e);return e.out.push(RegExp.escape(t)),e.i+=1,e.lastAtom="atom",!0}function ha(e,t,r){return e.out.push(t),e.i+=1,e.lastAtom=r,!0}function u_(e){if(e.i+=1,e.depth+=1,e.depth>2)return!1;if(e.groupQuant.push(!1),e.out.push("(?:"),!Jm(e)||e.raw[e.i]!==")")return!1;e.out.push(")"),e.i+=1,e.depth-=1;let t=e.groupQuant.pop()===!0;return e.lastAtom=t?"quantified-group":"atom",!0}function p_(e){let t=e.raw[e.i]??"";if(t==="{"||e.lastAtom==="none"||e.lastAtom==="quant"||e.lastAtom==="quantified-group")return!1;if(e.quantifiers>=8)return!1;if(t==="*")e.out.push("*");else if(t==="+")e.out.push("+");else if(t==="?")e.out.push("?");else return!1;e.i+=1,e.quantifiers+=1;let r=e.groupQuant.length-1;if(r>=0)e.groupQuant[r]=!0;if(e.raw[e.i]==="?")e.out.push("?"),e.i+=1;return e.lastAtom="quant",!0}function f_(e){if(e.i+=1,e.out.push("["),e.raw[e.i]==="^")e.out.push("^"),e.i+=1;if(e.raw[e.i]==="]")e.out.push(RegExp.escape("]")),e.i+=1;while(e.i<e.raw.length&&e.raw[e.i]!=="]"){let t=e.raw[e.i]??"";if(t==="\\"){if(!Xm(e))return!1}else if(t==="-")e.out.push("-"),e.i+=1;else e.out.push(RegExp.escape(t)),e.i+=1}if(e.raw[e.i]!=="]")return!1;return e.out.push("]"),e.i+=1,e.lastAtom="atom",!0}function Xm(e){let t=e.raw[e.i+1];if(t===void 0)return!1;if(t==="d")e.out.push("\\d");else if(t==="D")e.out.push("\\D");else if(t==="w")e.out.push("\\w");else if(t==="W")e.out.push("\\W");else if(t==="s")e.out.push("\\s");else if(t==="S")e.out.push("\\S");else if(t==="n")e.out.push("\\n");else if(t==="t")e.out.push("\\t");else if(t==="r")e.out.push("\\r");else if(t==="b")e.out.push("\\b");else if(t==="B")e.out.push("\\B");else e.out.push(RegExp.escape(t));return e.i+=2,e.lastAtom="atom",!0}function Fo(e,t){if(e.services&&e.services.length>0&&!e.services.includes(t.service))return!1;if(e.level&&(fd[t.level]??2)<(fd[e.level]??2))return!1;if(e.source&&t.source!==e.source)return!1;if(e.since&&t.timestamp<e.since)return!1;if(e.until&&t.timestamp>e.until)return!1;return!0}function xd(e){if(!e.search)return()=>!0;if(e.regex){let r=Mi(e.search);if(r)return(n)=>r.test(n.message)||n.raw!==void 0&&r.test(n.raw)}let t=e.search.toLowerCase();return(r)=>r.message.toLowerCase().includes(t)||r.raw!==void 0&&r.raw.toLowerCase().includes(t)}function wd(e){let t=xd(e);return(r)=>Fo(e,r)&&t(r)}var Qm=I(()=>{Li()});function Zm(e){if(!Number.isInteger(e)||(e??0)<=0)return Bm;return Math.min(e,jm)}var eh=I(()=>{Li()});var ga=I(()=>{Li();Ym();Qm();eh()});function E(e,t){return new Ve(e,t)}function Pe(e,t,r){let n=r instanceof Error?r:Error(String(r));return new Ve(e,t,{cause:n})}function Te(e,t,r,n=""){return new Ve(e,t,{hint:r,service:n})}function ba(e,t){return e instanceof Ve&&e.kind===t}function uh(e){if(e instanceof Ve)return e.exitCode();return 1}function ph(e){if(e instanceof Ve)return{error:L(e),kind:e.kind,hint:e.hint,service:e.service};return{error:L(e)}}function fh(e){if(typeof e==="string")return E("general",e);if(e.kind)return new Ve(e.kind,e.error.replace(`${e.kind}: `,""),{hint:e.hint,service:e.service});return E("general",e.error)}function L(e){if(e instanceof Ve){if(e.hint!=="")return`${ch(e)} \u2014 ${e.hint}`;return ch(e)}if(e instanceof Error)return e.message;return String(e)}function ch(e){let t=`${e.kind}: `;if(e.message.startsWith(t)){let r=e.message.slice(t.length),n=e.causeError?`: ${e.causeError.message}`:"";if(n!==""&&r.endsWith(n))return r.slice(0,r.length-n.length);return r}return e.message}var lh=1,Y="configuration",qn="configuration_missing",Ho="service_not_found",dh="dependency",He="process_start",hn="authentication",Vn="authorization",Fr="token",va="impersonation",ya="iap",Nr="proxy",Td="health_check",lt="general",Ve;var te=I(()=>{Ve=class Ve extends Error{kind;hint;service;causeError;constructor(e,t,r){super(r?.cause?`${e}: ${t}: ${r.cause.message}`:`${e}: ${t}`);this.name="DevctlError",this.kind=e,this.hint=r?.hint??"",this.service=r?.service??"",this.causeError=r?.cause}exitCode(){switch(this.kind){case"configuration":case"configuration_missing":case"service_not_found":case"dependency":return 2;case"authentication":case"token":case"iap":return 3;case"authorization":case"impersonation":return 4;case"process_start":return 5;case"health_check":return 6;case"proxy":return 7;default:return 1}}}});function Pa(e){return e>0?e:300}function Dd(){return{enabled:!1,paths:[],debounce_ms:300,ignore:[...$i]}}function Id(){return{enabled:void 0,host:"",port:""}}function Fi(){return{type:"",identity:{type:"",service_account:""},audience:"",service_account:"",client_id:"",client_secret:"",credentials:""}}function Bo(){return{args:[],shell:!1}}function Md(){return{vars:{},required:[],defaults:{}}}function Od(){return{type:"",url:"",address:"",command:Bo(),interval_seconds:0,timeout_seconds:0,start_period_seconds:0,unhealthy_threshold:3,healthy_reset_threshold:10}}function zt(e){return typeof e==="string"?e:e.service}function gn(e){return typeof e==="string"?"service_started":e.condition||"service_started"}function Ra(e){return gn(e)==="service_healthy"?`${zt(e)} (healthy)`:zt(e)}function $d(){return{type:"",mode:"",service_account:"",config:{}}}function vn(){return{extends:"",description:"",command:Bo(),shell:!1,working_dir:"",dependencies:[],ports:[],environment:Md(),health:Od(),identity:$d(),logs:{stdout:!1,stderr:!1},restart:{policy:"",max_retries:0,backoff_seconds:0},startup:{wait_for_healthy:!1,timeout_seconds:0},capabilities:[],proxy:[],expose:Id(),container:void 0,watch:Dd(),hooks:{pre_start:Bo(),post_start:Bo()}}}function Yn(){return{version:1,project:{name:""},google:{project_id:"",region:""},profiles:{},templates:{},services:{},tasks:{},proxy:{enabled:!1,gateway:!1,credentials:"",listen:{host:_a,port:0},token_endpoint:{enabled:!1,host:"",port:0},routes:[]},logs:{max_memory_events:O_,persistence:{enabled:!0,directory:"~/.devctl/logs",retention_days:$_,max_session_logs:0}},auth:{refresh_threshold_seconds:vh},shutdown:{grace_seconds:gh},ui:{theme:"system",keymap:{}},secrets:{extra_markers:[],extra_patterns:[]},doctor:{tools:[]},plugins:[],environment:{sources:[],secrets:{}},provenance:{},repoRoot:"",configPath:""}}function yn(e){return e.args.length===0||e.args.length===1&&(e.args[0]??"").trim()===""}function Hi(e){if(e.type!=="")return e.type;return e.mode}function en(e){let t=Hi(e).toLowerCase();return t==="service"||t==="service_account"}function yh(e){let t=Hi(e).toLowerCase();return t==="user"||t===""}function Ea(e){if(e.policy!==""){if(e.policy==="always"||e.policy==="on_failure"||e.policy==="never")return e.policy;return e.policy}if(e.enabled===!0)return"on_failure";return"never"}function bn(e){let t=e.host===""?_a:e.host;if(e.port===0)return t;return`${t}:${e.port}`}function bh(e){if(e.refresh_threshold_seconds<=0)return vh;return e.refresh_threshold_seconds}function Ta(e){if(e.stop_services_on_exit===void 0)return!0;return e.stop_services_on_exit}function xn(e){if(e.grace_seconds<=0)return gh;return e.grace_seconds}function xh(e){if(!e.logs.stdout&&!e.logs.stderr)return!0;return e.logs.stdout}function wh(e){if(!e.logs.stdout&&!e.logs.stderr)return!0;return e.logs.stderr}function Jn(e,t){return e.find((r)=>r.name===t)}function Sh(e){return e[0]}var Uo=1,Sa="never",ka="on_failure",Ca="always",$i,Ni=8080,gh=10,vh=300,O_=50000,$_=14,_a="127.0.0.1";var ht=I(()=>{$i=["**/node_modules/**","**/.git/**"]});var Nt=".devctl",lr="config.yaml";import{basename as F_}from"path";function wn(e,t=""){return{name:F_(e),project:t,profile:"",sa:"",audience:"",proxyPort:Ni}}function Aa(e,t=Ni){let r=Number(e);return Number.isFinite(r)&&r>0?r:t}function kh(e){let t=e;return`# yaml-language-server: $schema=https://raw.githubusercontent.com/amr-m-abdelgawad/devctl/main/schema/devctl.config.schema.json
4
4
  version: 1
5
5
 
6
6
  project:
@@ -47,8 +47,8 @@ auth:
47
47
  shutdown:
48
48
  stop_services_on_exit: true
49
49
  grace_seconds: 10
50
- `}var wt;var Ta=D(()=>{ht();wt=[{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 Nd,mkdirSync as $d,statSync as MR,writeFileSync as OR}from"fs";import{createInterface as $R}from"readline/promises";import{stdin as FR,stdout as NR}from"process";import{basename as Aa,dirname as Fd,join as tn,resolve as HR}from"path";function Hd(e,t){let r=e===""?process.cwd():e;if(t==="")return{repo:r,cfgPath:tn(r,Nt,lr)};let n=HR(t);if(!(Nd(n)?MR(n).isDirectory():!/\.ya?ml$/i.test(n))){let i=Fd(n);return{repo:Aa(i)===Nt?Fd(i):i,cfgPath:n}}if(Aa(n)===Nt)return{repo:Fd(n),cfgPath:tn(n,lr)};return{repo:n,cfgPath:tn(n,Nt,lr)}}function wh(e,t=Aa(e),r="",n="",o=!1){let i=tn(e,Nt,lr);if(!o&&Nd(i))throw I(Q,`configuration already exists at ${i}; not overwriting`);return La(e,{...wn(e,r),name:t,profile:n}),i}async function Sh(e,t,r="",n=!1){let{repo:o,cfgPath:i}=Hd(t,r);if(!n&&Nd(i)){Ht(`Found existing configuration at ${i}; nothing written.`),Ht("Pass --force to overwrite it, or edit it directly.");return}let s=$R({input:FR,output:NR}),a=async(h,v)=>{let x=v!==""?` [${v}]`:"",g=(await s.question(`${h}${x}: `)).trim();return g===""?v:g},c=0,l=()=>(c+=1,c);if(Ht(`devctl setup \u2014 ${wt.length} steps`),Ht(""),r==="")await a(`${l()}. Repository root`,o);let d=wn(o,""),u=await a(`${l()}. ${wt[1]?.prompt}`,Aa(o));d.name=u;let f=await e.detectGoogle(""),p=await a(`${l()}. ${wt[2]?.prompt}`,f.projectID);if(d.project=p,Ht(`${l()}. ${wt[3]?.title}`),!f.adcAvailable){if(Ht(" 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(v){Ht(` login failed: ${v instanceof Error?v.message:String(v)}`)}}else if(Ht(" \u2713 Application Default Credentials available"),f.userEmail!=="")Ht(` user: ${f.userEmail}`);d.sa=await a(`${l()}. ${wt[4]?.prompt}`,""),d.audience=await a(`${l()}. ${wt[5]?.prompt}`,"");let m=await a(`${l()}. ${wt[6]?.prompt}`,String($i));d.proxyPort=Ea(m),d.profile=await a(`${l()}. ${wt[7]?.prompt}`,""),s.close(),La(o,d),Ht(""),Ht(`Wrote starter configuration to ${i}`),Ht(`${l()}. Validation`);try{let h=e.load(o,""),v=await e.runDoctor.execute(h);Ht(e.formatDoctor(v))}catch(h){Ht(` configuration is not valid yet: ${h instanceof Error?h.message:String(h)}`),Ht(" edit .devctl/config.yaml and run `devctl config validate`")}}function La(e,t){let r=tn(e,".devctl");$d(tn(r,"services"),{recursive:!0}),$d(tn(r,"profiles"),{recursive:!0}),$d(tn(r,"proxy"),{recursive:!0}),OR(tn(r,"config.yaml"),bh(t))}function Ht(e){process.stdout.write(`${e}
51
- `)}var Da=D(()=>{ht();Ta();te()});function gg(){return/\$\{(?:env\.)?([A-Za-z_][A-Za-z0-9_]*)\}/g}function Zd(e){return[...e.matchAll(gg())].map((t)=>t[1]??"")}function vg(e,t){let r=[];return{value:e.replace(gg(),(o,i)=>{let s=t[i];if(s===void 0||s==="")return r.push(i),"";return s}),missing:r}}function Ha(e){let t=e.trim();if(t===""||Zd(t).length===0)return;return t}function yg(e){return/^\$\{(?:env\.)?[A-Za-z_][A-Za-z0-9_]*\}$/.test(e.trim())}function Pn(e){return Object.entries(e.provenance).sort(([t],[r])=>t.localeCompare(r)).flatMap(([t,r])=>{let n=r[r.length-1];if(!n)return[];return[{path:t,value:Q_(e,t),...n,shadowed:r.slice(0,-1)}]})}function Q_(e,t){let r=e;for(let n of t.split(".")){if(typeof r!=="object"||r===null)return;r=r[n]}return r}function Ga(e){if(en(e))return dr({kind:no,serviceAccount:e.service_account});if(hh(e)&&(e.type!==""||e.mode!==""))return dr({kind:Wa});let t=(e.type||e.mode).toLowerCase();if(t!=="")return dr({kind:t,providerConfig:e.config??{}});return dr({kind:Go})}function oo(e){if(e.type.toLowerCase()==="none")return dr({kind:Go});let t=e.identity.type.toLowerCase(),r=e.identity.service_account||e.service_account;if(t==="service"||t==="service_account"||e.type==="service_account")return dr({kind:no,serviceAccount:r});if(e.type.toLowerCase()==="iap"&&t==="")return dr({kind:Go});if(e.type===""&&t==="")return dr({kind:Go});return dr({kind:Wa})}function _g(e){return e.kind===Wa||e.kind===no}function Ko(e){if(e.tokenKey!=="")return e.tokenKey;if(e.kind===no)return`sa:${e.serviceAccount}`;if(e.email!=="")return`user:${e.email}`;return"user"}function dr(e={}){return{kind:Go,email:"",serviceAccount:"",project:"",projectSource:"",adcAvailable:!1,providerConfig:{},tokenKey:"",...e}}function nu(e){let t=new Map,r=(n,o)=>{if(n!=="")t.set(n,(t.get(n)??!1)||o)};for(let n of Object.values(e.services))if(en(n.identity)&&n.identity.service_account!=="")r(n.identity.service_account,!0);for(let n of e.proxy.routes){let o=n.auth.type.toLowerCase(),i=n.auth.identity.type.toLowerCase(),s=n.auth.identity.service_account||n.auth.service_account;if(i==="service"||i==="service_account"||o==="service_account")r(s,o!=="none")}return[...t.entries()].map(([n,o])=>({email:n,active:o})).sort((n,o)=>n.email.localeCompare(o.email))}function Hr(e){return nu(e).filter((t)=>t.active).map((t)=>t.email)}function Eg(e,t,r){let n=[];for(let o of t){let i=e.services[o];if(!i)continue;let s=Ga(i.identity);if(en(i.identity)&&i.identity.service_account===""){n.push({name:o,message:"service account identity is not configured"});continue}if((_g(s)||i.capabilities.some((c)=>c==="google_api"||c==="iap"||c==="service_identity"))&&!r)n.push({name:o,message:"ADC unavailable"})}return n}function Ui(e){if(Hr(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(_g(Ga(t.identity)))return!0;return t.capabilities.some((r)=>r==="google_api"||r==="iap"||r==="service_identity")})}function ou(){return{name:"user",accepts:(e)=>!en(e),resolve:async(e,t)=>{if(e.type===""&&e.mode==="")return dr({kind:Go});return{...await t(),kind:Wa}}}}function iu(){return{name:"service_account",accepts:(e)=>en(e),resolve:async(e,t)=>{if(e.service_account==="")throw I(ha,"service account identity is not configured");return{...await t(),kind:no,serviceAccount:e.service_account}}}}async function Tg(e,t,r){if(r)for(let n=r.length-1;n>=0;n-=1){let o=r[n];if(o?.accepts(e))return o.resolve(e,t)}if(en(e))return iu().resolve(e,t);return ou().resolve(e,t)}var Wa="user",no="service_account",Go="none";var jr=D(()=>{ht();te()});function er(){return"devctl 0.3.0"}var jt="0.3.0",Vi=1;function zg(e){return e}function ur(e){return e}function Jo(e){if(e.state===je&&e.health===Bt)return pr;if(e.state===je&&e.health===Oe)return Br;return e.state}function Ji(e){return{name:e,state:_t,health:Et,pid:0,ports:{},restarts:0,last_error:"",identity:"",startTime:void 0,cpuPercent:void 0,memoryKB:void 0,profile:"",env_source:"daemon",orphaned:!1}}function Xg(e,t){let r={},n={};for(let a of Object.keys(t))r[a]=0;for(let a of Object.keys(t)){let c=e.services[a]?.dependencies??[];for(let l of c){let d=zt(l);if(!t[d])continue;n[d]=[...n[d]??[],a],r[a]=(r[a]??0)+1}}let o=[],i=Object.keys(t).length,s={};while(i>0){let a=Object.entries(r).filter(([c,l])=>l===0&&!s[c]).map(([c])=>c).sort();if(a.length===0)throw I(ah,"dependency cycle detected while planning startup");o.push(a);for(let c of a){s[c]=!0,i-=1;for(let l of n[c]??[])r[l]=(r[l]??0)-1}}return o}function yu(e,t){if(!e.services[t])throw I(Ho,`unknown service "${t}"`)}function tc(e,t,r){let n={},o=(a)=>{if(n[a])return;yu(e,a),n[a]=!0;for(let c of e.services[a]?.dependencies??[])o(zt(c))};for(let a of t)o(a);let i=Xg(e,n),s=[];return i.forEach((a,c)=>{for(let l of a)s.push({name:l,wave:c+1,dependencies:[...e.services[l]?.dependencies??[]]})}),{profile:r,steps:s,waves:i,blockers:[]}}function lo(e,t){let r={};for(let[i,s]of Object.entries(e.services))for(let a of s.dependencies){let c=zt(a);r[c]=[...r[c]??[],i]}let n={},o=(i)=>{if(n[i])return;yu(e,i),n[i]=!0;for(let s of r[i]??[])o(s)};for(let i of t)o(i);return Object.keys(n)}function Qg(e,t){let r=[...Xg(e,t)].reverse().map((o)=>[...o].sort()),n=[];return r.forEach((o,i)=>{for(let s of o)n.push({name:s,wave:i+1,dependencies:[...e.services[s]?.dependencies??[]]})}),{steps:n,waves:r,profile:"",blockers:[]}}function rc(e,t){let r=Object.fromEntries(lo(e,t).map((n)=>[n,!0]));return Qg(e,r)}function nc(e,t){let r={};for(let n of t)yu(e,n),r[n]=!0;return Qg(e,r)}function Zg(e){let t="";if(e.profile!=="")t+=`Profile: ${e.profile}
50
+ `}var wt;var La=I(()=>{ht();wt=[{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 Hd,mkdirSync as Fd,statSync as N_,writeFileSync as H_}from"fs";import{createInterface as j_}from"readline/promises";import{stdin as B_,stdout as U_}from"process";import{basename as Da,dirname as Nd,join as tn,resolve as W_}from"path";function jd(e,t){let r=e===""?process.cwd():e;if(t==="")return{repo:r,cfgPath:tn(r,Nt,lr)};let n=W_(t);if(!(Hd(n)?N_(n).isDirectory():!/\.ya?ml$/i.test(n))){let i=Nd(n);return{repo:Da(i)===Nt?Nd(i):i,cfgPath:n}}if(Da(n)===Nt)return{repo:Nd(n),cfgPath:tn(n,lr)};return{repo:n,cfgPath:tn(n,Nt,lr)}}function Ch(e,t=Da(e),r="",n="",o=!1){let i=tn(e,Nt,lr);if(!o&&Hd(i))throw E(Y,`configuration already exists at ${i}; not overwriting`);return Ia(e,{...wn(e,r),name:t,profile:n}),i}async function Ph(e,t,r="",n=!1){let{repo:o,cfgPath:i}=jd(t,r);if(!n&&Hd(i)){Ht(`Found existing configuration at ${i}; nothing written.`),Ht("Pass --force to overwrite it, or edit it directly.");return}let s=j_({input:B_,output:U_}),a=async(h,v)=>{let b=v!==""?` [${v}]`:"",g=(await s.question(`${h}${b}: `)).trim();return g===""?v:g},c=0,l=()=>(c+=1,c);if(Ht(`devctl setup \u2014 ${wt.length} steps`),Ht(""),r==="")await a(`${l()}. Repository root`,o);let d=wn(o,""),u=await a(`${l()}. ${wt[1]?.prompt}`,Da(o));d.name=u;let f=await e.detectGoogle(""),p=await a(`${l()}. ${wt[2]?.prompt}`,f.projectID);if(d.project=p,Ht(`${l()}. ${wt[3]?.title}`),!f.adcAvailable){if(Ht(" 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(v){Ht(` login failed: ${v instanceof Error?v.message:String(v)}`)}}else if(Ht(" \u2713 Application Default Credentials available"),f.userEmail!=="")Ht(` user: ${f.userEmail}`);d.sa=await a(`${l()}. ${wt[4]?.prompt}`,""),d.audience=await a(`${l()}. ${wt[5]?.prompt}`,"");let m=await a(`${l()}. ${wt[6]?.prompt}`,String(Ni));d.proxyPort=Aa(m),d.profile=await a(`${l()}. ${wt[7]?.prompt}`,""),s.close(),Ia(o,d),Ht(""),Ht(`Wrote starter configuration to ${i}`),Ht(`${l()}. Validation`);try{let h=e.load(o,""),v=await e.runDoctor.execute(h);Ht(e.formatDoctor(v))}catch(h){Ht(` configuration is not valid yet: ${h instanceof Error?h.message:String(h)}`),Ht(" edit .devctl/config.yaml and run `devctl config validate`")}}function Ia(e,t){let r=tn(e,".devctl");Fd(tn(r,"services"),{recursive:!0}),Fd(tn(r,"profiles"),{recursive:!0}),Fd(tn(r,"proxy"),{recursive:!0}),H_(tn(r,"config.yaml"),kh(t))}function Ht(e){process.stdout.write(`${e}
51
+ `)}var Ma=I(()=>{ht();La();te()});function xg(){return/\$\{(?:env\.)?([A-Za-z_][A-Za-z0-9_]*)\}/g}function tu(e){return[...e.matchAll(xg())].map((t)=>t[1]??"")}function wg(e,t){let r=[];return{value:e.replace(xg(),(o,i)=>{let s=t[i];if(s===void 0||s==="")return r.push(i),"";return s}),missing:r}}function Ba(e){let t=e.trim();if(t===""||tu(t).length===0)return;return t}function Sg(e){return/^\$\{(?:env\.)?[A-Za-z_][A-Za-z0-9_]*\}$/.test(e.trim())}function Pn(e){return Object.entries(e.provenance).sort(([t],[r])=>t.localeCompare(r)).flatMap(([t,r])=>{let n=r[r.length-1];if(!n)return[];return[{path:t,value:tE(e,t),...n,shadowed:r.slice(0,-1)}]})}function tE(e,t){let r=e;for(let n of t.split(".")){if(typeof r!=="object"||r===null)return;r=r[n]}return r}function qa(e){if(en(e))return dr({kind:ro,serviceAccount:e.service_account});if(yh(e)&&(e.type!==""||e.mode!==""))return dr({kind:Ka});let t=(e.type||e.mode).toLowerCase();if(t!=="")return dr({kind:t,providerConfig:e.config??{}});return dr({kind:Go})}function no(e){if(e.type.toLowerCase()==="none")return dr({kind:Go});let t=e.identity.type.toLowerCase(),r=e.identity.service_account||e.service_account;if(t==="service"||t==="service_account"||e.type==="service_account")return dr({kind:ro,serviceAccount:r});if(e.type.toLowerCase()==="iap"&&t==="")return dr({kind:Go});if(e.type===""&&t==="")return dr({kind:Go});return dr({kind:Ka})}function Lg(e){return e.kind===Ka||e.kind===ro}function Ko(e){if(e.tokenKey!=="")return e.tokenKey;if(e.kind===ro)return`sa:${e.serviceAccount}`;if(e.email!=="")return`user:${e.email}`;return"user"}function dr(e={}){return{kind:Go,email:"",serviceAccount:"",project:"",projectSource:"",adcAvailable:!1,providerConfig:{},tokenKey:"",...e}}function iu(e){let t=new Map,r=(n,o)=>{if(n!=="")t.set(n,(t.get(n)??!1)||o)};for(let n of Object.values(e.services))if(en(n.identity)&&n.identity.service_account!=="")r(n.identity.service_account,!0);for(let n of e.proxy.routes){let o=n.auth.type.toLowerCase(),i=n.auth.identity.type.toLowerCase(),s=n.auth.identity.service_account||n.auth.service_account;if(i==="service"||i==="service_account"||o==="service_account")r(s,o!=="none")}return[...t.entries()].map(([n,o])=>({email:n,active:o})).sort((n,o)=>n.email.localeCompare(o.email))}function Hr(e){return iu(e).filter((t)=>t.active).map((t)=>t.email)}function Dg(e,t,r){let n=[];for(let o of t){let i=e.services[o];if(!i)continue;let s=qa(i.identity);if(en(i.identity)&&i.identity.service_account===""){n.push({name:o,message:"service account identity is not configured"});continue}if((Lg(s)||i.capabilities.some((c)=>c==="google_api"||c==="iap"||c==="service_identity"))&&!r)n.push({name:o,message:"ADC unavailable"})}return n}function Gi(e){if(Hr(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(Lg(qa(t.identity)))return!0;return t.capabilities.some((r)=>r==="google_api"||r==="iap"||r==="service_identity")})}function su(){return{name:"user",accepts:(e)=>!en(e),resolve:async(e,t)=>{if(e.type===""&&e.mode==="")return dr({kind:Go});return{...await t(),kind:Ka}}}}function au(){return{name:"service_account",accepts:(e)=>en(e),resolve:async(e,t)=>{if(e.service_account==="")throw E(va,"service account identity is not configured");return{...await t(),kind:ro,serviceAccount:e.service_account}}}}async function Ig(e,t,r){if(r)for(let n=r.length-1;n>=0;n-=1){let o=r[n];if(o?.accepts(e))return o.resolve(e,t)}if(en(e))return au().resolve(e,t);return su().resolve(e,t)}var Ka="user",ro="service_account",Go="none";var jr=I(()=>{ht();te()});function er(){return"devctl 0.3.2"}var jt="0.3.2",Yi=1;function Qg(e){return e}function ur(e){return e}function Jo(e){if(e.state===je&&e.health===Bt)return pr;if(e.state===je&&e.health===Oe)return Br;return e.state}function Qi(e){return{name:e,state:Et,health:Tt,pid:0,ports:{},restarts:0,last_error:"",identity:"",startTime:void 0,cpuPercent:void 0,memoryKB:void 0,profile:"",env_source:"daemon",orphaned:!1}}function tv(e,t){let r={},n={};for(let a of Object.keys(t))r[a]=0;for(let a of Object.keys(t)){let c=e.services[a]?.dependencies??[];for(let l of c){let d=zt(l);if(!t[d])continue;n[d]=[...n[d]??[],a],r[a]=(r[a]??0)+1}}let o=[],i=Object.keys(t).length,s={};while(i>0){let a=Object.entries(r).filter(([c,l])=>l===0&&!s[c]).map(([c])=>c).sort();if(a.length===0)throw E(dh,"dependency cycle detected while planning startup");o.push(a);for(let c of a){s[c]=!0,i-=1;for(let l of n[c]??[])r[l]=(r[l]??0)-1}}return o}function xu(e,t){if(!e.services[t])throw E(Ho,`unknown service "${t}"`)}function nc(e,t,r){let n={},o=(a)=>{if(n[a])return;xu(e,a),n[a]=!0;for(let c of e.services[a]?.dependencies??[])o(zt(c))};for(let a of t)o(a);let i=tv(e,n),s=[];return i.forEach((a,c)=>{for(let l of a)s.push({name:l,wave:c+1,dependencies:[...e.services[l]?.dependencies??[]]})}),{profile:r,steps:s,waves:i,blockers:[]}}function co(e,t){let r={};for(let[i,s]of Object.entries(e.services))for(let a of s.dependencies){let c=zt(a);r[c]=[...r[c]??[],i]}let n={},o=(i)=>{if(n[i])return;xu(e,i),n[i]=!0;for(let s of r[i]??[])o(s)};for(let i of t)o(i);return Object.keys(n)}function rv(e,t){let r=[...tv(e,t)].reverse().map((o)=>[...o].sort()),n=[];return r.forEach((o,i)=>{for(let s of o)n.push({name:s,wave:i+1,dependencies:[...e.services[s]?.dependencies??[]]})}),{steps:n,waves:r,profile:"",blockers:[]}}function oc(e,t){let r=Object.fromEntries(co(e,t).map((n)=>[n,!0]));return rv(e,r)}function ic(e,t){let r={};for(let n of t)xu(e,n),r[n]=!0;return rv(e,r)}function nv(e){let t="";if(e.profile!=="")t+=`Profile: ${e.profile}
52
52
 
53
53
  `;t+=`Plan:
54
54
 
@@ -56,8 +56,8 @@ shutdown:
56
56
  `,r+=1;if((e.blockers??[]).length>0){t+=`
57
57
  Blocked:
58
58
  `;for(let n of e.blockers??[])t+=`- ${n.name}: ${n.message}
59
- `}return t}function oc(e){return`${e.join(", ")} changed and only take effect after a full \`devctl down && devctl start\`, not a reload`}function XE(e,t,r){let n={};if(t!==""){let o=e.profiles[t];if(!o)throw I("configuration",`unknown profile "${t}"`);Object.assign(n,o.environment)}if(r.length>0)return{services:Yg(e,r),env:n};if(t==="")return{services:Object.keys(e.services),env:n};return{services:Yg(e,e.profiles[t]?.services??[]),env:n}}function QE(e){let t=Object.keys(e.profiles).sort();return ur(t[0]??"")}function uo(e,t){if(t==="")return{};return{...e.profiles[ur(t)]?.environment??{}}}function ic(e,t){let r=t.services??[],n=t.profile??ur("");if(r.length===0&&n===""){if(n=t.activeProfile||QE(e),n==="")throw I(Q,"no profile or services given; pass service names or add a profile")}let o=XE(e,n,r);return{services:o.services,env:o.env,profile:n}}function Yg(e,t){let r={},n=[];for(let o of t){if(r[o])continue;if(!e.services[o])throw I("service_not_found",`unknown service "${o}"`);r[o]=!0,n.push(o)}return n}var Jg="UNKNOWN",on="STARTING",je="RUNNING",pr="HEALTHY",Br="UNHEALTHY",Rr="STOPPING",_t="STOPPED",dt="FAILED",fr="RESTARTING",Et="UNKNOWN",Bt="HEALTHY",Oe="UNHEALTHY";var Tt=D(()=>{ht();te()});function Rv(e){if(e.newer)return`update available ${e.current} \u2192 ${e.latest} ${e.hint}`;if(e.latest!=="")return`${e.current} up to date`;return`${e.current} (latest unavailable)`}function _v(e){let t=Cv(e.scriptPath),r=Cv(e.execPath);if(!e.standalone&&t.includes("/@amr-m-abdelgawad/devctl/")){if(t.includes("/_npx/"))return{kind:"npx",hint:"npx @amr-m-abdelgawad/devctl@latest"};let n=_T();return{kind:"npm",command:n,hint:n.join(" ")}}if(e.standalone&&r.toLowerCase().includes("/cellar/devctl/"))return{kind:"homebrew",command:["brew","reinstall","--formula","https://raw.githubusercontent.com/amr-m-abdelgawad/devctl/main/homebrew/devctl.rb"],hint:"brew reinstall --formula https://raw.githubusercontent.com/amr-m-abdelgawad/devctl/main/homebrew/devctl.rb"};if(e.standalone)return{kind:"release",hint:"re-download the matching GitHub Release binary \u2014 see docs/installation.md"};if(t.endsWith("/src/bin.ts"))return{kind:"source",hint:"git pull && cd app && bun install"};return{kind:"unknown",hint:`npm install --global ${"@amr-m-abdelgawad/devctl"}@latest # alternatives: Homebrew or the unsigned GitHub Release binaries \u2014 see docs/installation.md`}}function Ev(e,t){let r=Pv(e),n=Pv(t);for(let o=0;o<3;o+=1){let i=(r[o]??0)-(n[o]??0);if(i!==0)return i>0?1:-1}return 0}function _T(){return[process.platform==="win32"?"npm.cmd":"npm","install","--global","@amr-m-abdelgawad/devctl@latest"]}function Cv(e){return e.replaceAll("\\","/")}function Pv(e){return e.replace(/^v/,"").split(".").slice(0,3).map((t)=>{let r=Number.parseInt(t.replace(/[^0-9].*$/,""),10);return Number.isFinite(r)?r:0})}var pc="restart the daemon with `devctl down` then start again";function _n(e){if(!Number.isInteger(e))return Ju;return Math.min(pA,Math.max(fA,e))}function Py(e,t){if(t.length!==1||t<"0"||t>"9")return e;if(e.length>=mA)return t;return`${e}${t}`}function Ry(e){return e.slice(0,-1)}function _y(e,t){if(e==="")return _n(t);let r=Number(e);if(!Number.isInteger(r))return _n(t);return _n(r)}function mo(e){let t=Number.parseInt(ar(e).slice(0,uA),16),r=Number.isFinite(t)?t%Cy:0;return Ju+r}function Ey(e,t){return t===mo(e)}var Ju=18700,Cy=600,Uj,uA=8,pA=65535,fA=1024,mA=5;var Xu=D(()=>{Ys();Uj=Ju+Cy-1});class mr{nameMarkers;patterns;constructor(e,t){this.nameMarkers=[...$y,...e],this.patterns=t.flatMap((r)=>{try{return[new RegExp(r)]}catch{return[]}})}update(e,t){this.nameMarkers=[...$y,...e],this.patterns=t.flatMap((r)=>{try{return[new RegExp(r)]}catch{return[]}})}isSecretName(e){let t=e.toUpperCase();return this.nameMarkers.some((r)=>t.includes(r.toUpperCase()))}redactValue(e,t){if(t==="")return t;if(this.isSecretName(e))return"********";if(this.patterns.some((r)=>r.test(t)))return"********";return t}redactMap(e){let t={};for(let[r,n]of Object.entries(e))t[r]=this.redactValue(r,n);return t}redactText(e){if(e==="")return e;let t=e;for(let r of this.patterns)t=t.replace(r,"********");return PA(t)}}function PA(e){let t=e.toLowerCase(),r="authorization: bearer ",n=t.indexOf("authorization: bearer ");if(n>=0)return Fy(e,n+22);let o=t.indexOf("bearer ");if(o<0)return e;return Fy(e,o+7)}function Fy(e,t){let r=t;while(r<e.length&&!RA(e[r]??""))r+=1;return e.slice(0,t)+"********"+e.slice(r)}function RA(e){return e===" "||e==="\t"||e===`
60
- `||e==="\r"}var $y;var ni=D(()=>{$y=["PASSWORD","SECRET","TOKEN","PRIVATE_KEY","CLIENT_SECRET","API_KEY","CREDENTIAL","ACCESS_KEY","AUTH_KEY"]});var Tn;var op=D(()=>{Tn={procedure:`---
59
+ `}return t}function sc(e){return`${e.join(", ")} changed and only take effect after a full \`devctl down && devctl start\`, not a reload`}function rT(e,t,r){let n={};if(t!==""){let o=e.profiles[t];if(!o)throw E("configuration",`unknown profile "${t}"`);Object.assign(n,o.environment)}if(r.length>0)return{services:Zg(e,r),env:n};if(t==="")return{services:Object.keys(e.services),env:n};return{services:Zg(e,e.profiles[t]?.services??[]),env:n}}function nT(e){let t=Object.keys(e.profiles).sort();return ur(t[0]??"")}function lo(e,t){if(t==="")return{};return{...e.profiles[ur(t)]?.environment??{}}}function ac(e,t){let r=t.services??[],n=t.profile??ur("");if(r.length===0&&n===""){if(n=t.activeProfile||nT(e),n==="")throw E(Y,"no profile or services given; pass service names or add a profile")}let o=rT(e,n,r);return{services:o.services,env:o.env,profile:n}}function Zg(e,t){let r={},n=[];for(let o of t){if(r[o])continue;if(!e.services[o])throw E("service_not_found",`unknown service "${o}"`);r[o]=!0,n.push(o)}return n}var ev="UNKNOWN",on="STARTING",je="RUNNING",pr="HEALTHY",Br="UNHEALTHY",_r="STOPPING",Et="STOPPED",dt="FAILED",fr="RESTARTING",Tt="UNKNOWN",Bt="HEALTHY",Oe="UNHEALTHY";var At=I(()=>{ht();te()});function Av(e){if(e.newer)return`update available ${e.current} \u2192 ${e.latest} ${e.hint}`;if(e.latest!=="")return`${e.current} up to date`;return`${e.current} (latest unavailable)`}function Lv(e){let t=Ev(e.scriptPath),r=Ev(e.execPath);if(!e.standalone&&t.includes("/@amr-m-abdelgawad/devctl/")){if(t.includes("/_npx/"))return{kind:"npx",hint:"npx @amr-m-abdelgawad/devctl@latest"};let n=DT();return{kind:"npm",command:n,hint:n.join(" ")}}if(e.standalone&&r.toLowerCase().includes("/cellar/devctl/"))return{kind:"homebrew",command:["brew","reinstall","--formula","https://raw.githubusercontent.com/amr-m-abdelgawad/devctl/main/homebrew/devctl.rb"],hint:"brew reinstall --formula https://raw.githubusercontent.com/amr-m-abdelgawad/devctl/main/homebrew/devctl.rb"};if(e.standalone)return{kind:"release",hint:"re-download the matching GitHub Release binary \u2014 see docs/installation.md"};if(t.endsWith("/src/bin.ts"))return{kind:"source",hint:"git pull && cd app && bun install"};return{kind:"unknown",hint:`npm install --global ${"@amr-m-abdelgawad/devctl"}@latest # alternatives: Homebrew or the unsigned GitHub Release binaries \u2014 see docs/installation.md`}}function Dv(e,t){let r=Tv(e),n=Tv(t);for(let o=0;o<3;o+=1){let i=(r[o]??0)-(n[o]??0);if(i!==0)return i>0?1:-1}return 0}function DT(){return[process.platform==="win32"?"npm.cmd":"npm","install","--global","@amr-m-abdelgawad/devctl@latest"]}function Ev(e){return e.replaceAll("\\","/")}function Tv(e){return e.replace(/^v/,"").split(".").slice(0,3).map((t)=>{let r=Number.parseInt(t.replace(/[^0-9].*$/,""),10);return Number.isFinite(r)?r:0})}var mc="restart the daemon with `devctl down` then start again";function Rn(e){if(!Number.isInteger(e))return Qu;return Math.min(yA,Math.max(bA,e))}function Ty(e,t){if(t.length!==1||t<"0"||t>"9")return e;if(e.length>=xA)return t;return`${e}${t}`}function Ay(e){return e.slice(0,-1)}function Ly(e,t){if(e==="")return Rn(t);let r=Number(e);if(!Number.isInteger(r))return Rn(t);return Rn(r)}function fo(e){let t=Number.parseInt(ar(e).slice(0,vA),16),r=Number.isFinite(t)?t%Ey:0;return Qu+r}function Dy(e,t){return t===fo(e)}var Qu=18700,Ey=600,Yj,vA=8,yA=65535,bA=1024,xA=5;var Zu=I(()=>{Xs();Yj=Qu+Ey-1});class mr{nameMarkers;patterns;constructor(e,t){this.nameMarkers=[...jy,...e],this.patterns=t.flatMap((r)=>{try{return[new RegExp(r)]}catch{return[]}})}update(e,t){this.nameMarkers=[...jy,...e],this.patterns=t.flatMap((r)=>{try{return[new RegExp(r)]}catch{return[]}})}isSecretName(e){let t=e.toUpperCase();return this.nameMarkers.some((r)=>t.includes(r.toUpperCase()))}redactValue(e,t){if(t==="")return t;if(this.isSecretName(e))return"********";if(this.patterns.some((r)=>r.test(t)))return"********";return t}redactMap(e){let t={};for(let[r,n]of Object.entries(e))t[r]=this.redactValue(r,n);return t}redactText(e){if(e==="")return e;let t=e;for(let r of this.patterns)t=t.replace(r,"********");return LA(t)}}function LA(e){let t=e.toLowerCase(),r="authorization: bearer ",n=t.indexOf("authorization: bearer ");if(n>=0)return By(e,n+22);let o=t.indexOf("bearer ");if(o<0)return e;return By(e,o+7)}function By(e,t){let r=t;while(r<e.length&&!DA(e[r]??""))r+=1;return e.slice(0,t)+"********"+e.slice(r)}function DA(e){return e===" "||e==="\t"||e===`
60
+ `||e==="\r"}var jy;var ni=I(()=>{jy=["PASSWORD","SECRET","TOKEN","PRIVATE_KEY","CLIENT_SECRET","API_KEY","CREDENTIAL","ACCESS_KEY","AUTH_KEY"]});var Tn;var sp=I(()=>{Tn={procedure:`---
61
61
  name: devctl-onboard
62
62
  description: Survey a repository \u2014 its runnable services, docker-compose, Terraform, Kubernetes manifests, .env files, task runners \u2014 and author a validated .devctl configuration for it. Use when asked to onboard a repo to devctl, add or fix .devctl/config.yaml, generate devctl services/profiles/proxy routes, or diagnose why a devctl config fails to load.
63
63
  ---
@@ -367,7 +367,7 @@ the agent to set it up" a complete path. In that state \`get_status\` reports
367
367
  arguments reports the missing configuration rather than a parse error. Draft,
368
368
  validate the candidate text, write the files, then call \`reload_config\`: setup
369
369
  mode clears on the reload that finds a valid configuration.
370
- `,authoring:"# Authoring rules the loader enforces\n\nThe JSON Schema (`schema/devctl.config.schema.json`) describes the shape.\nThis file describes what the **loader and validator actually reject** \u2014 the\nrules that turn a schema-plausible config into one that fails at\n`devctl config validate`. Check anything you are unsure of here before writing.\n\n---\n\n## Unknown fields are rejected everywhere\n\nNot warned about \u2014 rejected, with the offending path named. These are the\ncomplete allowlists.\n\n**Top level:** `version` `project` `google` `profiles` `templates` `services`\n`tasks` `proxy` `logs` `auth` `shutdown` `ui` `secrets` `doctor` `plugins`\n`environment`\n\n**Service** (and `templates.<name>`, same shape): `extends` `description`\n`command` `shell` `working_dir` `dependencies` `ports` `environment` `health`\n`identity` `logs` `restart` `startup` `capabilities` `proxy` `expose` `container` `watch` `hooks`\n\n| Section | Allowed keys |\n|---|---|\n| `project` | `name` |\n| `google` | `project_id` `region` |\n| `profiles.<name>` | `services` `environment` |\n| `service.health` | `type` `url` `address` `command` `interval_seconds` `timeout_seconds` `start_period_seconds` `unhealthy_threshold` `healthy_reset_threshold` |\n| `service.hooks` | `pre_start` `post_start` |\n| `service.container` | `image` `runtime` `ports` `env` `volumes` |\n| `service.watch` | `enabled` `paths` `debounce_ms` `ignore` |\n| `tasks.<name>` | `command` `shell` `working_dir` `dependencies` `environment` |\n| `service.identity` | `type` `mode` `service_account` |\n| `service.restart` | `enabled` `policy` `max_retries` `backoff_seconds` |\n| `service.startup` | `wait_for_healthy` `timeout_seconds` |\n| `service.logs` | `stdout` `stderr` |\n| `service.environment` | `required` `defaults` + arbitrary `KEY: value` pairs |\n| `service.expose` | `enabled` `host` `port` (or the `true` shorthand) |\n| `proxy` | `enabled` `gateway` `listen` `token_endpoint` `routes` |\n| `proxy.listen` | `host` `port` |\n| `proxy.token_endpoint` | `enabled` `host` `port` |\n| `route` | `name` `match` `upstream` `auth` |\n| `route.match` | `host` `path` |\n| `route.upstream` | `url` `service` `port` |\n| `route.auth` | `type` `identity` `audience` `service_account` `client_id` `client_secret` |\n| `logs` | `max_memory_events` `persistence` |\n| `logs.persistence` | `enabled` `directory` `retention_days` `max_session_logs` |\n| `auth` | `refresh_threshold_seconds` |\n| `shutdown` | `stop_services_on_exit` `grace_seconds` |\n| `ui` | `theme` `keymap` |\n| `secrets` | `extra_markers` `extra_patterns` |\n| `doctor` | `tools` (each `{ name, command }`) |\n| `plugins[]` | `path` |\n| `environment` | `sources` `secrets` |\n\nThere is no `depends_on`, `build`, `replicas`, or `env_file`. Container fields\nbelong under `service.container`, not directly on the service.\n\n---\n\n## Required, at minimum\n\n- `version: 1`. Anything else: *unsupported config version N; no migration is\n available.*\n- **At least one service.** An empty or missing `services` map fails with *at\n least one service must be defined* \u2014 so a proxy-only or profiles-only config\n is not valid.\n- Every host service needs a non-empty `command`; a container service may rely on its image command.\n\n---\n\n## Ports\n\n- **Globally unique across all services.** Two services on `8000` gives\n *duplicate port 8000 used by api and worker*. This is the single most common\n failure when onboarding a repo whose services each assumed they owned the\n default port.\n- Range 1\u201365535.\n- Forms accepted: a named map (`ports: { http: 8000 }`, preferred), a list, a\n bare number, or `auto`. `auto` is exempt from the uniqueness check \u2014\n devctl allocates at start.\n- Name the port `http` when it serves HTTP.\n- Prefer the explicit `${services.X.ports.http}` reference form. The short\n `${services.X.port}` form is only unambiguous for a single-port service: once\n the service is running it prefers the assigned port named `http`, but when it\n resolves against the configuration it takes the **first port declared**,\n whatever its name. Two forms of the same reference disagreeing on a\n multi-port service is a genuinely confusing bug to chase.\n\n## References\n\n`${services.<name>.ports.<portname>}` and `${services.<name>.port}` are the\nsupported forms. Anything else \u2014 `${env.FOO}`, `${project.name}` \u2014 throws.\n\n- The referenced service must exist and the named port must be defined, or\n validation fails with *unresolvable reference*.\n- References resolve inside service `environment` values, `defaults`, profile\n environments and dotenv values, before the process starts.\n- Use them for every cross-service URL. Hard-coded ports silently break when a\n port changes or is switched to `auto`.\n\n## Dependencies\n\n- Must name a service that exists.\n- A service may not depend on itself.\n- Cycles are rejected: *dependency cycle: a \u2192 b \u2192 a*.\n- Direction: `dependencies` means \"start these first\". `start x` walks **up**\n and starts x's dependencies; `stop x` walks **down** and stops x's\n *dependents*, never its dependencies.\n- A string dependency uses `service_started`. Use `{ service: db, condition:\n service_healthy }` to wait for the dependency's configured health check.\n\n## Commands and `shell`\n\nA command whose argv contains any of `|` `||` `&&` `;` `>` `>>` `<` `&` is\nrejected unless `shell: true`:\n\n```yaml\n# rejected\ncommand: \"npm run build && npm start\"\n\n# correct\ncommand: \"npm run build && npm start\"\nshell: true\n\n# better \u2014 no shell needed\ncommand: [npm, start]\n```\n\nPrefer argv lists. A string command is split on whitespace, which mangles\narguments containing spaces.\n\n## `working_dir`\n\nRelative to the **repository root** \u2014 the directory containing `.devctl` \u2014 not\nthe process cwd, and not the `.devctl` directory itself.\n\n---\n\n## Health\n\n| `type` | Also requires |\n|---|---|\n| `http` | `url` \u2014 omit it and you get *health.url is required for http health checks* |\n| `tcp` | `address`, **or** at least one port defined on the service |\n| `command` | `health.command` (non-empty) |\n| `process` or omitted | nothing; only checks the pid is alive |\n\nAny other `type` is rejected \u2014 *health.type must be http, tcp, process, or\ncommand* \u2014 **unless** `plugins` is non-empty. That exemption is not approval:\nplugins load after validation, so the supervisor re-checks the type at boot and\nat reload. A custom type no plugin provides fails as a *reload rejection*, not\na config error, which is much harder to diagnose. Only use a custom type when\nyou can point at the plugin that registers it.\n\nDefaults when omitted: interval 2s, timeout 2s, start period 0s, unhealthy\nthreshold 3, healthy reset threshold 10. Failures during the start period do\nnot mark the service unhealthy or consume restart budget.\n\n## Identity\n\n- `type: user` (or omitted) needs nothing else.\n- `type: service_account` (or `service`) **requires** `service_account`, and it\n must contain `@` \u2014 *service_account must be an email*.\n- `mode` is an accepted alias of `type`.\n- Any other type behaves like a custom health type: allowed only with\n `plugins` set, re-checked at boot.\n\nNever fabricate an SA email. If Terraform interpolates it beyond what you can\nresolve, leave a clearly-marked placeholder and tell the user.\n\n## Restart\n\n`policy` must be `never`, `on_failure`, or `always`. `enabled: true` with no\npolicy behaves as `on_failure`.\n\n`max_retries` is a real budget: it resets on a manual stop/start/restart and\nafter a sustained healthy run, but an automatic health-triggered restart\npreserves it across its own cycle.\n\n## Capabilities\n\nDocumentation for doctor, not behaviour. Only these are accepted:\n`google` `google_api` `iap` `network` `service_identity` `local_http`.\nAnything else is rejected.\n\n---\n\n## Proxy\n\n- `proxy.listen.port` is **required** when `proxy.enabled: true`.\n- `proxy.listen.host` must be loopback (`127.0.0.1`, `localhost`, `::1`, or `127.0.0.0/8`). `0.0.0.0` and `::` are rejected.\n- `proxy.token_endpoint.host` must be loopback; `0.0.0.0` and `::` are rejected.\n- Every route needs a `name` and either `upstream.url` or `upstream.service`.\n A service reference must name a real service and an existing port (default\n port name is `http`). `service.expose` and `proxy.gateway` synthesize\n service-reference routes; they do nothing unless `proxy.enabled` is true.\n A hand-written route of the same name wins.\n- **Route names must be unique** \u2014 including names generated from per-service\n `proxy` fragments.\n- `auth.type: iap` requires **both** `audience` and `auth.identity.type`. A\n missing identity type on an IAP route is a configuration error, not a\n default.\n- Optional `client_id` plus `client_secret` mints the user IAP ID token with\n that OAuth client instead of ADC's default client. Put `${NAME}` or\n `${env.NAME}` in `client_secret` so the value is read from the environment\n at mint time (this is not service-env interpolation). These fields are\n invalid on non-IAP routes and on IAP routes whose identity is a service\n account.\n- An identity of `service` / `service_account` requires an SA email, from\n either `auth.identity.service_account` or the route's `auth.service_account`.\n- `auth.type: none` means no auth at all \u2014 any identity left on such a route is\n ignored entirely, and will not be probed at start or by doctor.\n\n### Per-service route fragments\n\nA service's `proxy` key is one route or a list. At load they append into the\n**same** global `proxy.routes` list, named `<service>` or `<service>-<n>`:\n\n```yaml\nservices:\n api:\n command: [python3, main.py]\n proxy:\n - match: { path: /api }\n upstream: { url: \"http://127.0.0.1:8000\" }\n```\n\nThat generates a route named `api`. So a service named `api` **and** a global\nroute named `api` collide \u2014 *duplicate route name api*. Watch for this when\nmixing both styles.\n\nMatching is host + optional path prefix. There is no path rewriting or\nstripping: the matched path is forwarded to the upstream as-is.\n\n---\n\n## Modular layout\n\nWhen the main file is `.devctl/config.yaml`, these merge in automatically:\n\n```\n.devctl/services/<name>.yaml \u2192 services.<name> (FILENAME is the key)\n.devctl/profiles/<name>.yaml \u2192 profiles.<name>\n.devctl/proxy/routes.yaml \u2192 proxy.routes (see the wrapper below)\n```\n\nA service or profile file contains that object's body **only** \u2014 no `services:`\nwrapper, no `version:`. Getting this wrong reads oddly: a `services:` wrapper\ninside `.devctl/services/api.yaml` reports\n`unknown fields: services.api.services`.\n\n`proxy/routes.yaml` is the exception: it accepts either a full `proxy:` wrapper\nor a bare top-level `routes:` list. Both append to the same global route list.\n\n```yaml\n# .devctl/proxy/routes.yaml \u2014 wrapper form, also lets you set proxy.listen here\nproxy:\n routes:\n - name: invoices-api\n match: { host: invoices-api.local }\n upstream: { url: \"http://127.0.0.1:18000\" }\n```\n\n```yaml\n# .devctl/proxy/routes.yaml \u2014 bare form, equivalent for routes alone\nroutes:\n - name: invoices-api\n match: { host: invoices-api.local }\n upstream: { url: \"http://127.0.0.1:18000\" }\n```\n\nPick one. Both keys in the same file append **both** lists, which trips the\nduplicate-route-name check.\n\nOverlays merge after the repo config, later wins:\nbuilt-in defaults \u2192 repo `.devctl` \u2192 `~/.devctl/config.local.yaml` \u2192\n`.devctl/config.local.yaml` \u2014 the repo-local overlay wins over the home one.\nOverlays are presence-aware:\n`false`, `0` and\nempty collections in an overlay do override, so `proxy.enabled: false` in a\nlocal overlay genuinely turns the proxy off.\n\n---\n\n## Environment\n\nMerge order \u2014 later sources win:\n\n```\nprocess \u2192 profile \u2192 dotenv \u2192 generated \u2192 keychain \u2192 secret_manager \u2192 defaults \u2192 vars \u2192 runtime\n```\n\n`process`, `defaults`, `vars` and `runtime` always run. Listing\n`environment.sources` **adds** optional sources (`profile`, `dotenv`,\n`generated`, `keychain`, `secret_manager`) to that always-on set \u2014 it does not\nreplace it, and it does not reorder anything.\n\n- `dotenv` reads repo root then `working_dir`: `.env`, `.env.development`,\n `.env.local`, `.env.<profile>`.\n- `keychain` and `secret_manager` throw when listed and the fetch fails, so\n only list them when the repo genuinely uses them.\n- `environment.required` on a service fails the start if those keys are still\n empty after the whole merge \u2014 the right place to encode \"this cannot run\n without X\".\n\nRuntime values devctl injects: `SERVICE_PORT`, `SERVICE_HOST`,\n`DEVCTL_PROXY_URL`, `DEVCTL_SERVICE_NAME`, `DEVCTL_ENVIRONMENT`,\n`DEVCTL_TOKEN_URL`, `DEVCTL_INTERNAL_TOKEN`. Do not define these yourself.\n\n---\n\n## Templates\n\n```yaml\ntemplates:\n python-http:\n health: { type: http, interval_seconds: 2, timeout_seconds: 1 }\n logs: { stdout: true, stderr: true }\n restart: { policy: on_failure, max_retries: 2, backoff_seconds: 1 }\n\nservices:\n api:\n extends: python-http\n command: [python3, main.py]\n health: { url: \"http://127.0.0.1:8000/health\" } # merged field-by-field\n```\n\n- `extends` must name a template that exists.\n- Nested sections merge **field by field**, so a service can override one\n health field without restating the rest.\n- A template is not a service and is never started.\n\n---\n\n## Reading validation output\n\nEvery message names its path. Fix the path it names.\n\n| Message | Meaning |\n|---|---|\n| `at least one service must be defined` | empty/missing `services` |\n| `duplicate port N used by X and Y` | two services pinned the same port |\n| `services.X.command is required` | missing or empty command |\n| `services.X.command contains shell metacharacters` | add `shell: true` or use argv |\n| `services.X.dependencies: unknown service \"Y\"` | typo, or Y lives in a file you did not create |\n| `dependency cycle: a \u2192 b \u2192 a` | remove an edge |\n| `services.X.health.url is required for http health checks` | add `url`, or change the type |\n| `services.X.identity.service_account must be an email` | placeholder left unresolved |\n| `services.X.environment.K: unresolvable reference ${\u2026}` | referenced service or port name does not exist |\n| `services.X.capabilities: unknown capability \"c\"` | only the six listed above are accepted |\n| `profiles.P references unknown service \"S\"` | profile lists a service that is not defined |\n| `proxy.routes[i]: duplicate route name N` | often a per-service fragment colliding with a global route |\n| `proxy.routes[i].auth.audience is required when auth.type is iap` | IAP needs both audience and identity.type |\n| `proxy.routes[i].auth.client_id is only valid when auth.type is iap` | `client_id` / secret only apply to IAP user routes |\n| `proxy.routes[i].auth.client_id is required when client_secret is set` | secret without client_id |\n| `proxy.routes[i].auth.client_secret is required when client_id is set` | client_id needs a secret |\n| `proxy.routes[i].auth.client_id is only valid with identity.type user` | SA IAP uses generateIdToken, not a user OAuth client |\n| `proxy.listen.port is required when proxy.enabled is true` | pin a port |\n| `unsupported config version N (expected 1)` | `version:` must be `1` |\n| `unknown fields: services.a.depends_on` | not in the allowlists at the top of this file \u2014 usually a compose or k8s spelling |\n\nEvery message above is the validator's exact wording. `devctl config validate`\nprints one per line and exits 2; a clean run prints `configuration is valid`\nand exits 0.\n",discovery:`# Discovery: repository signal \u2192 devctl service
370
+ `,authoring:"# Authoring rules the loader enforces\n\nThe JSON Schema (`schema/devctl.config.schema.json`) describes the shape.\nThis file describes what the **loader and validator actually reject** \u2014 the\nrules that turn a schema-plausible config into one that fails at\n`devctl config validate`. Check anything you are unsure of here before writing.\n\n---\n\n## Unknown fields are rejected everywhere\n\nNot warned about \u2014 rejected, with the offending path named. These are the\ncomplete allowlists.\n\n**Top level:** `version` `project` `google` `profiles` `templates` `services`\n`tasks` `proxy` `logs` `auth` `shutdown` `ui` `secrets` `doctor` `plugins`\n`environment`\n\n**Service** (and `templates.<name>`, same shape): `extends` `description`\n`command` `shell` `working_dir` `dependencies` `ports` `environment` `health`\n`identity` `logs` `restart` `startup` `capabilities` `proxy` `expose` `container` `watch` `hooks`\n\n| Section | Allowed keys |\n|---|---|\n| `project` | `name` |\n| `google` | `project_id` `region` |\n| `profiles.<name>` | `services` `environment` |\n| `service.health` | `type` `url` `address` `command` `interval_seconds` `timeout_seconds` `start_period_seconds` `unhealthy_threshold` `healthy_reset_threshold` |\n| `service.hooks` | `pre_start` `post_start` |\n| `service.container` | `image` `runtime` `ports` `env` `volumes` |\n| `service.watch` | `enabled` `paths` `debounce_ms` `ignore` |\n| `tasks.<name>` | `command` `shell` `working_dir` `dependencies` `environment` |\n| `service.identity` | `type` `mode` `service_account` |\n| `service.restart` | `enabled` `policy` `max_retries` `backoff_seconds` |\n| `service.startup` | `wait_for_healthy` `timeout_seconds` |\n| `service.logs` | `stdout` `stderr` |\n| `service.environment` | `required` `defaults` + arbitrary `KEY: value` pairs |\n| `service.expose` | `enabled` `host` `port` (or the `true` shorthand) |\n| `proxy` | `enabled` `gateway` `credentials` `listen` `token_endpoint` `routes` |\n| `proxy.listen` | `host` `port` |\n| `proxy.token_endpoint` | `enabled` `host` `port` |\n| `route` | `name` `match` `upstream` `auth` |\n| `route.match` | `host` `path` |\n| `route.upstream` | `url` `service` `port` |\n| `route.auth` | `type` `identity` `audience` `service_account` `client_id` `client_secret` `credentials` |\n| `logs` | `max_memory_events` `persistence` |\n| `logs.persistence` | `enabled` `directory` `retention_days` `max_session_logs` |\n| `auth` | `refresh_threshold_seconds` |\n| `shutdown` | `stop_services_on_exit` `grace_seconds` |\n| `ui` | `theme` `keymap` |\n| `secrets` | `extra_markers` `extra_patterns` |\n| `doctor` | `tools` (each `{ name, command }`) |\n| `plugins[]` | `path` |\n| `environment` | `sources` `secrets` |\n\nThere is no `depends_on`, `build`, `replicas`, or `env_file`. Container fields\nbelong under `service.container`, not directly on the service.\n\n---\n\n## Required, at minimum\n\n- `version: 1`. Anything else: *unsupported config version N; no migration is\n available.*\n- **At least one service.** An empty or missing `services` map fails with *at\n least one service must be defined* \u2014 so a proxy-only or profiles-only config\n is not valid.\n- Every host service needs a non-empty `command`; a container service may rely on its image command.\n\n---\n\n## Ports\n\n- **Globally unique across all services.** Two services on `8000` gives\n *duplicate port 8000 used by api and worker*. This is the single most common\n failure when onboarding a repo whose services each assumed they owned the\n default port.\n- Range 1\u201365535.\n- Forms accepted: a named map (`ports: { http: 8000 }`, preferred), a list, a\n bare number, or `auto`. `auto` is exempt from the uniqueness check \u2014\n devctl allocates at start.\n- Name the port `http` when it serves HTTP.\n- Prefer the explicit `${services.X.ports.http}` reference form. The short\n `${services.X.port}` form is only unambiguous for a single-port service: once\n the service is running it prefers the assigned port named `http`, but when it\n resolves against the configuration it takes the **first port declared**,\n whatever its name. Two forms of the same reference disagreeing on a\n multi-port service is a genuinely confusing bug to chase.\n\n## References\n\n`${services.<name>.ports.<portname>}` and `${services.<name>.port}` are the\nsupported forms. Anything else \u2014 `${env.FOO}`, `${project.name}` \u2014 throws.\n\n- The referenced service must exist and the named port must be defined, or\n validation fails with *unresolvable reference*.\n- References resolve inside service `environment` values, `defaults`, profile\n environments and dotenv values, before the process starts.\n- Use them for every cross-service URL. Hard-coded ports silently break when a\n port changes or is switched to `auto`.\n\n## Dependencies\n\n- Must name a service that exists.\n- A service may not depend on itself.\n- Cycles are rejected: *dependency cycle: a \u2192 b \u2192 a*.\n- Direction: `dependencies` means \"start these first\". `start x` walks **up**\n and starts x's dependencies; `stop x` walks **down** and stops x's\n *dependents*, never its dependencies.\n- A string dependency uses `service_started`. Use `{ service: db, condition:\n service_healthy }` to wait for the dependency's configured health check.\n\n## Commands and `shell`\n\nA command whose argv contains any of `|` `||` `&&` `;` `>` `>>` `<` `&` is\nrejected unless `shell: true`:\n\n```yaml\n# rejected\ncommand: \"npm run build && npm start\"\n\n# correct\ncommand: \"npm run build && npm start\"\nshell: true\n\n# better \u2014 no shell needed\ncommand: [npm, start]\n```\n\nPrefer argv lists. A string command is split on whitespace, which mangles\narguments containing spaces.\n\n## `working_dir`\n\nRelative to the **repository root** \u2014 the directory containing `.devctl` \u2014 not\nthe process cwd, and not the `.devctl` directory itself.\n\n---\n\n## Health\n\n| `type` | Also requires |\n|---|---|\n| `http` | `url` \u2014 omit it and you get *health.url is required for http health checks* |\n| `tcp` | `address`, **or** at least one port defined on the service |\n| `command` | `health.command` (non-empty) |\n| `process` or omitted | nothing; only checks the pid is alive |\n\nAny other `type` is rejected \u2014 *health.type must be http, tcp, process, or\ncommand* \u2014 **unless** `plugins` is non-empty. That exemption is not approval:\nplugins load after validation, so the supervisor re-checks the type at boot and\nat reload. A custom type no plugin provides fails as a *reload rejection*, not\na config error, which is much harder to diagnose. Only use a custom type when\nyou can point at the plugin that registers it.\n\nDefaults when omitted: interval 2s, timeout 2s, start period 0s, unhealthy\nthreshold 3, healthy reset threshold 10. Failures during the start period do\nnot mark the service unhealthy or consume restart budget.\n\n## Identity\n\n- `type: user` (or omitted) needs nothing else.\n- `type: service_account` (or `service`) **requires** `service_account`, and it\n must contain `@` \u2014 *service_account must be an email*.\n- `mode` is an accepted alias of `type`.\n- Any other type behaves like a custom health type: allowed only with\n `plugins` set, re-checked at boot.\n\nNever fabricate an SA email. If Terraform interpolates it beyond what you can\nresolve, leave a clearly-marked placeholder and tell the user.\n\n## Restart\n\n`policy` must be `never`, `on_failure`, or `always`. `enabled: true` with no\npolicy behaves as `on_failure`.\n\n`max_retries` is a real budget: it resets on a manual stop/start/restart and\nafter a sustained healthy run, but an automatic health-triggered restart\npreserves it across its own cycle.\n\n## Capabilities\n\nDocumentation for doctor, not behaviour. Only these are accepted:\n`google` `google_api` `iap` `network` `service_identity` `local_http`.\nAnything else is rejected.\n\n---\n\n## Proxy\n\n- `proxy.listen.port` is **required** when `proxy.enabled: true`.\n- `proxy.listen.host` must be loopback (`127.0.0.1`, `localhost`, `::1`, or `127.0.0.0/8`). `0.0.0.0` and `::` are rejected.\n- `proxy.token_endpoint.host` must be loopback; `0.0.0.0` and `::` are rejected.\n- Every route needs a `name` and either `upstream.url` or `upstream.service`.\n A service reference must name a real service and an existing port (default\n port name is `http`). `service.expose` and `proxy.gateway` synthesize\n service-reference routes; they do nothing unless `proxy.enabled` is true.\n A hand-written route of the same name wins.\n- **Route names must be unique** \u2014 including names generated from per-service\n `proxy` fragments.\n- `auth.type: iap` requires **both** `audience` and `auth.identity.type`. A\n missing identity type on an IAP route is a configuration error, not a\n default.\n- Optional `client_id` plus `client_secret` mints the user IAP ID token with\n that OAuth client instead of ADC's default client. Put `${NAME}` or\n `${env.NAME}` in `client_secret` so the value is read from the environment\n at mint time (this is not service-env interpolation). These fields are\n invalid on non-IAP routes and on IAP routes whose identity is a service\n account.\n- An identity of `service` / `service_account` requires an SA email, from\n either `auth.identity.service_account` or the route's `auth.service_account`.\n- `auth.type: none` means no auth at all \u2014 any identity left on such a route is\n ignored entirely, and will not be probed at start or by doctor.\n\n### Per-service route fragments\n\nA service's `proxy` key is one route or a list. At load they append into the\n**same** global `proxy.routes` list, named `<service>` or `<service>-<n>`:\n\n```yaml\nservices:\n api:\n command: [python3, main.py]\n proxy:\n - match: { path: /api }\n upstream: { url: \"http://127.0.0.1:8000\" }\n```\n\nThat generates a route named `api`. So a service named `api` **and** a global\nroute named `api` collide \u2014 *duplicate route name api*. Watch for this when\nmixing both styles.\n\nMatching is host + optional path prefix. There is no path rewriting or\nstripping: the matched path is forwarded to the upstream as-is.\n\n---\n\n## Modular layout\n\nWhen the main file is `.devctl/config.yaml`, these merge in automatically:\n\n```\n.devctl/services/<name>.yaml \u2192 services.<name> (FILENAME is the key)\n.devctl/profiles/<name>.yaml \u2192 profiles.<name>\n.devctl/proxy/routes.yaml \u2192 proxy.routes (see the wrapper below)\n```\n\nA service or profile file contains that object's body **only** \u2014 no `services:`\nwrapper, no `version:`. Getting this wrong reads oddly: a `services:` wrapper\ninside `.devctl/services/api.yaml` reports\n`unknown fields: services.api.services`.\n\n`proxy/routes.yaml` is the exception: it accepts either a full `proxy:` wrapper\nor a bare top-level `routes:` list. Both append to the same global route list.\n\n```yaml\n# .devctl/proxy/routes.yaml \u2014 wrapper form, also lets you set proxy.listen here\nproxy:\n routes:\n - name: invoices-api\n match: { host: invoices-api.local }\n upstream: { url: \"http://127.0.0.1:18000\" }\n```\n\n```yaml\n# .devctl/proxy/routes.yaml \u2014 bare form, equivalent for routes alone\nroutes:\n - name: invoices-api\n match: { host: invoices-api.local }\n upstream: { url: \"http://127.0.0.1:18000\" }\n```\n\nPick one. Both keys in the same file append **both** lists, which trips the\nduplicate-route-name check.\n\nOverlays merge after the repo config, later wins:\nbuilt-in defaults \u2192 repo `.devctl` \u2192 `~/.devctl/config.local.yaml` \u2192\n`.devctl/config.local.yaml` \u2014 the repo-local overlay wins over the home one.\nOverlays are presence-aware:\n`false`, `0` and\nempty collections in an overlay do override, so `proxy.enabled: false` in a\nlocal overlay genuinely turns the proxy off.\n\n---\n\n## Environment\n\nMerge order \u2014 later sources win:\n\n```\nprocess \u2192 profile \u2192 dotenv \u2192 generated \u2192 keychain \u2192 secret_manager \u2192 defaults \u2192 vars \u2192 runtime\n```\n\n`process`, `defaults`, `vars` and `runtime` always run. Listing\n`environment.sources` **adds** optional sources (`profile`, `dotenv`,\n`generated`, `keychain`, `secret_manager`) to that always-on set \u2014 it does not\nreplace it, and it does not reorder anything.\n\n- `dotenv` reads repo root then `working_dir`: `.env`, `.env.development`,\n `.env.local`, `.env.<profile>`.\n- `keychain` and `secret_manager` throw when listed and the fetch fails, so\n only list them when the repo genuinely uses them.\n- `environment.required` on a service fails the start if those keys are still\n empty after the whole merge \u2014 the right place to encode \"this cannot run\n without X\".\n\nRuntime values devctl injects: `SERVICE_PORT`, `SERVICE_HOST`,\n`DEVCTL_PROXY_URL`, `DEVCTL_SERVICE_NAME`, `DEVCTL_ENVIRONMENT`,\n`DEVCTL_TOKEN_URL`, `DEVCTL_INTERNAL_TOKEN`. Do not define these yourself.\n\n---\n\n## Templates\n\n```yaml\ntemplates:\n python-http:\n health: { type: http, interval_seconds: 2, timeout_seconds: 1 }\n logs: { stdout: true, stderr: true }\n restart: { policy: on_failure, max_retries: 2, backoff_seconds: 1 }\n\nservices:\n api:\n extends: python-http\n command: [python3, main.py]\n health: { url: \"http://127.0.0.1:8000/health\" } # merged field-by-field\n```\n\n- `extends` must name a template that exists.\n- Nested sections merge **field by field**, so a service can override one\n health field without restating the rest.\n- A template is not a service and is never started.\n\n---\n\n## Reading validation output\n\nEvery message names its path. Fix the path it names.\n\n| Message | Meaning |\n|---|---|\n| `at least one service must be defined` | empty/missing `services` |\n| `duplicate port N used by X and Y` | two services pinned the same port |\n| `services.X.command is required` | missing or empty command |\n| `services.X.command contains shell metacharacters` | add `shell: true` or use argv |\n| `services.X.dependencies: unknown service \"Y\"` | typo, or Y lives in a file you did not create |\n| `dependency cycle: a \u2192 b \u2192 a` | remove an edge |\n| `services.X.health.url is required for http health checks` | add `url`, or change the type |\n| `services.X.identity.service_account must be an email` | placeholder left unresolved |\n| `services.X.environment.K: unresolvable reference ${\u2026}` | referenced service or port name does not exist |\n| `services.X.capabilities: unknown capability \"c\"` | only the six listed above are accepted |\n| `profiles.P references unknown service \"S\"` | profile lists a service that is not defined |\n| `proxy.routes[i]: duplicate route name N` | often a per-service fragment colliding with a global route |\n| `proxy.routes[i].auth.audience is required when auth.type is iap` | IAP needs both audience and identity.type |\n| `proxy.routes[i].auth.client_id is only valid when auth.type is iap` | `client_id` / secret only apply to IAP user routes |\n| `proxy.routes[i].auth.client_id is required when client_secret is set` | secret without client_id |\n| `proxy.routes[i].auth.client_secret is required when client_id is set` | client_id needs a secret |\n| `proxy.routes[i].auth.client_id is only valid with identity.type user` | SA IAP uses generateIdToken, not a user OAuth client |\n| `proxy.listen.port is required when proxy.enabled is true` | pin a port |\n| `unsupported config version N (expected 1)` | `version:` must be `1` |\n| `unknown fields: services.a.depends_on` | not in the allowlists at the top of this file \u2014 usually a compose or k8s spelling |\n\nEvery message above is the validator's exact wording. `devctl config validate`\nprints one per line and exits 2; a clean run prints `configuration is valid`\nand exits 0.\n",discovery:`# Discovery: repository signal \u2192 devctl service
371
371
 
372
372
  How to turn what a repository contains into the service list you draft from.
373
373
  Read the signals in roughly this order \u2014 the earlier ones describe *how the
@@ -594,7 +594,7 @@ Rules for turning that into YAML:
594
594
  a full one. Remember that a bare \`devctl start\` falls back to the first
595
595
  configured profile, and the TUI's default is the first profile
596
596
  alphabetically \u2014 so name them with that in mind.
597
- `}});var Ny;var Hy=D(()=>{Ny=[{path:"docs/README.md",title:"devctl wiki",body:`# devctl wiki
597
+ `}});var Uy;var Wy=I(()=>{Uy=[{path:"docs/README.md",title:"devctl wiki",body:`# devctl wiki
598
598
 
599
599
  \`devctl\` is a configuration-driven local development orchestrator. It starts and stops services, resolves environment, optional Google identity, an auth-aware proxy, health checks, and centralized logs \u2014 from a keyboard-first TUI, a CLI, or an optional MCP server for coding agents.
600
600
 
@@ -1263,9 +1263,81 @@ layout: home
1263
1263
  ---
1264
1264
 
1265
1265
  <script setup>
1266
- import { ref } from 'vue'
1266
+ import { onMounted, onUnmounted, ref } from 'vue'
1267
1267
  import { withBase } from 'vitepress'
1268
1268
  import TerminalHero from './.vitepress/theme/TerminalHero.vue'
1269
+ const landingRoot = ref(null)
1270
+ let revealObserver
1271
+ let motionPreference
1272
+ let pointerPreference
1273
+ let pointerFrame = 0
1274
+ let previewElement
1275
+
1276
+ function resetPreview() {
1277
+ cancelAnimationFrame(pointerFrame)
1278
+ if (!previewElement) return
1279
+ previewElement.style.removeProperty('--pointer-x')
1280
+ previewElement.style.removeProperty('--pointer-y')
1281
+ previewElement.style.removeProperty('--tilt-x')
1282
+ previewElement.style.removeProperty('--tilt-y')
1283
+ previewElement.removeAttribute('data-pointer')
1284
+ }
1285
+
1286
+ function followPointer(event) {
1287
+ if (motionPreference?.matches || !pointerPreference?.matches || event.pointerType === 'touch') return
1288
+ previewElement = event.currentTarget
1289
+ const { clientX, clientY } = event
1290
+ cancelAnimationFrame(pointerFrame)
1291
+ pointerFrame = requestAnimationFrame(() => {
1292
+ const bounds = previewElement.getBoundingClientRect()
1293
+ const x = Math.max(0, Math.min(1, (clientX - bounds.left) / bounds.width))
1294
+ const y = Math.max(0, Math.min(1, (clientY - bounds.top) / bounds.height))
1295
+ previewElement.style.setProperty('--pointer-x', \`\${x * 100}%\`)
1296
+ previewElement.style.setProperty('--pointer-y', \`\${y * 100}%\`)
1297
+ previewElement.style.setProperty('--tilt-x', \`\${(0.5 - y) * 1.2}deg\`)
1298
+ previewElement.style.setProperty('--tilt-y', \`\${(x - 0.5) * 1.2}deg\`)
1299
+ previewElement.setAttribute('data-pointer', '')
1300
+ })
1301
+ }
1302
+
1303
+ function revealAll() {
1304
+ revealObserver?.disconnect()
1305
+ landingRoot.value?.querySelectorAll('[data-reveal]').forEach(element => element.removeAttribute('data-reveal'))
1306
+ }
1307
+
1308
+ function updateMotionPreference() {
1309
+ if (motionPreference?.matches) {
1310
+ revealAll()
1311
+ resetPreview()
1312
+ }
1313
+ }
1314
+
1315
+ onMounted(() => {
1316
+ motionPreference = window.matchMedia('(prefers-reduced-motion: reduce)')
1317
+ pointerPreference = window.matchMedia('(hover: hover) and (pointer: fine)')
1318
+ motionPreference.addEventListener('change', updateMotionPreference)
1319
+ if (motionPreference.matches || !('IntersectionObserver' in window)) return
1320
+ revealObserver = new IntersectionObserver(entries => {
1321
+ for (const entry of entries) {
1322
+ if (!entry.isIntersecting) continue
1323
+ entry.target.setAttribute('data-reveal', 'visible')
1324
+ revealObserver.unobserve(entry.target)
1325
+ }
1326
+ }, { threshold: 0.06 })
1327
+ landingRoot.value?.querySelectorAll('.section-heading, .surface, .workflow-panel, .setup-copy, .setup-panel, .details-section > div, .demo-band, .faq-section > div, .closing').forEach(element => {
1328
+ // Leave content already on screen visible; enhance only upcoming sections.
1329
+ if (element.getBoundingClientRect().top < window.innerHeight) return
1330
+ element.setAttribute('data-reveal', 'pending')
1331
+ revealObserver.observe(element)
1332
+ })
1333
+ })
1334
+
1335
+ onUnmounted(() => {
1336
+ revealObserver?.disconnect()
1337
+ motionPreference?.removeEventListener('change', updateMotionPreference)
1338
+ resetPreview()
1339
+ })
1340
+
1269
1341
  const copyLabel = ref('Copy command')
1270
1342
  const activeProfile = ref('backend')
1271
1343
  const profileExamples = {
@@ -1283,7 +1355,7 @@ async function copyInstall() {
1283
1355
  }
1284
1356
  </script>
1285
1357
 
1286
- <div class="landing vp-raw">
1358
+ <div ref="landingRoot" class="landing vp-raw">
1287
1359
  <section class="landing-hero" aria-labelledby="hero-title">
1288
1360
  <div class="hero-copy">
1289
1361
  <p class="eyebrow"><span class="status-dot"></span> YOUR LOCAL DEVELOPMENT, IN SYNC</p>
@@ -1295,7 +1367,7 @@ async function copyInstall() {
1295
1367
  </div>
1296
1368
  <p class="hero-footnote">Open source. Local first. Your workflow.</p>
1297
1369
  </div>
1298
- <div class="hero-preview">
1370
+ <div class="hero-preview" @pointermove="followPointer" @pointerleave="resetPreview" @pointercancel="resetPreview">
1299
1371
  <div class="preview-caption"><span>ONE TERMINAL. THE WHOLE PICTURE.</span><span aria-hidden="true">\u2199</span></div>
1300
1372
  <TerminalHero />
1301
1373
  <div class="preview-footer"><span class="status-dot"></span> An illustrative devctl session <span class="preview-shortcut">\u2318 / ctrl + p <span>command palette</span></span></div>
@@ -1322,7 +1394,7 @@ async function copyInstall() {
1322
1394
  </div>
1323
1395
  <div class="workflow-example" aria-live="polite" aria-atomic="true">
1324
1396
  <p class="eyebrow">DEMO PLATFORM / EXAMPLE PROFILE</p>
1325
- <p class="workflow-command"><span aria-hidden="true">$ </span><code>devctl start --profile {{ activeProfile }}</code></p>
1397
+ <p :key="activeProfile" class="workflow-command"><span aria-hidden="true">$ </span><code>devctl start --profile {{ activeProfile }}</code></p>
1326
1398
  <p class="workflow-description">{{ profileExamples[activeProfile].description }}</p>
1327
1399
  <div class="workflow-services"><span v-for="service in profileExamples[activeProfile].services" :key="service"><i aria-hidden="true"></i>{{ service }}</span></div>
1328
1400
  <p class="workflow-note">Your repo, your names. Profiles are defined in your config.</p>
@@ -1465,7 +1537,7 @@ The npm package supports macOS arm64/x64, Linux arm64/x64 (glibc or musl), and W
1465
1537
  - [npm publishing (maintainers)](npm-publishing.md)
1466
1538
  - [How it fits together](overview.md)
1467
1539
  - [Changelog](../CHANGELOG.md)
1468
- `},{path:"docs/logs.md",title:"Logs",body:"# Logs\n\nAll service stdout/stderr, proxy events, health checks, authentication events, and `devctl` internal lines go through one log manager on the supervisor.\n\nSources you will see: `stdout`, `stderr`, `health`, `auth`, `devctl`, plus proxy lines.\n\n## Buffer and persistence\n\n- In-memory circular buffer: `logs.max_memory_events` (default 50,000). Retention stays O(1) per line even after the buffer fills.\n- The live ring lives in a Bun Worker behind `LogStore` when running from source or npm, so parse and search do not stall the supervisor event loop. The main thread only receives page/facet/export results (and a cached snapshot for `status`). Compiled standalone binaries (`bun build --compile`) keep the ring in-process \u2014 Bun 1.4.0 cannot resolve the worker script inside a single-file executable. If the worker fails to start, the daemon falls back to the in-process store rather than hanging.\n- Ingest truncates lines longer than 16 KiB and skips `JSON.parse` on payloads larger than 64 KiB. Regex search is already capped (pattern length, nested quantifiers).\n- Optional persistence under `~/.devctl/logs/` (`persistence.enabled`, `directory`, `retention_days`, `max_session_logs`).\n- Ingest is a bounded channel; UI updates batch (~30ms) so a noisy service cannot freeze the TUI.\n- The detached supervisor's own bootstrap stderr (before it has a config, so before any of the above even starts) is a separate file with its own rotation \u2014 the last 5 boot attempts are kept, each overwrite-proof against the next. See `devctl daemon logs` in the [CLI reference](cli.md).\n\n## Pagination and facets\n\nQueries (CLI, TUI, MCP) return a bounded, cursor-paged slice instead of the whole matching history: a page defaults to the latest 500 matching events, capped at 5,000. The cursor is opaque (carries the daemon session and an internal per-event sequence number) and pages both backward (older) and forward (newer) without duplicating or dropping events that share the same millisecond \u2014 a plain timestamp boundary can't make that guarantee once two events land in the same millisecond and a page cuts between them. `since`/`until` keep working as ordinary timestamp filters alongside the cursor. Exporting (`/export`, `devctl logs export`) still reads the entire matching history \u2014 page size never truncates an export.\n\nFacets \u2014 the total matching count, plus per-service/level/source counts (each computed under every *other* active filter, not its own) \u2014 come from a separate, lightweight stats query with no event payload. The TUI refreshes them every two seconds while its logs screen is open, and immediately on a filter change, a clear, or reconnecting, so the filter chips' counts and the log pane title stay accurate even though the TUI itself only ever holds a bounded page rather than the full history.\n\n## TUI (Logs tab)\n\n- `f` focuses search. `/` stays the command line. Matches are highlighted in the log line (plain or `/regex`). Dashboard tail uses the same search filter.\n- `e` / `/filter` \u2014 ERROR and above.\n- `p` / `/pause` \u2014 freeze the live stream.\n- `z` / `/fullscreen` \u2014 hide header and nav so the stream fills a small editor terminal. `z` or `esc` exits.\n- `t` / `m` \u2014 timestamp and metadata columns (persist in `tui.json`).\n- `w` / `/wrap` \u2014 clip \u2192 unwrap the selected row \u2192 wrap every long line.\n- `g` \u2014 jump to latest. Leaving the tail pins the view (`pinned \xB7 +N new`).\n- `\u2190`/`\u2192` or click a chip \u2014 cycle service filters. Digits `1`\u2013`4` jump nav tabs, not log sources.\n- `\\\\` / `/split` \u2014 second pane on the same live stream, with its own service filter. Shared search. `|` focuses the other pane.\n- `/trace <id>` \u2014 set search to that request/trace id. Enter in the details overlay on a row that has an id does the same.\n- `enter` \u2014 details overlay (full message, pid, stream, request_id, identity).\n- `command+c` (macOS) or `ctrl+c` (Linux/Windows) \u2014 copy the highlighted selection. Remap with `keybinds.copy`.\n- `/export [path]` \u2014 write the **current** filters. Default file: `~/.devctl/exports/devctl-logs-<timestamp>.log`.\n- `/exports` or the **open folder** chip \u2014 reveal that directory.\n- `/history [id]` \u2014 load a persisted session (`LogManager.listSessions`).\n- `/regex`, `/since`, `/until` \u2014 search and time range (`until` is exclusive of later lines).\n\nLong lines fold with a `\u25B8N` marker. `j`/`k` moves the highlight and unwraps that row.\n\n## CLI\n\n```bash\ndevctl logs [svc\u2026] [--level] [--search] [--regex] [--source] [--since] [--until] [--json]\ndevctl logs # latest page (same as MCP get_logs); pass --all for the full match set\ndevctl logs -f # keep printing new matching events until interrupted\ndevctl logs --output FILE # same filters, write a file (full history, not just one page)\ndevctl logs export --output FILE # explicit export subcommand\ndevctl daemon logs [-f] # the supervisor's own bootstrap stderr, not service logs\n```\n\n## Related\n\n- [TUI](tui.md)\n- [CLI](cli.md)\n- [Security](security.md)\n"},{path:"docs/mcp.md",title:"MCP server for coding agents",body:"# MCP server for coding agents\n\n`devctl` can expose a **localhost Streamable HTTP** MCP server so Claude, Cursor, Codex, and Kilo Code can read status, logs, and config, and start or stop services.\n\nThe listener lives on the **supervisor** (the same process that owns services and the proxy). The TUI only toggles it and copies config. Agents need a URL; a stdio child of the TUI would die when the TUI exits.\n\n## Setting up a repository that has no `.devctl` yet\n\n`devctl mcp --on` works in a repository with no configuration at all. The\ndaemon boots in **setup mode**: nothing is validated and no service can start,\nbut the MCP server is up, so you can point an agent at it and ask it to set\ndevctl up for the repository.\n\n```bash\ndevctl mcp --on\n```\n\nThe agent calls `get_setup_guide` and `search_docs`, surveys the repo, drafts a\nconfig, checks it with `validate_config` (which accepts candidate text, so it\ncan check a draft before writing it), writes the files, and calls\n`reload_config`. Setup mode clears on the reload that finds a valid\nconfiguration, and `.devctl/` starts being watched for changes from then on.\n\n`get_status` reports `setup_mode: true` while this state is active, so an agent\ncan tell an empty service list apart from a daemon that failed to start\nanything.\n\nDefault is **off** (`mcp_enabled` in `~/.devctl/tui.json`). Once on, the supervisor applies that preference itself at startup \u2014 whether it was spawned by the TUI or by a plain CLI command \u2014 so the listener comes back on the next `devctl start` too, not only while the TUI is attached.\n\n## Enable it\n\n```mermaid\nflowchart LR\n open[\"/mcp or Settings \u2192 MCP page\"] --> listen[\"Listen ON\"]\n listen --> copy[\"Copy JSON / TOML\"]\n copy --> agent[\"Claude / Cursor / Codex / Kilo\"]\n agent --> http[\"127.0.0.1:port/mcp\"]\n http --> sup[\"Supervisor\"]\n```\n\nMCP is **not** a nav tab. Flip **Listen** (`space` / `enter`). The header shows an **MCP** chip when it is running.\n\nThe default port is derived from the repo so checkouts do not collide:\n\n`18700 + (parseInt(repoID.slice(0, 8), 16) % 600)` \u2192 **18700\u201319299**.\n\nChange it with `\u2190` / `\u2192` or `devctl mcp --port`. An override is persisted as `mcp_port` only when it is not the derived default. If the preferred port is busy, the supervisor walks upward until it finds a free one.\n\nThe server binds **`127.0.0.1` only**. Mutating tools require `Authorization: Bearer` with a short session token. Copied snippets include that header. Tool output never includes tokens or raw secret env values. `get_status` reports MCP running/address/port, not the bearer token.\n\n## CLI\n\n```text\ndevctl mcp # URL + four snippets\ndevctl mcp --on [--port N]\ndevctl mcp --off\ndevctl mcp --json\n```\n\n`--on` starts a supervisor if needed. `--off` stops the listener only.\n\n## Tools and resources\n\n| Tool | Group | What it does |\n|------|-------|----------------|\n| `list_services` | inspect | Name, state, health, ports, pid, last error |\n| `get_service` | inspect | One service plus command/cwd/ports (env redacted or left as `${\u2026}` refs) |\n| `get_status` | inspect | Profile, session, identity flags, proxy, log counts, MCP listen |\n| `get_logs` | logs | Filtered logs, capped at 200 events per page, secrets redacted. Pass `cursor` from the previous `next_cursor` to page forward with no duplicate or same-millisecond-lost lines; `since`/`until` are plain timestamp filters for a fresh query |\n| `list_profiles` | inspect | Config profiles and members |\n| `get_config` | inspect | Merged summary: project, services, routes, proxy paths |\n| `get_config_sources` | inspect | Effective values with winning and shadowed configuration sources; secret-like values are redacted |\n| `run_doctor` | diagnostics | Doctor report |\n| `start_services` | control | Named list, or a `profile`. Omitted names use `profile`, then the active session profile, then the first configured profile \u2014 never every service. No profile and no names fails closed |\n| `stop_services` | control | Named list, or all started services when omitted. Also stops every transitive dependent of a named service \u2014 never its dependencies |\n| `restart_services` | control | Named list; touches only those services, not dependents, unless `cascade: true`. Start still expands dependencies |\n| `reload_config` | control | Reload `.devctl` |\n| `run_task` | control | Run a named task from configuration; output is also in the log ring as `task:<name>` |\n| `start_proxy` / `stop_proxy` | control | Start or stop the local reverse proxy |\n| `exec_service` | control | Run an arbitrary command in a service's resolved environment/cwd, or inspect its redacted environment with `print_env` |\n| `get_setup_guide` | setup | The onboarding guide for authoring a `.devctl`. `section`: `procedure` (default), `authoring`, `discovery`. Same text as [`skills/devctl-onboard`](../skills/devctl-onboard/SKILL.md), compiled into the binary so no skill install is needed |\n| `search_docs` | setup | Keyword search over the compiled-in product docs (`docs/*.md`) and the onboarding skill. Pass `query`; optional `limit` (default 5, max 10). Returns ranked pages with snippets |\n| `validate_config` | setup | Validate configuration and return the loader's exact issues. No arguments validates what is on disk; `text` validates a candidate `config.yaml` through the real load pipeline before it is written |\n\nNo tool writes files. An agent authors `.devctl` with its own editing tools and uses `validate_config` to check the result.\n\nInteractive `gcloud` login stays CLI/TUI-only (`devctl auth login` / `/auth login`). MCP `run_doctor` already probes service accounts; run `devctl auth login` when ADC is missing.\n\n`get_logs` is paged (cap 200). To follow, poll with `cursor=next_cursor`. There is no blocking `follow` tool.\n\n## Enabling and disabling tools\n\nEvery tool is on by default. The TUI's **MCP** page lists them grouped by the\n`Group` column above, each marked `read` or `write`, and `space` toggles the\nhighlighted one. The common case is turning off the whole `control` group \u2014\n`start_services`, `stop_services`, `restart_services`, `reload_config`, `run_task`, `start_proxy`, `stop_proxy`, `exec_service` \u2014 so an\nagent can read status and logs but not start or stop anything.\n\nA disabled tool is left out of `tools/list` **and** refused if called anyway,\nsince an agent may still hold a tool list from before it was turned off. The\nrefusal names the tool and says it is disabled, rather than reporting it as\nunknown.\n\nThe setting is a deny-list stored as `mcp_disabled_tools` in `tui.json`, so a\ntool added by a later devctl version is available without editing anything.\nThe daemon applies it at boot the same way it applies `mcp_enabled`, and a TUI\ntoggle takes effect immediately without restarting the listener.\n\nAn agent cannot change this: `mcp_set_tools` is a local RPC and is deliberately\nabsent from the MCP host surface, so a connected client cannot re-enable a tool\nits operator turned off.\n\nResources (always-fresh reads): `devctl://status`, `devctl://services`, `devctl://logs`, `devctl://config`, `devctl://doctor`.\n\nDoctor may report ports \u201Cin use\u201D while your own services hold them \u2014 that is expected after a successful start.\n\n## Related\n\n- [TUI](tui.md)\n- [CLI](cli.md)\n- [How it fits together](overview.md)\n- [Agent skills](../skills/README.md)\n- [Security](security.md)\n"},{path:"docs/npm-publishing.md",title:"npm publishing",body:`# npm publishing
1540
+ `},{path:"docs/logs.md",title:"Logs",body:"# Logs\n\nAll service stdout/stderr, proxy events, health checks, authentication events, and `devctl` internal lines go through one log manager on the supervisor.\n\nSources you will see: `stdout`, `stderr`, `health`, `auth`, `devctl`, plus proxy lines.\n\n## Buffer and persistence\n\n- In-memory circular buffer: `logs.max_memory_events` (default 50,000). Retention stays O(1) per line even after the buffer fills.\n- The live ring lives in a Bun Worker behind `LogStore` when running from source or npm, so parse and search do not stall the supervisor event loop. The main thread only receives page/facet/export results (and a cached snapshot for `status`). Compiled standalone binaries (`bun build --compile`) keep the ring in-process \u2014 Bun 1.4.0 cannot resolve the worker script inside a single-file executable. If the worker fails to start, the daemon falls back to the in-process store rather than hanging.\n- Ingest truncates lines longer than 16 KiB and skips `JSON.parse` on payloads larger than 64 KiB. Regex search is already capped (pattern length, nested quantifiers).\n- Optional persistence under `~/.devctl/logs/` (`persistence.enabled`, `directory`, `retention_days`, `max_session_logs`).\n- Ingest is a bounded channel; UI updates batch (~30ms) so a noisy service cannot freeze the TUI.\n- The detached supervisor's own bootstrap stderr (before it has a config, so before any of the above even starts) is a separate file with its own rotation \u2014 the last 5 boot attempts are kept, each overwrite-proof against the next. See `devctl daemon logs` in the [CLI reference](cli.md).\n\n## Pagination and facets\n\nQueries (CLI, TUI, MCP) return a bounded, cursor-paged slice instead of the whole matching history: a page defaults to the latest 500 matching events, capped at 5,000. The cursor is opaque (carries the daemon session and an internal per-event sequence number) and pages both backward (older) and forward (newer) without duplicating or dropping events that share the same millisecond \u2014 a plain timestamp boundary can't make that guarantee once two events land in the same millisecond and a page cuts between them. `since`/`until` keep working as ordinary timestamp filters alongside the cursor. Exporting (`/export`, `devctl logs export`) still reads the entire matching history \u2014 page size never truncates an export.\n\nFacets \u2014 the total matching count, plus per-service/level/source counts (each computed under every *other* active filter, not its own) \u2014 come from a separate, lightweight stats query with no event payload. The TUI refreshes them every two seconds while its logs screen is open, and immediately on a filter change, a clear, or reconnecting, so the filter chips' counts and the log pane title stay accurate even though the TUI itself only ever holds a bounded page rather than the full history.\n\n## TUI (Logs tab)\n\n- `f` focuses search. `/` stays the command line. Matches are highlighted in the log line (plain or `/regex`). Dashboard tail uses the same search filter.\n- `e` / `/filter` \u2014 ERROR and above.\n- `p` / `/pause` \u2014 freeze the live stream.\n- `z` / `/fullscreen` \u2014 hide header and nav so the stream fills a small editor terminal. `z` or `esc` exits.\n- `t` / `m` \u2014 timestamp and metadata columns (persist in `tui.json`).\n- `w` / `/wrap` \u2014 clip \u2192 unwrap the selected row \u2192 wrap every long line.\n- `g` \u2014 jump to latest. Leaving the tail pins the view (`pinned \xB7 +N new`).\n- `\u2190`/`\u2192` or click a chip \u2014 cycle service filters. Digits `1`\u2013`4` jump nav tabs, not log sources.\n- `\\\\` / `/split` \u2014 second pane on the same live stream, with its own service filter. Shared search. `|` focuses the other pane.\n- `/trace <id>` \u2014 set search to that request/trace id. Enter in the details overlay on a row that has an id does the same.\n- `enter` \u2014 details overlay (full message, pid, stream, request_id, identity).\n- `command+c` (macOS) or `ctrl+c` (Linux/Windows) \u2014 copy the highlighted selection. Remap with `keybinds.copy`.\n- `/export [path]` \u2014 write the **current** filters. Default file: `~/.devctl/exports/devctl-logs-<timestamp>.log`.\n- `/exports` or the **open folder** chip \u2014 reveal that directory.\n- `/history [id]` \u2014 load a persisted session (`LogManager.listSessions`).\n- `/regex`, `/since`, `/until` \u2014 search and time range (`until` is exclusive of later lines).\n\nLong lines fold with a `\u25B8N` marker. `j`/`k` moves the highlight and unwraps that row.\n\n## CLI\n\n```bash\ndevctl logs [svc\u2026] [--level] [--search] [--regex] [--source] [--since] [--until] [--json]\ndevctl logs # latest page (same as MCP get_logs); pass --all for the full match set\ndevctl logs -f # keep printing new matching events until interrupted\ndevctl logs --output FILE # same filters, write a file (full history, not just one page)\ndevctl logs export --output FILE # explicit export subcommand\ndevctl daemon logs [-f] # the supervisor's own bootstrap stderr, not service logs\n```\n\n## Related\n\n- [TUI](tui.md)\n- [CLI](cli.md)\n- [Security](security.md)\n"},{path:"docs/mcp.md",title:"MCP server for coding agents",body:"# MCP server for coding agents\n\n`devctl` can expose a **localhost Streamable HTTP** MCP server so Claude, Cursor, Codex, and Kilo Code can read status, logs, and config, and start or stop services.\n\nThe listener lives on the **supervisor** (the same process that owns services and the proxy). The TUI only toggles it and copies config. Agents need a URL; a stdio child of the TUI would die when the TUI exits.\n\n## Setting up a repository that has no `.devctl` yet\n\n`devctl mcp --on` works in a repository with no configuration at all. The\ndaemon boots in **setup mode**: nothing is validated and no service can start,\nbut the MCP server is up, so you can point an agent at it and ask it to set\ndevctl up for the repository.\n\n```bash\ndevctl mcp --on\n```\n\nThe agent calls `get_setup_guide` and `search_docs`, surveys the repo, drafts a\nconfig, checks it with `validate_config` (which accepts candidate text, so it\ncan check a draft before writing it), writes the files, and calls\n`reload_config`. Setup mode clears on the reload that finds a valid\nconfiguration, and `.devctl/` starts being watched for changes from then on.\n\n`get_status` reports `setup_mode: true` while this state is active, so an agent\ncan tell an empty service list apart from a daemon that failed to start\nanything.\n\nDefault is **off** (`mcp_enabled` in `~/.devctl/tui.json`). Once on, the supervisor applies that preference itself at startup \u2014 whether it was spawned by the TUI or by a plain CLI command \u2014 so the listener comes back on the next `devctl start` too, not only while the TUI is attached.\n\n## Enable it\n\n```mermaid\nflowchart LR\n open[\"/mcp or Settings \u2192 MCP page\"] --> listen[\"Listen ON\"]\n listen --> copy[\"Copy JSON / TOML\"]\n copy --> agent[\"Claude / Cursor / Codex / Kilo\"]\n agent --> http[\"127.0.0.1:port/mcp\"]\n http --> sup[\"Supervisor\"]\n```\n\nMCP is **not** a nav tab. Flip **Listen** (`space` / `enter`). The header shows an **MCP** chip when it is running.\n\nThe default port is derived from the repo so checkouts do not collide:\n\n`18700 + (parseInt(repoID.slice(0, 8), 16) % 600)` \u2192 **18700\u201319299**.\n\nChange it with `\u2190` / `\u2192` or `devctl mcp --port`. An override is persisted as `mcp_port` only when it is not the derived default. If the preferred port is busy, the supervisor walks upward until it finds a free one.\n\nThe server binds **`127.0.0.1` only**. Mutating tools require `Authorization: Bearer` with a short session token. Copied snippets include that header. Tool output never includes tokens or raw secret env values. `get_status` reports MCP running/address/port, not the bearer token.\n\n## CLI\n\n```text\ndevctl mcp # URL + four snippets\ndevctl mcp --on [--port N]\ndevctl mcp --off\ndevctl mcp --json\n```\n\n`--on` starts a supervisor if needed. `--off` stops the listener only.\n\n## Tools and resources\n\n| Tool | Group | What it does |\n|------|-------|----------------|\n| `list_services` | inspect | Name, state, health, ports, pid, last error |\n| `get_service` | inspect | One service plus command/cwd/ports (env redacted or left as `${\u2026}` refs) |\n| `get_status` | inspect | Profile, session, identity flags, proxy, log counts, MCP listen |\n| `get_logs` | logs | Filtered logs, capped at 200 events per page, secrets redacted. Pass `cursor` from the previous `next_cursor` to page forward with no duplicate or same-millisecond-lost lines; `since`/`until` are plain timestamp filters for a fresh query |\n| `list_profiles` | inspect | Config profiles and members |\n| `get_config` | inspect | Merged summary: project, services, routes, proxy paths |\n| `get_config_sources` | inspect | Effective values with winning and shadowed configuration sources; secret-like values are redacted |\n| `run_doctor` | diagnostics | Doctor report |\n| `start_services` | control | Named list, or a `profile`. Omitted names use `profile`, then the active session profile, then the first configured profile \u2014 never every service. No profile and no names fails closed |\n| `stop_services` | control | Named list, or all started services when omitted. Also stops every transitive dependent of a named service \u2014 never its dependencies |\n| `restart_services` | control | Named list; touches only those services, not dependents, unless `cascade: true`. Start still expands dependencies |\n| `reload_config` | control | Reload `.devctl` |\n| `run_task` | control | Run a named task from configuration; output is also in the log ring as `task:<name>` |\n| `start_proxy` / `stop_proxy` | control | Start or stop the local reverse proxy |\n| `exec_service` | control | Run an arbitrary command in a service's resolved environment/cwd, or inspect its redacted environment with `print_env` |\n| `get_setup_guide` | setup | The onboarding guide for authoring a `.devctl`. `section`: `procedure` (default), `authoring`, `discovery`. Same text as [`skills/devctl-onboard`](../skills/devctl-onboard/SKILL.md), compiled into the binary so no skill install is needed |\n| `search_docs` | setup | Keyword search over the compiled-in product docs (`docs/*.md`) and the onboarding skill. Pass `query`; optional `limit` (default 5, max 10). Returns ranked pages with short snippets \u2014 pass a hit's `path` to `get_doc` to read the whole page |\n| `get_doc` | setup | Return the full text of one embedded doc page. Pass `path` from a `search_docs` hit (e.g. `docs/proxy.md`); an unambiguous basename like `proxy.md` also resolves |\n| `validate_config` | setup | Validate configuration and return the loader's exact issues. No arguments validates what is on disk; `text` validates a candidate `config.yaml` through the real load pipeline before it is written |\n\nNo tool writes files. An agent authors `.devctl` with its own editing tools and uses `validate_config` to check the result.\n\nInteractive `gcloud` login stays CLI/TUI-only (`devctl auth login` / `/auth login`). MCP `run_doctor` already probes service accounts; run `devctl auth login` when ADC is missing.\n\n`get_logs` is paged (cap 200). To follow, poll with `cursor=next_cursor`. There is no blocking `follow` tool.\n\n## Enabling and disabling tools\n\nEvery tool is on by default. The TUI's **MCP** page lists them grouped by the\n`Group` column above, each marked `read` or `write`, and `space` toggles the\nhighlighted one. The common case is turning off the whole `control` group \u2014\n`start_services`, `stop_services`, `restart_services`, `reload_config`, `run_task`, `start_proxy`, `stop_proxy`, `exec_service` \u2014 so an\nagent can read status and logs but not start or stop anything.\n\nA disabled tool is left out of `tools/list` **and** refused if called anyway,\nsince an agent may still hold a tool list from before it was turned off. The\nrefusal names the tool and says it is disabled, rather than reporting it as\nunknown.\n\nThe setting is a deny-list stored as `mcp_disabled_tools` in `tui.json`, so a\ntool added by a later devctl version is available without editing anything.\nThe daemon applies it at boot the same way it applies `mcp_enabled`, and a TUI\ntoggle takes effect immediately without restarting the listener.\n\nAn agent cannot change this: `mcp_set_tools` is a local RPC and is deliberately\nabsent from the MCP host surface, so a connected client cannot re-enable a tool\nits operator turned off.\n\nResources (always-fresh reads): `devctl://status`, `devctl://services`, `devctl://logs`, `devctl://config`, `devctl://doctor`.\n\nDoctor may report ports \u201Cin use\u201D while your own services hold them \u2014 that is expected after a successful start.\n\n## Related\n\n- [TUI](tui.md)\n- [CLI](cli.md)\n- [How it fits together](overview.md)\n- [Agent skills](../skills/README.md)\n- [Security](security.md)\n"},{path:"docs/npm-publishing.md",title:"npm publishing",body:`# npm publishing
1469
1541
 
1470
1542
  This project publishes \`@amr-m-abdelgawad/devctl\` as a free public npm package through npm Trusted Publishing. The release workflow uses GitHub's short-lived OpenID Connect identity; the repository must not contain an npm access token.
1471
1543
 
@@ -1722,7 +1794,7 @@ The OIDC plugin is intentionally a reference implementation: it supports client
1722
1794
  - [Environment](environment.md)
1723
1795
  - [Authentication](authentication.md)
1724
1796
  - [Security](security.md)
1725
- `},{path:"docs/profiles.md",title:"Profiles",body:"# Profiles\n\nA profile is a named list of services plus optional extra environment.\n\n```yaml\nprofiles:\n data:\n services: [postgres]\n minimal:\n services: [identity, invoices-api]\n backend:\n services: [identity, invoices-api, invoices-worker]\n environment:\n LOG_LEVEL: DEBUG\n full:\n services: [identity, invoices-api, invoices-worker, billing-console]\n```\n\n```bash\ndevctl start --profile backend\n```\n\nThe TUI **profiles** screen (`o` or `/profiles`) lists configured profiles. `enter` selects one and offers start. None are hard-coded.\n\nEmpty-dashboard `enter` uses the first profile name **alphabetically** when no session profile is set \u2014 YAML key order does not matter. In the [demo platform](../examples/demo-platform/README.md) that is `backend`, not `data`, even though `data` is listed first in the file.\n\n`devctl start` / MCP `start_services` with **no** profile and **no** names starts the active session profile, or the first configured profile (alphabetically). With no profiles it fails closed. Pass `--profile` or explicit names to stay on a subset. It never expands to every service just because the list was empty.\n\nThe demo also defines `data` (opt-in Docker/PostgreSQL). It is not a default profile; start it with `--profile data`.\n\n## Sessions\n\nPer-repo state lives under `~/.devctl/state/<repoID>/`:\n\n| File | Role |\n|------|------|\n| `state.json` | session id, profile, pid / command / cwd / startTime / ports |\n| `devctl.lock` | supervisor lock (stale locks from dead PIDs are replaced) |\n| `devctl.sock` | JSON-RPC socket for TUI, CLI, and attach (Unix) |\n| `\\\\.\\pipe\\devctl-<repoID>` | Named pipe used instead of the socket on Windows |\n\nA leftover `~/.devctl/sessions/<repoID>/` is migrated once.\n\nA new supervisor **adopts** leftover processes only when pid + command + cwd + startTime still match. An empty cwd on either side is ignored. It never signals an unrelated PID. `SessionRecovered` is published when anything is adopted. Adopted processes keep health polling; stdout/stderr from before adopt are not captured. A port-only leftover is logged and shown in Doctor; it is not attached.\n\nOverride the home directory with `DEVCTL_HOME`.\n\n## Related\n\n- [Services](services.md)\n- [How it fits together](overview.md)\n- [CLI](cli.md)\n"},{path:"docs/proxy.md",title:"Proxy",body:"# Proxy\n\nThe local proxy injects authentication so services do not each implement Google/IAP logic. It runs **inside the supervisor**.\n\nThere is no implicit listen port. `proxy.listen.port` is **required** when `proxy.enabled` is true \u2014 `devctl config validate` and any load fail with `proxy.listen.port is required when proxy.enabled is true` (exit **2**). `devctl setup` writes `127.0.0.1:8080` as a starter; the demo uses `127.0.0.1:18080`. Binding to `0.0.0.0` or `::` is rejected.\n\n`devctl proxy start`, TUI `n` on the proxy screen, and MCP `start_proxy` still refuse a `0` port even when the proxy is not enabled: same wording, CLI exit **7**.\n\n```bash\ndevctl proxy start\ndevctl proxy status\ndevctl proxy stop\n```\n\nThe TUI **proxy** tab (`p`) shows status, routes, and a live log of recent requests. `n` starts, `x` stops. SA email is shown when a route uses one.\n\nEach request gets `X-Devctl-Request-ID` \u2014 propagated from the caller if it sent one, generated otherwise \u2014 and it's echoed back on the response so a caller can find its own request in the log below. Proxy logs never include `Authorization` headers. Bodies are streamed.\n\nWebSocket upgrades use the same route matching, identity injection, middleware, request logging, and statistics as ordinary HTTP traffic (HMR and other upgraded connections behind a route). Active upgraded sockets are closed during proxy shutdown so `devctl down` cannot hang.\n\nIf `proxy.enabled` is true, `devctl start` also starts the proxy.\n\n## Routes\n\n```yaml\nproxy:\n enabled: true\n listen:\n host: 127.0.0.1\n port: 8080\n routes:\n - name: invoices-api\n match:\n host: invoices-api.local\n path: \"\"\n upstream:\n url: http://127.0.0.1:18000\n auth:\n type: none # none | iap | service_account\n identity: user # or { type: service_account, service_account: email }\n # IAP only: audience is required. Optional client_id + client_secret\n # mint the ID token with that OAuth client instead of ADC's default.\n # client_secret may be a literal or ${NAME} / ${env.NAME}.\n```\n\nMatch is host + optional path prefix.\n\n### Per-service routes\n\nOptional `proxy` on a service is one route fragment or a list. At load they append to the **same** global `proxy.routes` list with stable names (`<service>` or `<service>-<n>`). Duplicate names fail validation. Runtime stays one listener.\n\n```yaml\nservices:\n api:\n command: python main.py\n proxy:\n - match:\n path: /api\n upstream:\n url: http://127.0.0.1:8000\n```\n\n## Expose \u2014 the proxy as a stable entry point\n\nInstead of hand-writing a route, a service can be **exposed** through the proxy at a stable, logical address. The synthesized route addresses its target by service name, so the proxy resolves the service's **current** port at request time \u2014 a service that restarts on a different auto-assigned port is followed with no proxy reload and no consumer change.\n\n```yaml\nproxy:\n enabled: true # expose requires an enabled proxy\n listen: { host: 127.0.0.1, port: 8080 }\nservices:\n invoices-api:\n command: python main.py\n ports: { http: 18000 }\n expose: true # \u2192 route \"invoices-api\", match host invoices-api.local\n```\n\n`expose: true` matches host `<service>.local` and forwards to the service's `http` port. Exposure is host-based: the request path is forwarded verbatim, so a path prefix belongs on a hand-written route, not here. Use the object form to override the host or port:\n\n```yaml\n expose:\n host: api.internal # default: <service>.local\n port: grpc # named port to forward to (default: http)\n```\n\nSet `proxy.gateway: true` to expose **every** HTTP-capable service (one with a port named `http`) at once \u2014 sugar over per-service `expose`. For selective exposure, leave `gateway` off and mark services individually.\n\nA hand-written route or `proxy:` fragment of the same name always wins over a synthesized one, so you can override any auto route (for example to attach auth).\n\n**Auth is always `none` on synthesized routes.** An internal service-to-service hop never silently acquires a service's identity token \u2014 injecting credentials stays an explicit choice you make with a hand-written route.\n\n### Referencing an exposed service \u2014 `${services.<name>.url}`\n\n`${services.<name>.url}` and `${services.<name>.host}` give a service a stable logical address in another service's environment:\n\n```yaml\nservices:\n billing-console:\n environment:\n API_URL: ${services.invoices-api.url}\n```\n\n- **Direct** (target not exposed): resolves to `http://127.0.0.1:<port>` \u2014 a startup snapshot, like `${services.<name>.port}`.\n- **Hub** (target exposed and proxy enabled): resolves to the proxy entry address, e.g. `http://invoices-api.local:8080`. This is stable \u2014 the consumer keeps working even when the target moves to a new port.\n\nHost-based addressing is for **host** clients: `<service>.local` must resolve to `127.0.0.1` on the machine that makes the request \u2014 add it to `/etc/hosts` or your resolver. A container's loopback is isolated from the host proxy, and the container schema has no host-network or extra-hosts mode, so do not point a container at `<service>.local`.\n\n## Token endpoint\n\nOptional `GET /token` (`proxy.token_endpoint`) binds to loopback (never `0.0.0.0` or `::`), requires `X-Devctl-Internal-Token`, and only accepts loopback peers.\n\n```json\n{\n \"access_token\": \"\u2026\",\n \"token_type\": \"Bearer\",\n \"expires_at\": \"2026-08-30T00:05:00.000Z\",\n \"identity\": \"user\"\n}\n```\n\nManaged processes receive `DEVCTL_TOKEN_URL` (rewritten to the bound port after listen) and `DEVCTL_INTERNAL_TOKEN`, not raw tokens in the environment.\n\n## Live request log\n\nThe proxy keeps the last 100 requests in memory \u2014 method, path, matched route (blank for a request that matched no route, still logged as a 404), identity key used, status, duration, and request id \u2014 and reports a running total/error count alongside them. This is part of the regular status snapshot, so it updates the same way everything else in the TUI does: the moment a request refreshes a token or hits a route, the **proxy** tab reflects it without pressing `r` or restarting anything.\n\nPaths are redacted the same way response header values already are, since a query string can carry secrets. Nothing here is persisted \u2014 it's an in-memory ring buffer, reset on daemon restart.\n\n## Request flow\n\n```mermaid\nflowchart TB\n client[\"Local client\"] --> listen[\"127.0.0.1:proxy\"]\n listen --> match[\"Match route: host + path prefix\"]\n match --> ident{\"identity.type\"}\n ident -->|user| adc[\"Application Default Credentials\"]\n ident -->|service_account| sa[\"Impersonated SA token\"]\n adc --> auth{\"auth.type\"}\n sa --> auth\n auth -->|iap| iap[\"IAP ID token for audience\"]\n auth -->|none| inject\n auth -->|service_account| satoken[\"OAuth access token for the SA\"]\n satoken --> inject\n iap --> inject[\"Inject Authorization + X-Devctl-Request-ID\"]\n inject --> up[\"Stream to upstream.url\"]\n```\n\nA missing `identity.type` on an IAP route is a configuration error.\n\n## Related\n\n- [IAP](iap.md)\n- [Impersonation](impersonation.md)\n- [Security](security.md)\n- [TUI](tui.md)\n"},{path:"docs/quickstart.md",title:"Quick start",body:`# Quick start
1797
+ `},{path:"docs/profiles.md",title:"Profiles",body:"# Profiles\n\nA profile is a named list of services plus optional extra environment.\n\n```yaml\nprofiles:\n data:\n services: [postgres]\n minimal:\n services: [identity, invoices-api]\n backend:\n services: [identity, invoices-api, invoices-worker]\n environment:\n LOG_LEVEL: DEBUG\n full:\n services: [identity, invoices-api, invoices-worker, billing-console]\n```\n\n```bash\ndevctl start --profile backend\n```\n\nThe TUI **profiles** screen (`o` or `/profiles`) lists configured profiles. `enter` selects one and offers start. None are hard-coded.\n\nEmpty-dashboard `enter` uses the first profile name **alphabetically** when no session profile is set \u2014 YAML key order does not matter. In the [demo platform](../examples/demo-platform/README.md) that is `backend`, not `data`, even though `data` is listed first in the file.\n\n`devctl start` / MCP `start_services` with **no** profile and **no** names starts the active session profile, or the first configured profile (alphabetically). With no profiles it fails closed. Pass `--profile` or explicit names to stay on a subset. It never expands to every service just because the list was empty.\n\nThe demo also defines `data` (opt-in Docker/PostgreSQL). It is not a default profile; start it with `--profile data`.\n\n## Sessions\n\nPer-repo state lives under `~/.devctl/state/<repoID>/`:\n\n| File | Role |\n|------|------|\n| `state.json` | session id, profile, pid / command / cwd / startTime / ports |\n| `devctl.lock` | supervisor lock (stale locks from dead PIDs are replaced) |\n| `devctl.sock` | JSON-RPC socket for TUI, CLI, and attach (Unix) |\n| `\\\\.\\pipe\\devctl-<repoID>` | Named pipe used instead of the socket on Windows |\n\nA leftover `~/.devctl/sessions/<repoID>/` is migrated once.\n\nA new supervisor **adopts** leftover processes only when pid + command + cwd + startTime still match. An empty cwd on either side is ignored. It never signals an unrelated PID. `SessionRecovered` is published when anything is adopted. Adopted processes keep health polling; stdout/stderr from before adopt are not captured. A port-only leftover is logged and shown in Doctor; it is not attached.\n\nOverride the home directory with `DEVCTL_HOME`.\n\n## Related\n\n- [Services](services.md)\n- [How it fits together](overview.md)\n- [CLI](cli.md)\n"},{path:"docs/proxy.md",title:"Proxy",body:"# Proxy\n\nThe local proxy injects authentication so services do not each implement Google/IAP logic. It runs **inside the supervisor**.\n\nThere is no implicit listen port. `proxy.listen.port` is **required** when `proxy.enabled` is true \u2014 `devctl config validate` and any load fail with `proxy.listen.port is required when proxy.enabled is true` (exit **2**). `devctl setup` writes `127.0.0.1:8080` as a starter; the demo uses `127.0.0.1:18080`. Binding to `0.0.0.0` or `::` is rejected.\n\n`devctl proxy start`, TUI `n` on the proxy screen, and MCP `start_proxy` still refuse a `0` port even when the proxy is not enabled: same wording, CLI exit **7**.\n\n```bash\ndevctl proxy start\ndevctl proxy status\ndevctl proxy stop\n```\n\nThe TUI **proxy** tab (`p`) shows status, routes, and a live log of recent requests. `n` starts, `x` stops. SA email is shown when a route uses one.\n\nEach request gets `X-Devctl-Request-ID` \u2014 propagated from the caller if it sent one, generated otherwise \u2014 and it's echoed back on the response so a caller can find its own request in the log below. Proxy logs never include `Authorization` headers. Bodies are streamed.\n\nWebSocket upgrades use the same route matching, identity injection, middleware, request logging, and statistics as ordinary HTTP traffic (HMR and other upgraded connections behind a route). Active upgraded sockets are closed during proxy shutdown so `devctl down` cannot hang.\n\nIf `proxy.enabled` is true, `devctl start` also starts the proxy.\n\n## Routes\n\n```yaml\nproxy:\n enabled: true\n listen:\n host: 127.0.0.1\n port: 8080\n routes:\n - name: invoices-api\n match:\n host: invoices-api.local\n path: \"\"\n upstream:\n url: http://127.0.0.1:18000\n auth:\n type: none # none | iap | service_account\n identity: user # or { type: service_account, service_account: email }\n # IAP only: audience is required. Optional client_id + client_secret\n # mint the ID token with that OAuth client instead of ADC's default.\n # client_secret may be a literal or ${NAME} / ${env.NAME}.\n```\n\nMatch is host + optional path prefix.\n\n### Custom OAuth client credentials (separate from ADC)\n\nA route can mint IAP tokens with a **custom OAuth client** via `auth.client_id` /\n`auth.client_secret`. By default the refresh token comes from gcloud ADC\n(`~/.config/gcloud/application_default_credentials.json`), which only works when\nADC was itself logged in with that same client \u2014 otherwise Google rejects the\nmint as `unauthorized_client` (\"client mismatch\").\n\nTo use a custom client without clobbering ADC (which GCS/Firestore and other\nGoogle SDKs depend on), point the route \u2014 or the whole proxy \u2014 at a **separate**\ngcloud *authorized_user* credentials file:\n\n```yaml\nproxy:\n credentials: ~/.devctl/iap-credentials.json # default for every IAP route\n routes:\n - name: orchestrator-api\n auth:\n type: iap\n audience: 507686272917-0dpd...\n client_id: 507686272917-4j6f...\n # client_secret optional here \u2014 the file can supply it\n credentials: ~/.devctl/iap-credentials.json # per-route override\n identity: { type: user }\n```\n\nThe file is a standard gcloud authorized_user JSON (the same shape as ADC):\n\n```json\n{ \"type\": \"authorized_user\", \"client_id\": \"\u2026\", \"client_secret\": \"\u2026\", \"refresh_token\": \"\u2026\" }\n```\n\nGenerate it with a scoped `gcloud auth application-default login` written to a\ncustom path (not the default ADC location). Notes:\n\n- The file's `refresh_token` must have been issued by the same `client_id` as the\n route \u2014 a mismatched `client_id` in the file is rejected.\n- `auth.credentials` wins over `proxy.credentials`; the path may start with `~`,\n be absolute, or be relative to the repository root.\n- The `client_secret` comes from the route when set, otherwise from the file.\n- ADC is **never read** for a file-backed route, so gcloud's default client stays\n usable for GCS/Firestore and everything else.\n- The file is read locally at mint time; its contents are never logged. It holds\n a long-lived refresh token and client secret \u2014 keep it private (`chmod 600`).\n\n### Per-service routes\n\nOptional `proxy` on a service is one route fragment or a list. At load they append to the **same** global `proxy.routes` list with stable names (`<service>` or `<service>-<n>`). Duplicate names fail validation. Runtime stays one listener.\n\n```yaml\nservices:\n api:\n command: python main.py\n proxy:\n - match:\n path: /api\n upstream:\n url: http://127.0.0.1:8000\n```\n\n## Expose \u2014 the proxy as a stable entry point\n\nInstead of hand-writing a route, a service can be **exposed** through the proxy at a stable, logical address. The synthesized route addresses its target by service name, so the proxy resolves the service's **current** port at request time \u2014 a service that restarts on a different auto-assigned port is followed with no proxy reload and no consumer change.\n\n```yaml\nproxy:\n enabled: true # expose requires an enabled proxy\n listen: { host: 127.0.0.1, port: 8080 }\nservices:\n invoices-api:\n command: python main.py\n ports: { http: 18000 }\n expose: true # \u2192 route \"invoices-api\", match host invoices-api.local\n```\n\n`expose: true` matches host `<service>.local` and forwards to the service's `http` port. Exposure is host-based: the request path is forwarded verbatim, so a path prefix belongs on a hand-written route, not here. Use the object form to override the host or port:\n\n```yaml\n expose:\n host: api.internal # default: <service>.local\n port: grpc # named port to forward to (default: http)\n```\n\nSet `proxy.gateway: true` to expose **every** HTTP-capable service (one with a port named `http`) at once \u2014 sugar over per-service `expose`. For selective exposure, leave `gateway` off and mark services individually.\n\nA hand-written route or `proxy:` fragment of the same name always wins over a synthesized one, so you can override any auto route (for example to attach auth).\n\n**Auth is always `none` on synthesized routes.** An internal service-to-service hop never silently acquires a service's identity token \u2014 injecting credentials stays an explicit choice you make with a hand-written route.\n\n### Referencing an exposed service \u2014 `${services.<name>.url}`\n\n`${services.<name>.url}` and `${services.<name>.host}` give a service a stable logical address in another service's environment:\n\n```yaml\nservices:\n billing-console:\n environment:\n API_URL: ${services.invoices-api.url}\n```\n\n- **Direct** (target not exposed): resolves to `http://127.0.0.1:<port>` \u2014 a startup snapshot, like `${services.<name>.port}`.\n- **Hub** (target exposed and proxy enabled): resolves to the proxy entry address, e.g. `http://invoices-api.local:8080`. This is stable \u2014 the consumer keeps working even when the target moves to a new port.\n\nHost-based addressing is for **host** clients: `<service>.local` must resolve to `127.0.0.1` on the machine that makes the request \u2014 add it to `/etc/hosts` or your resolver. A container's loopback is isolated from the host proxy, and the container schema has no host-network or extra-hosts mode, so do not point a container at `<service>.local`.\n\n## Token endpoint\n\nOptional `GET /token` (`proxy.token_endpoint`) binds to loopback (never `0.0.0.0` or `::`), requires `X-Devctl-Internal-Token`, and only accepts loopback peers.\n\n```json\n{\n \"access_token\": \"\u2026\",\n \"token_type\": \"Bearer\",\n \"expires_at\": \"2026-08-30T00:05:00.000Z\",\n \"identity\": \"user\"\n}\n```\n\nManaged processes receive `DEVCTL_TOKEN_URL` (rewritten to the bound port after listen) and `DEVCTL_INTERNAL_TOKEN`, not raw tokens in the environment.\n\n## Live request log\n\nThe proxy keeps the last 100 requests in memory \u2014 method, path, matched route (blank for a request that matched no route, still logged as a 404), identity key used, status, duration, and request id \u2014 and reports a running total/error count alongside them. This is part of the regular status snapshot, so it updates the same way everything else in the TUI does: the moment a request refreshes a token or hits a route, the **proxy** tab reflects it without pressing `r` or restarting anything.\n\nPaths are redacted the same way response header values already are, since a query string can carry secrets. Nothing here is persisted \u2014 it's an in-memory ring buffer, reset on daemon restart.\n\n## Request flow\n\n```mermaid\nflowchart TB\n client[\"Local client\"] --> listen[\"127.0.0.1:proxy\"]\n listen --> match[\"Match route: host + path prefix\"]\n match --> ident{\"identity.type\"}\n ident -->|user| adc[\"Application Default Credentials\"]\n ident -->|service_account| sa[\"Impersonated SA token\"]\n adc --> auth{\"auth.type\"}\n sa --> auth\n auth -->|iap| iap[\"IAP ID token for audience\"]\n auth -->|none| inject\n auth -->|service_account| satoken[\"OAuth access token for the SA\"]\n satoken --> inject\n iap --> inject[\"Inject Authorization + X-Devctl-Request-ID\"]\n inject --> up[\"Stream to upstream.url\"]\n```\n\nA missing `identity.type` on an IAP route is a configuration error.\n\n## Related\n\n- [IAP](iap.md)\n- [Impersonation](impersonation.md)\n- [Security](security.md)\n- [TUI](tui.md)\n"},{path:"docs/quickstart.md",title:"Quick start",body:`# Quick start
1726
1798
 
1727
1799
  From a repository that should be orchestrated:
1728
1800
 
@@ -2132,80 +2204,80 @@ Plugins can register extra health types. \`capabilities\` document intent (\`loc
2132
2204
  - [Environment](environment.md)
2133
2205
  - [Configuration](configuration.md)
2134
2206
  - [Logs](logs.md)
2135
- `},{path:"docs/troubleshooting.md",title:"Troubleshooting",body:"# Troubleshooting\n\n| Symptom | What to do |\n|---------|------------|\n| `gcloud` not installed | Install the Cloud SDK only if you need Google identity. Local-only services still run |\n| ADC unavailable | `/auth login` or `gcloud auth application-default login`, then `devctl auth status` / `/auth refresh` |\n| Wrong project | Set `google.project_id` or check `gcloud config get-value core/project`. Identity shows the source |\n| Permission denied | Ask an admin for the missing IAM role; `devctl doctor` names the resource |\n| Cannot impersonate SA | Need `roles/iam.serviceAccountTokenCreator` on that service account (group binding preferred) |\n| IAP authentication failure | Confirm audience, IAP client, and that the identity matches the route. If `client_id` is set, the ADC refresh token must belong to that OAuth client |\n| Port already in use | Doctor lists the holder. Stop a leftover, or change config. Running your own services will also show as \u201Cin use\u201D |\n| Service crashes | Open Logs, filter `ERROR` (`e`), restart with `R` |\n| Health check failure | Confirm the health URL/port; `process` checks only PID liveness |\n| Proxy unavailable / missing listen port | Pin `proxy.listen.port` (required when `proxy.enabled` is true \u2014 validate exits **2**). Then `devctl proxy start` or TUI `n`. Starting with port `0` exits **7**. Bind is loopback only |\n| Token expired | Automatic refresh uses `auth.refresh_threshold_seconds`; run `devctl auth refresh`. Open Doctor if ADC itself expired |\n| Token audience incorrect | Set `auth.audience` on the IAP route; Doctor flags missing audiences |\n| IAP used a user token for an SA route | Confirm the route identity is `service_account`; Doctor probes impersonated IAP separately |\n| Leftover process after crash | Reopen `devctl` \u2014 adopt only when pid + command + cwd + startTime match `~/.devctl/state/<hash>/state.json`. A port-only leftover is never attached |\n| `devctl attach` fails | No supervisor. Use `devctl start` first; attach never starts one |\n| `devctl status` looks empty | If the socket is down, status prints persisted state and exits 0 when nothing is running. The TUI still starts a supervisor; leftover PIDs from the previous session appear on the idle dashboard |\n| Start exits 5 or 6 | 5 = spawn failed; 6 = health never passed. Doctor then Logs |\n| `start` brought up extra services | Empty start uses the active or first profile **alphabetically**, plus dependencies \u2014 YAML key order does not matter (demo: `backend`, not `data`). Pass `--profile` or explicit names to stay narrower |\n| Docker / Podman missing or daemon down | A service in config declares `container` (the demo's `postgres` always does, even if you never start `data`). Install and start that runtime, or drop the service. Default profiles do not start postgres |\n| Doctor offers to kill a busy port that is Docker | It should not: ports owned by a running container service are healthy, and Doctor never offers to terminate the Docker or Podman daemon. Re-run Doctor after the container is up |\n| MCP tools work but nothing starts | `get_status` reports `setup_mode: true` \u2014 there is no `.devctl` yet. Have the agent call `get_setup_guide`, `search_docs`, and `validate_config`, write the files, then `reload_config` |\n| `devctl exec --print-env` hides values | Secret-like names are redacted unless you also pass `--reveal` (TUI: `/exec <svc> --print-env --reveal` or `/reveal`). MCP `exec_service` never reveals them |\n| TUI env pane looks incomplete | It now loads the same resolved map as `devctl exec --print-env`. If the chip says `config fallback`, the daemon `exec` call failed \u2014 check `/daemon` and that the supervisor is up |\n| Supervisor will not start | `devctl daemon logs` or TUI `/daemon` is the bootstrap stderr, not the service log bus |\n| TUI stale / not updating | TUI follows the event bus (20\u201350ms batch). Quit and let a new supervisor start if an old one is still listening |\n| Reload needs a restart | `devctl reload` and `/reload` list services whose command, env, ports, or identity changed |\n| Configuration invalid | `devctl config validate` \u2014 unknown fields, cycles, and missing refs fail closed. TUI `v` / `/buffer` validates before write |\n| Config on disk is broken but the TUI still opens fine | Expected: it attached to an already-running daemon and is showing its `config_snapshot` (last-known-good), not a fresh reparse of the broken file. Fix the file and `/reload` |\n| `devctl update` says unavailable | GitHub Releases API could not be reached. For npm: `npm install --global @amr-m-abdelgawad/devctl@latest`. Homebrew and GitHub binaries: see [installation](installation.md) |\n| MCP agent cannot connect | Listener is off by default. `/mcp` or `devctl mcp --on`. URL is loopback only; snippets include the bearer token |\n| `devctl: command not found` | Run `npm install --global @amr-m-abdelgawad/devctl`, then ensure npm's global binary directory is on `PATH`. See [Installation](installation.md) |\n| Bundled Bun runtime was not installed | Reinstall the npm package without `--ignore-scripts`; Bun uses its install script to select the correct platform runtime |\n\nInternal `devctl` logs appear in the Logs screen with source `devctl`.\n\n## Related\n\n- [Doctor](doctor.md)\n- [Logs](logs.md)\n- [CLI](cli.md)\n- [How it fits together](overview.md)\n"},{path:"docs/tui.md",title:"TUI",body:"# TUI\n\nThe TUI is [OpenTUI](https://opentui.com/docs/) (`@opentui/core` + `@opentui/react`). It attaches to the supervisor and never owns child processes.\n\n```bash\ncd examples/demo-platform\nnpx @amr-m-abdelgawad/devctl@latest\n```\n\nFrom a source checkout (`bun` on PATH):\n\n```bash\ncd examples/demo-platform\nbun run ../../app/src/bin.ts\n```\n\nIf a supervisor session already exists, the TUI attaches to it. Preferences: `tui.json` / `DEVCTL_TUI_CONFIG` \u2014 see [Building from source](typescript.md).\n\n## First run\n\nWith no `.devctl` configuration the TUI opens **setup**: \u201CNo configuration found. Would you like to run setup? **[Enter] Setup [Esc] Exit**\u201D. Enter starts the same 9-step wizard as `devctl setup` (OpenTUI fields, then write and attach the daemon \u2014 no process restart). Invalid existing YAML still refuses overwrite.\n\nIf a `.devctl/config.yaml` exists but fails to parse or validate, the TUI shows **Configuration error** with the actual error instead \u2014 pressing Enter here does not run setup, since that would silently overwrite the file the error is about. Fix the file and restart devctl, or run `devctl config validate` for the same error from the CLI.\n\nWhen services exist but none are running, the dashboard empty state:\n\n- `enter` starts the default profile (first profile name alphabetically) after a plan overlay\n- `n` / `x` start or stop the highlighted row (or the space-selected set)\n- a **lifecycle panel** shows start and stop waves; later start waves wait for health and do not run if a wave fails\n- the panel stays open until `esc` so you can read the result\n- `o` picks a profile, then confirms start\n\n## Quit\n\n`q` / `/exit` / `esc` twice:\n\n| `shutdown.stop_services_on_exit` | Behavior |\n|----------------------------------|----------|\n| `true` | Stop managed services and leave |\n| `false` | Detach immediately |\n| unset | Confirm: `enter` stops services, `d` detaches (daemon stays), `k` stops the daemon and leaves services (`devctl down --keep-services`), `esc` stays |\n\n**Detach** (`d`) leaves the supervisor running. **`/down --keep-services`** (or quit `k`) stops the supervisor and persists PIDs so a later start can adopt them. **`/down`** stops services and the supervisor. `/stop` only stops selected services.\n\n## Interaction model\n\nKeyboard-first. Chords use **command** on macOS and **ctrl** on Linux and Windows. Help, the status bar, and empty-state hints label the modifier for the OS you are on.\n\n| Input | What it does |\n|-------|----------------|\n| `/` | Command overlay \u2014 ranked as you type (name, alias, fuzzy, then description). `/start api` still lists `start`. `\u2191`/`\u2193` to move, `enter` to run |\n| `command+p` / `ctrl+p` | Same command overlay as `/` |\n| `command+x` / `ctrl+x` | Leader key (2s), then a shortcut \u2014 keymap overlay |\n| `?` | Grouped help \u2014 `j`/`k` scroll when the list is taller than the terminal |\n| `tab` / `shift+tab` / `1`\u2013`4` | Cycle or jump the **four nav tabs**. Other screens are `/auth`, `/credentials`, `/doctor`, `/config`, `/profiles`, `/setup`, `/stats`, `/settings`, `/mcp`. On a secondary screen, `tab` returns to the dashboard. When the strip is wider than the terminal it slides (`\u2039` `\u203A`). |\n| `s` `l` `a` `p` `d` `c` `u` | Direct letter nav when no overlay owns keys (services, logs, identity, proxy, doctor, config, setup) |\n| `r` | Refresh snapshot (doctor `r` re-runs checks) |\n| `R` | Restart selected services |\n| `j` `k` / arrows | Move selection |\n| `enter` | Start (empty dashboard) or open service detail |\n| `space` | Multi-select a service |\n| `esc` | Back / close overlay. Twice (when nothing else is open) asks to quit |\n| `f` | Focus log search (`g` jumps to latest). Remap with `keybinds.search` |\n| `z` | Expand logs to fill the terminal. `z` or `esc` exits |\n| `w` | Cycle log wrap: clip, unwrap the selected row, or wrap every long line |\n| `command+c` / `ctrl+c` | Copy the highlighted selection (drag with the mouse). Remap with `keybinds.copy` |\n| `command+=` / `ctrl+=` (and `-` / `0`) | Display size (padding/row height, not the terminal font) |\n| `esc` `esc` | Twice to quit when no overlay or back target is open. The OS copy chord does not quit |\n| Mouse | Click nav, click a service, scroll logs (toggle in Settings) |\n\nThe status bar only lists keys that work **on the current screen**. There is no idle command row \u2014 `/` and the OS palette chord open the command overlay.\n\n## Nav tabs (4)\n\n1. dashboard \xB7 2. services \xB7 3. logs \xB7 4. proxy\n\nEverything else is a slash command (or a letter jump): `/auth`, `/credentials`, `/doctor`, `/config`, `/profiles`, `/setup`, `/stats`, `/settings`. **MCP** is `/mcp`, `/agent`, or Settings \u2192 **MCP \u2192 Settings page**.\n\n## Screens\n\n- **Dashboard** \u2014 services, proxy, live log tail. Identity lives on `/auth`; ADC status is in the header. When nothing is running, a **last session** panel shows leftover PIDs from the previous supervisor (same data `devctl status` prints when the socket is down)\n- **Services** \u2014 list plus a live inspector: status chips, two-column facts, then a scrollable **resolved** env pane (dotenv, profile, secrets, plugins, runtime ports). Narrow terminals stack the panes. `enter` opens the full detail screen\n- **Service detail** \u2014 same inspector; env pane is focused so `j`/`k` scroll. `/reveal` shows secrets. `n`/`x`/`R`/`l`\n- **Logs** \u2014 ANSI color codes are stripped so wrap uses visible width; `w` cycles clip / wrap selected / wrap all. `\\\\` / `/split` opens a second pane on the same live stream (independent service filter, shared search). `/trace <id>` or Enter on a log details request id jumps search to that id. See [Logs](logs.md)\n- **Identity** \u2014 user, project, source, ADC, gcloud, configured SAs, impersonation AVAILABLE/UNAVAILABLE, IAP (no tokens). `/auth login` suspends the TUI, runs `gcloud auth application-default login` on the real terminal, then restores the TUI. `/auth logout` revokes ADC without leaving the screen\n- **Credentials** \u2014 store backend and entry names only. Tokens stay in the OS keychain or `~/.devctl/credentials`\n- **Proxy** \u2014 status + routes (match and upstream wrap instead of clipping); click a route for full details. `n` start / `x` stop\n- **Doctor** \u2014 re-runs on every visit; \u2713 / ! / \u2717 with hints. `enter` on a busy host port asks to stop that process; it never offers to kill the Docker or Podman daemon. `r` reruns\n- **Config** \u2014 merged view including **tasks**. `v` / `/buffer` opens a validate/save overlay on `cfg.configPath` (invalid YAML is not written; `esc` discards). `e` / `/edit` still opens `$EDITOR` / `DEVCTL_EDITOR`. `/diff` shows provenance (`devctl config diff`). `/reload` re-reads after an external edit\n- **Profiles** \u2014 members; `enter` selects and offers start\n- **Setup** \u2014 onboarding checklist. First-run with no config still opens here\n- **Settings** \u2014 grouped prefs: theme, display size, mouse, leader timeout, **MCP settings page**, about, reset. `\u2190`/`\u2192` writes the highlighted cycle or toggles mouse. Reset asks before restoring defaults. Saves to `~/.devctl/tui.json` unless `DEVCTL_TUI_CONFIG` is set\n- **MCP** \u2014 Listen `[ ON ]` / `[ OFF ]`, port stepper `\u2039 N \u203A`, per-agent **Copy JSON** / **Copy TOML**, and a **Tools** list grouped by purpose (inspect, logs, diagnostics, control, setup) with each tool marked `read` or `write`; `space` enables or disables the highlighted one, all on by default. Off by default. See [MCP](mcp.md)\n\n`/reveal` toggles secret env values for this session only. The header shows `secrets shown`.\n\n## Slash commands\n\n```text\n/start [service\u2026] start selection, args, or the current profile\n/stop [service\u2026]\n/down [--keep-services] stop the supervisor (and services unless --keep-services)\n/restart [--cascade|-c] named services only; cascade also restarts dependents\n R with dependents: Enter = named, c = cascade\n/run [task] one-off task; empty /run opens a picker. Output is in Logs under task:<name>\n/exec [service] -- <command\u2026>\n empty /exec opens a service picker, then type the command\n/exec <service> --print-env [--reveal]\n resolved env (dotenv, profile, secrets, plugins, ports), not config-only vars\n/logs /services /auth /credentials /proxy /mcp /doctor /config /profiles /setup\n/stats system and service statistics (sparklines when the supervisor has samples)\n/split second log pane (`\\\\`); `|` focuses the other pane\n/trace <id> set log search to a request_id / trace_id\n/dashboard return home\n/themes [name] picker with live preview; enter saves to ~/.devctl/tui.json\n/settings theme, mouse, display size, MCP page\n/filter toggle ERROR+\n/pause freeze the live log stream\n/reveal show or hide secret env values\n/wrap cycle log wrap (selected / all / clip)\n/copy copy visible logs to the clipboard\n/export [path] write filtered logs to ~/.devctl/exports (or the given path)\n/exports open the export folder\n/regex /since /until /history /edit /buffer\n/clear\n/refresh\n/reload reload .devctl\n/diff winning config sources and what they shadowed (`devctl config diff`)\n/import compose [path] [--write]\n/daemon supervisor bootstrap stderr (`devctl daemon logs`)\n/auth login|logout|refresh\n/update install a newer GitHub Release when the method is known (npm/Homebrew)\n/version current version, then the same update check (does not install)\n/exit /quit /q\n```\n\nAliases include `/up`, `/identity`, `/creds`, `/agent`, `/init`, `/home`, `/prefs`, `/task`, `/provenance`, `/bootstrap`. `/down` is no longer an alias of `/stop`.\n\n## Leader key\n\nDefault leader is `command+x` on macOS and `ctrl+x` elsewhere (2 second timeout). Then:\n\n```text\nn start x stop R restart (c cascade if dependents) s services l logs\na auth p proxy d doctor c config o profiles\nt themes e export r refresh i setup h dashboard\nq quit z fullscreen\n```\n\nOverride in `tui.json` (`keybinds`) or `DEVCTL_TUI_CONFIG`.\n\n## Layout\n\n- **Header** \u2014 product + version as text, then project and profile; chips only for running count, live proxy, MCP when on, ADC, and secrets-shown\n- **Nav** \u2014 the four primary tabs; the active tab is highlighted, not filled\n- **Body** \u2014 dashboard or a focused screen\n- **Command overlay** \u2014 `/` and `command+p` / `ctrl+p` open the same grouped list with a real OpenTUI input\n- **Status bar** \u2014 live/paused, last human result, contextual keys\n\nStatus is never color-only: `\u2713` healthy, `\u25CF` running, `!` warning, `\u2717` failed, `\u25CB` stopped.\n\n## Themes\n\n`/themes` opens a picker with live preview. Built-ins:\n\n- Product: `devctl` (default), `ember`\n- Common dark: `tokyonight`, `catppuccin`, `nord`, `gruvbox`, `kanagawa`, `dracula`, `onedark`, `monokai`, `rose-pine`, `everforest`, `github-dark`, `iceberg`, `ayu-dark`, `oxocarbon`, `night-owl`\n- Light: `catppuccin-latte`, `solarized-light`\n- Other: `solarized-dark`, `terminal` (black + VGA ANSI chrome; aliases `ansi`, `xterm`, `console`), `system` (follows macOS `AppleInterfaceStyle` / `COLORFGBG`; light uses Solarized Light)\n\nAliases: `mocha` \u2192 Catppuccin Mocha, `latte`, `one-dark`, `solarized` (dark), `rosepine`, `github`, `ayu`, `night owl`.\n\nMCP agent chips use brand colors (Claude terracotta, Cursor blue, Kilo gold, Codex green) with light/dark variants.\n\n## Related\n\n- [Logs](logs.md)\n- [MCP](mcp.md)\n- [Building from source](typescript.md)\n- [CLI](cli.md)\n"},{path:"docs/typescript.md",title:"Building from source",body:'# Building from source\n\nThe application lives in `app/`. Runtime is [Bun](https://bun.sh). The TUI is [OpenTUI](https://opentui.com/docs/).\n\n```bash\nexport PATH="$HOME/.bun/bin:$PATH"\ncd app\nbun install\nbun run src/bin.ts --help\nbun test\nbun run check:coverage\nbunx tsc --noEmit\nbun run check:architecture\nbun run check:dead\nbun run check:dup\n```\n\nFrom the repository root (after `bun install` in `app/`):\n\n```bash\nbun run app/src/bin.ts\ncd app && bun link # optional: `devctl` on PATH\n```\n\n## Layout\n\n| Path | Role |\n|------|------|\n| `app/src/bin.ts` | Entry |\n| `app/src/bootstrap/` | Composition roots (daemon + client) |\n| `app/src/application/client-runtime.ts` | Injected CLI/TUI operations and controller contract |\n| `app/src/presentation/cli/` | Commander CLI |\n| `app/src/presentation/tui/` | OpenTUI screens and overlays |\n| `app/src/presentation/tui/hooks/` | Client queries, command execution, and TUI state |\n| `app/src/presentation/tui/helpers/` | Screen-specific formatting, navigation, logs, and plan helpers |\n| `app/src/presentation/mcp/` | Streamable HTTP MCP server |\n| `app/src/domain/` | Service, identity, config, log, session, and preference types and policies |\n| `app/src/adapters/config/` | Discover, decode, merge, validate |\n| `app/src/adapters/process/` | Host process runtime |\n| `app/src/adapters/google/` | Google / IAP / tokens |\n| `app/src/adapters/daemon/supervisor.ts` | Daemon host (socket, recovery, watch, listeners) |\n| `app/src/adapters/rpc/controller.ts` | Local supervisor or socket / named-pipe client |\n| `app/tui.json` | Starter TUI preferences |\n\nLayer rules: [architecture.md](architecture.md). Check with `bun run check:architecture`. Coverage: `bun run check:coverage` (aggregate funcs/lines; OpenTUI screens and hooks are ignored). Unused files and dependencies: `bun run check:dead` ([Knip](https://knip.dev)). Copy-paste clones: `bun run check:dup` ([jscpd](https://jscpd.dev)).\n\nThere is no separate Go tree.\n\n## TUI preferences\n\nConfiguration is **`tui.json` or `tui.jsonc`**: `theme`, `keybinds`, `leader_timeout`, `font_size`, `mouse`, `scroll_speed`, `log_timestamps`, `log_metadata`, `mcp_enabled`, `mcp_port`, `mcp_disabled_tools`.\n\n`mcp_disabled_tools` is a deny-list of MCP tool names (empty means every tool is available). See [MCP](mcp.md).\n\nSearch order:\n\n```mermaid\nflowchart TB\n env{"DEVCTL_TUI_CONFIG<br/>or OPENCODE_TUI_CONFIG exists?"}\n env -->|yes| only["Use that file only"]\n env -->|no| first["First existing of<br/>./tui.jsonc \xB7 .devctl/tui.jsonc \xB7 ~/.devctl/tui.jsonc"]\n first --> merge{"~/.devctl/tui.json is a different path?"}\n merge -->|yes| overlay["Merge user file on top"]\n merge -->|no| done["Use that file"]\n```\n\nSettings writes go to `~/.devctl/tui.json` unless the env override is set (then changes apply for this session only).\n\n`keybinds` merge with the built-in defaults, so you only override what you change. Defaults use `cmd` on macOS and `ctrl` on Linux/Windows (`command+c` / `ctrl+c` in the TUI).\n\n```json\n{\n "theme": "devctl",\n "leader_timeout": 2000,\n "keybinds": {\n "leader": "ctrl+x",\n "command_list": "ctrl+p"\n },\n "mouse": true,\n "mcp_enabled": false,\n "mcp_disabled_tools": []\n}\n```\n\n## Tests\n\n```bash\ncd app && bun test\n```\n\nIntegration tests that need Google stay skipped unless credentials are present.\n\n## Related\n\n- [Installation](installation.md)\n- [TUI](tui.md)\n- [How it fits together](overview.md)\n- [Contributing](../CONTRIBUTING.md)\n'}]});function OA(){return[{path:"skills/devctl-onboard/SKILL.md",title:"Onboard procedure",body:Tn.procedure},{path:"skills/devctl-onboard/references/authoring.md",title:"Onboard authoring",body:Tn.authoring},{path:"skills/devctl-onboard/references/discovery.md",title:"Onboard discovery",body:Tn.discovery}]}function $A(e){return e.toLowerCase().split(/[^a-z0-9_]+/).filter((t)=>t.length>=IA)}function FA(e,t){let r=e.title.toLowerCase(),n=e.path.toLowerCase(),o=e.body.toLowerCase(),i=0;for(let a of t){if(r.includes(a))i+=TA;if(n.includes(a))i+=AA;let c=o.split(a).length-1;i+=Math.min(c,LA)}let s=t.join(" ");if(s.length>=MA&&o.includes(s))i+=DA;return i}function NA(e,t){let r=e.split(`
2136
- `),n=0,o=-1;for(let s=0;s<r.length;s++){let a=r.slice(s,s+By).join(`
2137
- `).toLowerCase(),c=0;for(let l of t)if(a.includes(l))c+=1;if(c>o)o=c,n=s}let i=r.slice(n,n+By).join(`
2138
- `).trim();if(i.length<=jy)return i;return`${i.slice(0,jy).trimEnd()}\u2026`}function HA(e){if(e===void 0||Number.isNaN(e))return _A;return Math.min(EA,Math.max(1,Math.floor(e)))}function Uy(e,t){let r=e.trim(),n=$A(r);if(n.length===0)throw Error("query is required");let o=HA(t),i=[...Ny,...OA()].map((s)=>({path:s.path,title:s.title,score:FA(s,n),snippet:NA(s.body,n)})).filter((s)=>s.score>0).sort((s,a)=>a.score-s.score||s.path.localeCompare(a.path)).slice(0,o);return{query:r,hits:i}}var _A=5,EA=10,TA=12,AA=8,LA=8,DA=15,jy=400,By=5,IA=2,MA=4;var Wy=D(()=>{op();Hy()});import{existsSync as jA,readFileSync as BA}from"fs";function An(e,t){return!(t??[]).includes(e)}function Gy(e){return Pc.filter((t)=>An(t.name,e))}function Rc(e){return Pc.some((t)=>t.name===e)}function ds(e){return new mr(e.secrets.extra_markers,e.secrets.extra_patterns)}function Ky(e){return Object.values(e.services).map((t)=>({name:t.name,state:t.state,health:t.health,ports:t.ports,pid:t.pid,last_error:t.last_error}))}function WA(e,t){let r=e.config(),n=r.services[t];if(!n)throw Error(`unknown service ${t}`);let i=e.status().services[t],s=ds(r);return{name:t,state:i?.state,health:i?.health,pid:i?.pid,last_error:i?.last_error,command:n.command.args,cwd:n.working_dir,ports:i?.ports??Object.fromEntries(n.ports.map((a)=>[a.name,a.value])),environment:s.redactMap({...n.environment.defaults,...n.environment.vars}),container:n.container?{...n.container,env:s.redactMap(n.container.env)}:void 0}}function qy(e){return{session_id:e.session_id,repo_root:e.repo_root,profile:e.profile,setup_mode:e.setup_mode===!0?!0:void 0,identity:{user:e.identity.user,project:e.identity.project,adc:e.identity.adc,iap:e.identity.iap,service_accounts:e.identity.service_accounts,service_account_status:e.identity.service_account_status},proxy:{running:e.proxy.running,address:e.proxy.address,routes:e.proxy.routes},logs:e.logs,mcp:e.mcp?{running:e.mcp.running,address:e.mcp.address,port:e.mcp.port}:{running:!1}}}async function Vy(e,t){let r=typeof t.service==="string"?t.service:"",n=typeof t.since==="string"?t.since:"",o=typeof t.cursor==="string"?t.cursor:void 0,i=await e.logsPage({services:r===""?[]:[r],level:typeof t.level==="string"?t.level:"",search:typeof t.search==="string"?t.search:"",source:typeof t.source==="string"?t.source:"",since:n,until:typeof t.until==="string"?t.until:"",cursor:o,direction:o?"forward":void 0,limit:UA}),s=ds(e.config());return{events:i.events.map((a)=>({timestamp:a.timestamp,service:a.service,source:a.source,level:a.level,message:s.redactText(a.message),pid:a.pid})),truncated:i.hasPrev,has_more:i.hasNext,next_since:i.events[i.events.length-1]?.timestamp??n,next_cursor:i.nextCursor,session_changed:i.sessionChanged}}function GA(e){return Object.entries(e.profiles).sort(([t],[r])=>t.localeCompare(r)).map(([t,r])=>({name:t,services:r.services}))}function kc(e){return e.trim()===""?void 0:e}function KA(e){let t={name:e.name,match:e.match,upstream:e.upstream.url,auth:e.auth.type},r=kc(e.auth.identity.type),n=kc(e.auth.audience),o=kc(e.auth.identity.service_account||e.auth.service_account),i=kc(e.auth.client_id),s=Ha(e.auth.client_secret);if(r)t.identity=r;if(n)t.audience=n;if(o)t.service_account=o;if(i)t.client_id=i;if(s)t.client_secret=s;return t}function zy(e){let t=Object.entries(e.services).map(([r,n])=>({name:r,description:n.description,command:n.command.args,cwd:n.working_dir,ports:n.ports.map((o)=>({name:o.name,value:o.auto?0:o.value,auto:o.auto})),dependencies:n.dependencies,container:n.container?{image:n.container.image,runtime:n.container.runtime||"docker",ports:n.container.ports,volumes:n.container.volumes}:void 0}));return{project:e.project.name,config_path:e.configPath,repo_root:e.repoRoot,services:t,proxy:{enabled:e.proxy.enabled,listen:{host:e.proxy.listen.host,port:e.proxy.listen.port},routes:e.proxy.routes.map((r)=>KA(r))}}}function qA(e){let t=ds(e);return{entries:Pn(e).map((r)=>{let n=typeof r.value==="string"?r.value:JSON.stringify(r.value);return{...r,value:t.redactMap({[r.path]:n})[r.path]}})}}function Cc(e){if(!Array.isArray(e))return[];return e.filter((t)=>typeof t==="string")}async function Yy(e,t,r){switch(t){case"list_services":return Ky(e.status());case"get_service":if(typeof r.name!=="string"||r.name==="")throw Error("name is required");return WA(e,r.name);case"get_status":return qy(e.status());case"get_logs":return Vy(e,r);case"list_profiles":return GA(e.config());case"get_config":return zy(e.config());case"get_config_sources":return qA(e.config());case"run_doctor":return e.doctor();case"start_services":return e.start({services:Cc(r.services),profile:typeof r.profile==="string"&&r.profile!==""?r.profile:void 0});case"stop_services":return await e.stop(Cc(r.services)),{ok:!0};case"restart_services":return await e.restart(Cc(r.services),r.cascade===!0),{ok:!0};case"reload_config":return e.reload();case"run_task":{if(typeof r.name!=="string"||r.name==="")throw Error("name is required");let n=await e.runTask(r.name),o=ds(e.config());return{...n,stdout:o.redactText(n.stdout),stderr:o.redactText(n.stderr)}}case"start_proxy":return await e.startProxy(),{ok:!0};case"stop_proxy":return await e.stopProxy(),{ok:!0};case"exec_service":{if(typeof r.service!=="string"||r.service==="")throw Error("service is required");if(!e.exec)throw Error("exec is unavailable");let n=await e.exec(r.service,Cc(r.command),r.print_env===!0),o=ds(e.config());return{...n,stdout:o.redactText(n.stdout),stderr:o.redactText(n.stderr),environment:n.environment?o.redactMap(n.environment):void 0}}case"get_setup_guide":return VA(r);case"search_docs":return Uy(typeof r.query==="string"?r.query:"",typeof r.limit==="number"?r.limit:void 0);case"validate_config":return zA(e,r);default:throw Error(`unknown tool ${t}`)}}function VA(e){let t=typeof e.section==="string"?e.section:"procedure",r=t in Tn?t:"procedure";return{section:r,text:Tn[r],sections:Object.keys(Tn)}}function zA(e,t){let r=e.config();if(typeof t.text==="string"){let o=e.validateConfigText(t.text);return{valid:o.length===0,issues:o,source:"candidate",config_path:r.configPath}}if(!jA(r.configPath))return{valid:!1,issues:[`no configuration at ${r.configPath}`],source:"disk",config_path:r.configPath,setup_mode:!0};let n=e.validateConfigText(BA(r.configPath,"utf8"));return{valid:n.length===0,issues:n,source:"disk",config_path:r.configPath}}function Jy(e){return ip.includes(e)}async function Xy(e,t){switch(t){case"devctl://status":return qy(e.status());case"devctl://services":return Ky(e.status());case"devctl://logs":return Vy(e,{});case"devctl://config":return zy(e.config());case"devctl://doctor":return e.doctor();default:throw Error(`unknown resource ${t}`)}}var UA=200,ip,sp,Pc;var us=D(()=>{ni();Wy();op();ip=["devctl://status","devctl://services","devctl://logs","devctl://config","devctl://doctor"],sp=["inspect","logs","diagnostics","control","setup"],Pc=[{name:"list_services",label:"List services",summary:"Name, state, health, ports, pid",category:"inspect",description:"List services with state, health, ports, pid, and last error",inputSchema:{type:"object",properties:{},additionalProperties:!1}},{name:"get_service",label:"Service detail",summary:"One service with command, cwd, ports",category:"inspect",description:"One service plus command, cwd, and ports. Environment values are redacted.",inputSchema:{type:"object",properties:{name:{type:"string"}},required:["name"],additionalProperties:!1}},{name:"get_status",label:"Status",summary:"Profile, identity, proxy, log counts",category:"inspect",description:"Profile, session, identity flags (no tokens), proxy, and log counts",inputSchema:{type:"object",properties:{},additionalProperties:!1}},{name:"get_logs",label:"Read logs",summary:"Filtered log pages, secrets redacted",category:"logs",description:"Recent log lines, optionally filtered. Capped at 200 events per page. Secrets are redacted. Pass cursor=next_cursor to page forward with no duplicate or same-millisecond-lost events; since/until are plain timestamp filters for a fresh query, not a follow cursor.",inputSchema:{type:"object",properties:{service:{type:"string"},level:{type:"string"},search:{type:"string"},source:{type:"string"},since:{type:"string",description:"Only events at or after this timestamp"},until:{type:"string",description:"Only events at or before this timestamp"},cursor:{type:"string",description:"Opaque cursor from a previous response's next_cursor; continues forward from exactly there"}},additionalProperties:!1}},{name:"list_profiles",label:"List profiles",summary:"Configured profiles and their members",category:"inspect",description:"Configured profiles and their member services",inputSchema:{type:"object",properties:{},additionalProperties:!1}},{name:"get_config",label:"Read config",summary:"Merged project summary, no secret values",category:"inspect",description:"Merged project summary: services, routes, and proxy listen paths. Route auth includes type, identity, audience, and optional IAP client_id. client_secret is returned only as a ${ENV} template, never a resolved secret.",inputSchema:{type:"object",properties:{},additionalProperties:!1}},{name:"get_config_sources",label:"Config sources",summary:"Winning and shadowed config sources",category:"inspect",description:"List effective configuration values with their winning source/layer and any shadowed sources. Secret-like values are redacted.",inputSchema:{type:"object",properties:{},additionalProperties:!1}},{name:"run_doctor",label:"Run doctor",summary:"Environment diagnostics",category:"diagnostics",description:"Run environment diagnostics",inputSchema:{type:"object",properties:{},additionalProperties:!1}},{name:"start_services",label:"Start services",summary:"Start named services or a profile",category:"control",mutates:!0,description:"Start named services, or a profile when names are omitted. Empty start uses profile, then the active session profile, then the first configured profile \u2014 never every service.",inputSchema:{type:"object",properties:{services:{type:"array",items:{type:"string"}},profile:{type:"string",description:"Profile to start when services is omitted"}},additionalProperties:!1}},{name:"stop_services",label:"Stop services",summary:"Stop services and their dependents",category:"control",mutates:!0,description:"Stop named services, or all started services when names are omitted. Also stops every service that transitively depends on a named one (never a named service's own dependencies, which other running services may still need).",inputSchema:{type:"object",properties:{services:{type:"array",items:{type:"string"}}},additionalProperties:!1}},{name:"restart_services",label:"Restart services",summary:"Restart services, optionally cascading",category:"control",mutates:!0,description:"Restart named services. By default this touches only the named services, not anything that depends on them. Pass cascade=true to also restart their transitive dependents (the same set stop_services would affect).",inputSchema:{type:"object",properties:{services:{type:"array",items:{type:"string"}},cascade:{type:"boolean",description:"Also restart transitive dependents; default false restarts only the named services"}},additionalProperties:!1}},{name:"reload_config",label:"Reload config",summary:"Re-read .devctl configuration",category:"control",mutates:!0,description:"Reload .devctl configuration",inputSchema:{type:"object",properties:{},additionalProperties:!1}},{name:"run_task",label:"Run task",summary:"Run a named one-off task",category:"control",mutates:!0,description:"Run a named task from configuration. Output is also written to the log ring as task:<name>.",inputSchema:{type:"object",properties:{name:{type:"string"}},required:["name"],additionalProperties:!1}},{name:"start_proxy",label:"Start proxy",summary:"Start the local reverse proxy",category:"control",mutates:!0,description:"Start the local reverse proxy. Same as CLI `devctl proxy start` and TUI start on the proxy screen.",inputSchema:{type:"object",properties:{},additionalProperties:!1}},{name:"stop_proxy",label:"Stop proxy",summary:"Stop the local reverse proxy",category:"control",mutates:!0,description:"Stop the local reverse proxy and leave it suppressed until start_proxy (or CLI/TUI start) runs again.",inputSchema:{type:"object",properties:{},additionalProperties:!1}},{name:"exec_service",label:"Execute in service",summary:"Run a command in a resolved service context",category:"control",mutates:!0,description:"Run an arbitrary command with a service's fully resolved environment and working directory, whether or not it is running. Output and print_env values are redacted.",inputSchema:{type:"object",properties:{service:{type:"string"},command:{type:"array",items:{type:"string"}},print_env:{type:"boolean",description:"Return the resolved environment without executing a command"}},required:["service"],additionalProperties:!1}},{name:"get_setup_guide",label:"Setup guide",summary:"How to author a .devctl for this repo",category:"setup",description:"The devctl onboarding guide: how to survey a repository and author a .devctl configuration for it. Read section=procedure first; read section=authoring BEFORE writing any YAML (it carries the rules the loader rejects on, which the JSON Schema does not state); read section=discovery for mapping compose/package.json/pyproject/Terraform/k8s/.env to services. Write the files with your own editing tools \u2014 this server does not write them \u2014 then check your work with validate_config.",inputSchema:{type:"object",properties:{section:{type:"string",enum:["procedure","authoring","discovery"],description:"Which part of the guide to return; defaults to procedure"}},additionalProperties:!1}},{name:"search_docs",label:"Search docs",summary:"Search the embedded product documentation",category:"setup",description:"Search the compiled-in product documentation (docs/*.md plus the onboarding skill). Returns ranked pages with short snippets. Use this for IAP, proxy, MCP, configuration, and similar topics; use get_setup_guide for the full onboarding procedure.",inputSchema:{type:"object",properties:{query:{type:"string",description:"Keywords or a short phrase to search for"},limit:{type:"integer",description:"Maximum hits to return (default 5, max 10)"}},required:["query"],additionalProperties:!1}},{name:"validate_config",label:"Validate config",summary:"Check configuration or a draft for errors",category:"setup",description:"Validate devctl configuration and return the exact issues the loader would report. With no arguments, validates what is on disk. Pass text to validate a candidate config.yaml before writing it \u2014 the candidate is run through the real load pipeline (modular services/profiles, overlays, templates), so it works even when no configuration exists yet. This is the only way to validate over MCP; there is no CLI round-trip needed.",inputSchema:{type:"object",properties:{text:{type:"string",description:"Candidate config.yaml contents to validate instead of the file on disk"}},additionalProperties:!1}}]});function ox(e){return{...e,runDoctor:(t,r,n)=>e.runDoctor.execute(t,r,n),startupPlan:(t,r,n)=>e.getStartupPlan.execute(t,r,n),shutdownPlan:(t,r)=>e.getShutdownPlan.execute(t,r),shutdownPlanExact:(t,r)=>nc(t,r),resolveStartRequest:(t,r)=>e.resolveStart.execute(t,{...r,profile:r.profile?ur(r.profile):void 0,activeProfile:r.activeProfile===void 0?void 0:ur(r.activeProfile)})}}var ix=D(()=>{Tt()});function Ec(e){let t=e.toLowerCase().trim(),r=aL[t];if(r)return r;return St.includes(t)?t:"devctl"}function cL(){if(process.env.DEVCTL_THEME_LIGHT==="1")return!0;if(process.env.DEVCTL_THEME_LIGHT==="0")return!1;if(process.platform==="darwin")try{let t=Bun.spawnSync(["defaults","read","-g","AppleInterfaceStyle"],{stdout:"pipe",stderr:"pipe"}),r=t.stdout.toString();if(t.exitCode===0&&r.toLowerCase().includes("dark"))return!1;if(t.exitCode!==0)return!0}catch{}let e=process.env.COLORFGBG??"";if(e.includes(";15")||e.endsWith(";7"))return!0;if(e.includes(";0")||e.endsWith(";8"))return!1;return!1}function sx(e){let t=Ec(e);if(t==="system"){if(cL())return{...cp["solarized-light"],name:"system"};return cp.system}return cp[t]}function hr(e){switch(e.toUpperCase()){case"HEALTHY":case"OK":return"\u2713";case"UNHEALTHY":case"WARNING":case"WARN":return"!";case"FAILED":case"ERROR":return"\u2717";case"STOPPED":case"UNKNOWN":return"\u25CB";case"RUNNING":case"STARTING":case"RESTARTING":return"\u25CF";default:return"\u25CB"}}function ho(e,t){return gs(e)>=hs?Tc:t}function gs(e){let t=e.startsWith("#")?e.slice(1):e,r=Number.parseInt(t,vL);if(Number.isNaN(r))return 0;let n=r>>yL&lp,o=r>>xL&lp,i=r&lp;return(n*fL+o*mL+i*hL)/gL}function bL(e){let t=uL;for(let r=0;r<e.length;r+=1)t^=e.charCodeAt(r),t=Math.imul(t,pL);return t>>>0}function Ac(e){return gs(e.background)>=hs}function up(e){if(!e||!/^#[0-9a-f]{6}$/i.test(e))return!1;return gs(e)<hs}function Lc(e,t){let r=wL[e];return Ac(t)?r.light:r.dark}function ax(e,t){return gs(Lc(e,t))>=hs?SL:kL}function Ze(e,t){if(e===""||e==="all")return t.muted;let r=gs(t.background)>=hs?dL:lL;return r[bL(e)%r.length]??t.info}function CL(e,t){switch(t.toUpperCase()){case"ERROR":case"FATAL":return e.error;case"WARN":case"WARNING":return e.warning;case"INFO":return e.text;case"DEBUG":case"TRACE":return e.muted;default:return e.text}}function cx(e,t,r){if(r==="search")return e.search;if(r==="string")return e.accent;if(r==="number")return e.info;if(r==="keyword")return e.error;return CL(e,t)}function et(e,t){switch(t.toUpperCase()){case"HEALTHY":case"OK":return e.success;case"UNHEALTHY":case"WARNING":case"WARN":return e.warning;case"FAILED":case"ERROR":return e.error;case"RUNNING":case"STARTING":case"RESTARTING":return e.primary;case"INFO":return e.info;case"FATAL":return e.error;case"DEBUG":case"TRACE":case"UNKNOWN":return e.muted;default:return e.muted}}var St,dp,cp,aL,lL,dL,uL=2166136261,pL=16777619,fL=299,mL=587,hL=114,gL=1000,hs=160,vL=16,lp=255,yL=16,xL=8,Tc="#1A1A1A",wL,SL="#1A1A1A",kL="#F5F5F5";var tt=D(()=>{St=["devctl","ember","tokyonight","catppuccin","catppuccin-latte","nord","gruvbox","kanagawa","dracula","onedark","monokai","solarized-dark","solarized-light","rose-pine","everforest","github-dark","iceberg","ayu-dark","oxocarbon","night-owl","terminal","voltage","voltage-light","ironclad","ironclad-light","signal","signal-light","system"],dp={devctl:"forest + mint, website palette",ember:"warm orange",tokyonight:"Tokyo Night storm blues",catppuccin:"Catppuccin Mocha","catppuccin-latte":"Catppuccin Latte (light)",nord:"Arctic Nord frost",gruvbox:"Gruvbox dark hard",kanagawa:"Kanagawa wave / gold",dracula:"classic Dracula purple",onedark:"Atom / VS Code One Dark",monokai:"Monokai Sublime","solarized-dark":"Solarized dark","solarized-light":"Solarized light","rose-pine":"Ros\xE9 Pine moon",everforest:"Everforest dark medium","github-dark":"GitHub dimmed canvas",iceberg:"Iceberg navy","ayu-dark":"Ayu Mirage-adjacent dark",oxocarbon:"IBM Oxocarbon","night-owl":"Sarah Drasner Night Owl",terminal:"black + VGA ANSI chrome",voltage:"max contrast, electric neon (dark)","voltage-light":"max contrast, electric neon (light)",ironclad:"max contrast, steel & amber (dark)","ironclad-light":"max contrast, steel & amber (light)",signal:"max contrast, traffic-light semantics (dark)","signal-light":"max contrast, traffic-light semantics (light)",system:"neutral dark (system default)"},cp={tokyonight:{name:"tokyonight",primary:"#82AAFF",accent:"#C792EA",error:"#FF5370",warning:"#FFCB6B",search:"#FFCB6B",success:"#C3E88D",info:"#89DDFF",text:"#F4F7FF",muted:"#A8B2CC",background:"#0F111A",panel:"#1A1B2E",element:"#24283B",highlight:"#2E3350",inverse:"#0F111A",border:"#3D4466",borderActive:"#82AAFF"},catppuccin:{name:"catppuccin",primary:"#89B4FA",accent:"#F5C2E7",error:"#F38BA8",warning:"#F9E2AF",search:"#F9E2AF",success:"#A6E3A1",info:"#89DCEB",text:"#E4E8F7",muted:"#A6A9BE",background:"#11111B",panel:"#1E1E2E",element:"#313244",highlight:"#45475A",inverse:"#11111B",border:"#585B70",borderActive:"#89B4FA"},"catppuccin-latte":{name:"catppuccin-latte",primary:"#1E66F5",accent:"#EA76CB",error:"#D20F39",warning:"#DF8E1D",search:"#DF8E1D",success:"#40A02B",info:"#179299",text:"#4C4F69",muted:"#6C6F85",background:"#EFF1F5",panel:"#E6E9EF",element:"#DCE0E8",highlight:"#CCD0DA",inverse:"#EFF1F5",border:"#9CA0B0",borderActive:"#1E66F5"},nord:{name:"nord",primary:"#88C0D0",accent:"#B48EAD",error:"#BF616A",warning:"#EBCB8B",search:"#EBCB8B",success:"#A3BE8C",info:"#8FBCBB",text:"#F2F5FA",muted:"#A4AEBF",background:"#2E3440",panel:"#3B4252",element:"#434C5E",highlight:"#4C566A",inverse:"#2E3440",border:"#5E6A7D",borderActive:"#88C0D0"},gruvbox:{name:"gruvbox",primary:"#FE8019",accent:"#FABD2F",error:"#FB4934",warning:"#FE8019",search:"#FE8019",success:"#B8BB26",info:"#8EC07C",text:"#F2E5BC",muted:"#C4B394",background:"#1D2021",panel:"#282828",element:"#3C3836",highlight:"#504945",inverse:"#1D2021",border:"#665C54",borderActive:"#FE8019"},kanagawa:{name:"kanagawa",primary:"#7FB4CA",accent:"#E6C384",error:"#E82424",warning:"#FFA066",search:"#FFA066",success:"#98BB6C",info:"#7FB4CA",text:"#E8E4C9",muted:"#B4C0D6",background:"#16161D",panel:"#1F1F28",element:"#2A2A37",highlight:"#363646",inverse:"#16161D",border:"#54546D",borderActive:"#E6C384"},dracula:{name:"dracula",primary:"#BD93F9",accent:"#FF79C6",error:"#FF5555",warning:"#F1FA8C",search:"#F1FA8C",success:"#50FA7B",info:"#8BE9FD",text:"#F8F8F2",muted:"#8B9DC9",background:"#21222C",panel:"#282A36",element:"#343746",highlight:"#44475A",inverse:"#21222C",border:"#6272A4",borderActive:"#BD93F9"},onedark:{name:"onedark",primary:"#61AFEF",accent:"#C678DD",error:"#E06C75",warning:"#E5C07B",search:"#E5C07B",success:"#98C379",info:"#56B6C2",text:"#ABB2BF",muted:"#9DA5B4",background:"#21252B",panel:"#282C34",element:"#2C323C",highlight:"#3E4451",inverse:"#21252B",border:"#4B5263",borderActive:"#61AFEF"},monokai:{name:"monokai",primary:"#66D9EF",accent:"#AE81FF",error:"#F92672",warning:"#E6DB74",search:"#E6DB74",success:"#A6E22E",info:"#66D9EF",text:"#F8F8F2",muted:"#A39E80",background:"#1D1E19",panel:"#272822",element:"#3E3D32",highlight:"#49483E",inverse:"#1D1E19",border:"#75715E",borderActive:"#F92672"},"solarized-dark":{name:"solarized-dark",primary:"#268BD2",accent:"#6C71C4",error:"#DC322F",warning:"#B58900",search:"#B58900",success:"#859900",info:"#2AA198",text:"#93A1A1",muted:"#586E75",background:"#002B36",panel:"#073642",element:"#0A4A58",highlight:"#1A5A68",inverse:"#002B36",border:"#586E75",borderActive:"#268BD2"},"solarized-light":{name:"solarized-light",primary:"#268BD2",accent:"#D33682",error:"#DC322F",warning:"#CB4B16",search:"#CB4B16",success:"#859900",info:"#2AA198",text:"#586E75",muted:"#839496",background:"#FDF6E3",panel:"#EEE8D5",element:"#E4DCC8",highlight:"#D5CDB6",inverse:"#FDF6E3",border:"#93A1A1",borderActive:"#268BD2"},"rose-pine":{name:"rose-pine",primary:"#C4A7E7",accent:"#EBBCBA",error:"#EB6F92",warning:"#F6C177",search:"#F6C177",success:"#31748F",info:"#9CCFD8",text:"#E0DEF4",muted:"#908CAA",background:"#191724",panel:"#1F1D2E",element:"#26233A",highlight:"#403D52",inverse:"#191724",border:"#6E6A86",borderActive:"#C4A7E7"},everforest:{name:"everforest",primary:"#7FBBB3",accent:"#D699B6",error:"#E67E80",warning:"#DBBC7F",search:"#DBBC7F",success:"#A7C080",info:"#83C092",text:"#D3C6AA",muted:"#9DA9A0",background:"#232A2E",panel:"#2D353B",element:"#343F44",highlight:"#3D484D",inverse:"#232A2E",border:"#4F585E",borderActive:"#A7C080"},"github-dark":{name:"github-dark",primary:"#58A6FF",accent:"#A371F7",error:"#F85149",warning:"#D29922",search:"#D29922",success:"#3FB950",info:"#79C0FF",text:"#E6EDF3",muted:"#8B949E",background:"#0D1117",panel:"#161B22",element:"#21262D",highlight:"#30363D",inverse:"#0D1117",border:"#30363D",borderActive:"#58A6FF"},iceberg:{name:"iceberg",primary:"#84A0C6",accent:"#A093C7",error:"#E27878",warning:"#E2A478",search:"#E2A478",success:"#B4BE82",info:"#89B8C2",text:"#C6C8D1",muted:"#9AA0B8",background:"#161821",panel:"#1E2132",element:"#2A3150",highlight:"#3D425B",inverse:"#161821",border:"#444B71",borderActive:"#84A0C6"},"ayu-dark":{name:"ayu-dark",primary:"#59C2FF",accent:"#FFB454",error:"#D95757",warning:"#FF8F40",search:"#FF8F40",success:"#C2D94C",info:"#95E6CB",text:"#B3B1AD",muted:"#8B939C",background:"#0A0E14",panel:"#0D1017",element:"#131721",highlight:"#1B212C",inverse:"#0A0E14",border:"#2D3640",borderActive:"#FFB454"},oxocarbon:{name:"oxocarbon",primary:"#78A9FF",accent:"#EE5396",error:"#FA4D56",warning:"#F1C21B",search:"#F1C21B",success:"#42BE65",info:"#3DDBD9",text:"#F2F4F8",muted:"#A8A8A8",background:"#161616",panel:"#262626",element:"#393939",highlight:"#525252",inverse:"#161616",border:"#6F6F6F",borderActive:"#78A9FF"},"night-owl":{name:"night-owl",primary:"#82AAFF",accent:"#C792EA",error:"#EF5350",warning:"#ECC48D",search:"#ECC48D",success:"#ADDB67",info:"#7FDBCA",text:"#D6DEEB",muted:"#8FA4A4",background:"#011627",panel:"#0B2942",element:"#1D3B53",highlight:"#1E4B6B",inverse:"#011627",border:"#5F7E97",borderActive:"#82AAFF"},terminal:{name:"terminal",primary:"#55FFFF",accent:"#FF55FF",error:"#FF5555",warning:"#FFFF55",search:"#FFFF55",success:"#55FF55",info:"#5555FF",text:"#C0C0C0",muted:"#808080",background:"#000000",panel:"#000000",element:"#000000",highlight:"#404040",inverse:"#000000",border:"#555555",borderActive:"#55FFFF"},voltage:{name:"voltage",primary:"#00E5FF",accent:"#FF3EA5",error:"#FF1744",warning:"#FFEA00",search:"#FFEA00",success:"#00E676",info:"#40C4FF",text:"#FFFFFF",muted:"#C0C0C0",background:"#000000",panel:"#000000",element:"#0D0D0D",highlight:"#262626",inverse:"#000000",border:"#808080",borderActive:"#FFFFFF"},"voltage-light":{name:"voltage-light",primary:"#0057B7",accent:"#C2185B",error:"#D50000",warning:"#FF6D00",search:"#FF6D00",success:"#2E7D32",info:"#00838F",text:"#000000",muted:"#424242",background:"#FFFFFF",panel:"#FFFFFF",element:"#EDEDED",highlight:"#D6D6D6",inverse:"#FFFFFF",border:"#616161",borderActive:"#000000"},ironclad:{name:"ironclad",primary:"#4FC3F7",accent:"#FFD54F",error:"#FF5252",warning:"#FFB300",search:"#FFB300",success:"#69F0AE",info:"#64B5F6",text:"#FFFFFF",muted:"#BDBDBD",background:"#000000",panel:"#000000",element:"#101010",highlight:"#242424",inverse:"#000000",border:"#757575",borderActive:"#4FC3F7"},"ironclad-light":{name:"ironclad-light",primary:"#01579B",accent:"#E65100",error:"#B71C1C",warning:"#EF6C00",search:"#EF6C00",success:"#1B5E20",info:"#0277BD",text:"#000000",muted:"#4A4A4A",background:"#FFFFFF",panel:"#FFFFFF",element:"#EFEFEF",highlight:"#DADADA",inverse:"#FFFFFF",border:"#6E6E6E",borderActive:"#01579B"},signal:{name:"signal",primary:"#29B6F6",accent:"#AB47BC",error:"#FF0000",warning:"#FFC400",search:"#FFC400",success:"#00E400",info:"#00B8D4",text:"#FFFFFF",muted:"#B3B3B3",background:"#000000",panel:"#000000",element:"#0A0A0A",highlight:"#202020",inverse:"#000000",border:"#8A8A8A",borderActive:"#29B6F6"},"signal-light":{name:"signal-light",primary:"#01579B",accent:"#6A1B9A",error:"#C62828",warning:"#F9A825",search:"#F9A825",success:"#2E7D32",info:"#00796B",text:"#000000",muted:"#3D3D3D",background:"#FFFFFF",panel:"#FFFFFF",element:"#ECECEC",highlight:"#D2D2D2",inverse:"#FFFFFF",border:"#5C5C5C",borderActive:"#01579B"},system:{name:"system",primary:"#60A5FA",accent:"#C084FC",error:"#F87171",warning:"#FBBF24",search:"#FBBF24",success:"#34D399",info:"#22D3EE",text:"#F9FAFB",muted:"#C4CAD3",background:"#030712",panel:"#111827",element:"#1F2937",highlight:"#374151",inverse:"#030712",border:"#4B5563",borderActive:"#60A5FA"},devctl:{name:"devctl",primary:"#77DDBA",accent:"#9EE9CF",error:"#F28B91",warning:"#E8C586",search:"#E8C586",success:"#91D6A0",info:"#8EC8D8",text:"#DEEEE5",muted:"#A4B9AE",background:"#15201D",panel:"#202923",element:"#293B32",highlight:"#365448",inverse:"#15201D",border:"#526D60",borderActive:"#77DDBA"},ember:{name:"ember",primary:"#FF9F43",accent:"#2EE6FF",error:"#FF4D6D",warning:"#FFE566",search:"#FFE566",success:"#3DFF9A",info:"#2EE6FF",text:"#F7F9FF",muted:"#B0B8CC",background:"#07090F",panel:"#12161F",element:"#1A2130",highlight:"#243044",inverse:"#0B0D12",border:"#334155",borderActive:"#FF9F43"}},aL={opencode:"ember",light:"system",mocha:"catppuccin",latte:"catppuccin-latte","one-dark":"onedark","one dark":"onedark",solarized:"solarized-dark",rosepine:"rose-pine","rose pine":"rose-pine",github:"github-dark",ayu:"ayu-dark","night owl":"night-owl",ansi:"terminal",xterm:"terminal",console:"terminal",classic:"terminal",vga:"terminal",contrast:"voltage","contrast-light":"voltage-light",hc:"voltage","hc-light":"voltage-light",a11y:"voltage","a11y-light":"voltage-light"};lL=["#7AA2F7","#9ECE6A","#E0AF68","#BB9AF7","#2AC3DE","#FF9E64","#F7768E","#73DACA","#C0CAF5","#FF79C6","#7DCFFF","#C3E88D"],dL=["#2E5AAC","#3D7A1F","#9A6B12","#6B3FA0","#0E7A86","#B85C1A","#B4234A","#1A7A6D","#4A5080","#9A3480","#1565C0","#4A7C19"];wL={claude:{dark:"#E08A6A",light:"#B54328"},cursor:{dark:"#B8D4FF",light:"#1E4EBF"},kilo:{dark:"#F0C94A",light:"#8A6400"},codex:{dark:"#4ADE80",light:"#0B7A4B"}}});var _r=D(()=>{Zs()});function ux(e){return e!==void 0}function lx(e){let t=e/1000;if(Number.isInteger(t))return`${t}s`;return`${e}ms`}function mp(e,t,r){let n=e.indexOf(t),i=((n<0?0:n)+r+e.length)%e.length;return e[i]??e[0]}function px(e,t){let r=[...St],n=r.includes(e)?e:fp;return mp(r,n,t)}function fx(e,t){let r=[...PL],n=r.reduce((o,i)=>Math.abs(i-e)<Math.abs(o-e)?i:o,r[0]);return mp(r,n,t)}function vs(e){let t=[...pp];return t.reduce((r,n)=>Math.abs(n-e)<Math.abs(r-e)?n:r,t[0])}function ys(e,t){return mp([...pp],vs(e),t)}function xs(e){let t=vs(e),r=dx[t]??dx[Do];return{px:t,...r}}function go(e){return e.label==="compact"}function bs(e){return e.steps<=2}function mx(e){return pp.map((r,n)=>n<e?"\u2588":"\u2591").join(" ")}function Dc(e){return xs(e).label}function hx(e){let t=e.locked?"Applies this session only. DEVCTL_TUI_CONFIG overrides the user file.":`Saved to ${e.configPath}.`;return[{id:"theme",group:"Appearance",kind:"cycle",name:"Theme",value:e.themeName,hint:"\u2190 \u2192 save enter picker",detail:`Arrows write the theme to the user file. Enter opens the picker. ${t}`},{id:"font",group:"Appearance",kind:"cycle",name:"Display size",value:Dc(e.fontSize),hint:"\u2190 \u2192 save",detail:`Scales content padding and list-row height. Default keeps a thin rule between chrome and panes. Compact sits toolbars flush against borders. Header, nav, and status stay one line. Does not change the terminal font. ${t}`},{id:"mouse",group:"Input",kind:"toggle",name:"Mouse",value:e.mouse?"on":"off",hint:"space or enter toggle",detail:`Clicks on nav and lists. Restart the TUI after a change. ${t}`},{id:"leader",group:"Input",kind:"cycle",name:"Leader",value:lx(e.leaderMs),hint:"\u2190 \u2192 save",detail:`How long ${It("x")} waits for the next key. ${t}`},{id:"mcp",group:"MCP",kind:"page",name:"Settings page",value:e.mcpRunning?"running \u2192 /mcp":"off \u2192 /mcp",hint:"enter open the MCP page",detail:"Opens a dedicated MCP page (also /mcp or /agent). Start or stop the localhost server, change the port, and copy Claude, Cursor, Codex, and Kilo Code config."},{id:"file",group:"About",kind:"info",name:"File",value:e.configPath,hint:"read-only",detail:e.locked?`Using ${e.configPath} from DEVCTL_TUI_CONFIG.`:`User preferences live in ${e.configPath}.`},{id:"version",group:"About",kind:"info",name:"Version",value:jt,hint:"read-only",detail:er()},{id:"reset",group:"About",kind:"action",name:"Reset",value:"defaults",hint:"enter confirm reset",detail:`Restore theme ${fp}, display ${Dc(Do)}, mouse on, leader ${lx(id)}. Enter asks first. ${t}`}]}function Ic(){return{theme:fp,mouse:!0,leader_timeout:id,font_size:Do}}function gx(e){return["Appearance","Input","MCP","About"].flatMap((r)=>{let n=e.filter((o)=>o.group===r);if(n.length===0)return[];return[{group:r,items:n}]})}function hp(e,t){if(e.length===0)return 0;return Math.min(Math.max(t,0),e.length-1)}function Ln(e,t){return e[hp(e,t)]}function gp(e,t){return e??t}var PL,pp,fp="devctl",dx;var Er=D(()=>{tt();_r();PL=[1000,2000,3000],pp=[12,14,16,18,20,22];dx={12:{pad:0,rowH:1,gap:0,steps:1,label:"compact"},14:{pad:0,rowH:1,gap:0,steps:2,label:"default"},16:{pad:1,rowH:1,gap:1,steps:3,label:"comfortable"},18:{pad:1,rowH:2,gap:1,steps:4,label:"large"},20:{pad:2,rowH:2,gap:1,steps:5,label:"xl"},22:{pad:2,rowH:2,gap:2,steps:6,label:"xxl"}}});import{createContext as RL,useContext as _L}from"react";function ye(){return _L(vp)}var vp;var Mt=D(()=>{Er();_r();vp=RL(xs(Do))});function _e(e,t){if(t<=0)return"";if(e.length<=t)return e;if(t===1)return"\u2026";return`${e.slice(0,t-1)}\u2026`}function oe(e,t){if(t<=0)return"";if(e.length>=t)return _e(e,t);return e.padEnd(t)}function ii(e,t=20){let r=Number.isFinite(e)?Math.min(1,Math.max(0,e)):0,n=Math.round(r*t);return"\u2588".repeat(n)+"\u2591".repeat(Math.max(0,t-n))}function ws(e){if(!Number.isFinite(e)||e<yp)return"< 1s";let t=Math.floor(e/vx),r=Math.floor(e%vx/bp),n=Math.floor(e%bp/xp),o=Math.floor(e%xp/yp);if(t>0)return`${t}d ${r}h`;if(r>0)return`${r}h ${n}m`;if(n>0)return`${n}m ${String(o).padStart(2,"0")}s`;return`${o}s`}function Ss(e){if(!Number.isFinite(e)||e<0)return"\u2014";let t=e/EL;if(t>=yx)return`${(t/yx).toFixed(1)}G`;if(t>=1)return`${Math.round(t)}M`;return`${Math.round(e)}K`}function xx(e){if(!Number.isFinite(e)||e<0)return"\u2014";return`${e.toFixed(1)}%`}var yp=1000,xp,bp,vx,EL=1024,yx=1024;var Ot=D(()=>{xp=60*yp,bp=60*xp,vx=24*bp});function Sp(e){return e>=LL}function kp(e){return e>=DL}function Cp(e){return e>=IL}function Fc(e,t=0){return Math.max(1,e-wx-t*2)}function Sx(e){let t=bx+Tr+$t;if(Sp(e))t+=Ur;if(kp(e))t+=Oc;if(Cp(e))t+=sn;return Math.max($c,e-t)}function Nc(e,t,r){if(r)return e;let n=t.reduce((a,c)=>Math.max(a,c.length),0),o=Math.min(wp,Math.max($c,n+TL)),i=bx+Tr+$t+o+wx+AL,s=Math.max(Mc,Math.floor(e*ML));return Math.min(s,Math.max(Mc,i))}function kx(e){if(!e)return"";return Object.keys(e.profiles).sort()[0]??""}function Hc(e){if(!e)return!0;let t=Object.values(e.services);if(t.length===0)return!0;return!t.some((r)=>yo(r))}function Pp(e,t){if(e.length>0)return e;return[...t]}function jc(e,t){if(e.length>0)return[...e];return t===""?[]:[t]}function Bc(e){return e.command.args.join(" ")||"\u2014"}function Rs(e,t){let r=vo(t);if(r!=="")return r;if(e.ports.length===0)return"\u2014";return e.ports.map((n)=>`${n.name}:${n.auto?"auto":n.value}`).join(" ")}function Uc(e){let t=e.health.type||"none",r=e.health.url||e.health.address;return r===""?t:`${t} ${r}`}function Wc(e,t){if(t?.identity)return t.identity;let r=e.identity.type||"none",n=e.identity.service_account;return n===""?r:`${r} ${n}`}function Gc(e){let t=e.restart.policy||"none";if(e.restart.max_retries>0)return`${t} \xD7${e.restart.max_retries}`;return t}function Cx(e,t,r,n,o){let i={...e.environment.defaults,...e.environment.vars},a=OL(o??i,t,r,n);return[...new Set([...Object.keys(a),...e.environment.required])].map((l)=>({key:l,value:a[l]??"",required:e.environment.required.includes(l),fromConfig:Object.prototype.hasOwnProperty.call(i,l)})).sort((l,d)=>l.fromConfig===d.fromConfig?l.key.localeCompare(d.key):l.fromConfig?-1:1)}function Px(e,t){if(!e||e.processes.length===0)return;if(t!==void 0&&t!==""&&e.session_id===t)return;return e}function vo(e){if(!e)return"";if(e.ports.http!==void 0)return String(e.ports.http);let t=Object.values(e.ports)[0];return t!==void 0?String(t):""}function tr(e){if(!e)return"STOPPED";return Jo(e)}function OL(e,t,r,n){if(t)return e;return new mr(r,n).redactMap(e)}function _s(e){return $L.has(e)}function yo(e){if(!e)return!1;return _s(e.state)}var bx=8,Tr=12,Ur=10,Oc=8,sn=8,ks=9,Cs=7,Ps=8,$c=12,wp=40,$t=2,TL=2,wx=2,AL=2,Mc=34,LL=48,DL=60,IL=72,ML=0.48,$L;var At=D(()=>{Tt();ni();$L=new Set(["RUNNING","STARTING","RESTARTING","HEALTHY","UNHEALTHY"])});function zc(e){return e===pa?"\u2014":e}function Rp(e,t,r){let n=r?_x:FL+_x+Math.max(0,t)*2;return Math.max(1,e-n)}function BL(e){if(e.startsWith('"')||e.startsWith("'"))return"string";if(/^\d+$/.test(e))return"number";return"keyword"}function Yc(e){return e.replace(UL,"")}function Lx(e){try{return JSON.stringify(JSON.parse(e),null,2)}catch{return e}}function Jc(e,t){let r=Math.max(1,t),n=Yc(e).replace(/\r\n/g,`
2207
+ `},{path:"docs/troubleshooting.md",title:"Troubleshooting",body:"# Troubleshooting\n\n| Symptom | What to do |\n|---------|------------|\n| `gcloud` not installed | Install the Cloud SDK only if you need Google identity. Local-only services still run |\n| ADC unavailable | `/auth login` or `gcloud auth application-default login`, then `devctl auth status` / `/auth refresh` |\n| Wrong project | Set `google.project_id` or check `gcloud config get-value core/project`. Identity shows the source |\n| Permission denied | Ask an admin for the missing IAM role; `devctl doctor` names the resource |\n| Cannot impersonate SA | Need `roles/iam.serviceAccountTokenCreator` on that service account (group binding preferred) |\n| IAP authentication failure | Confirm audience, IAP client, and that the identity matches the route. If `client_id` is set, the ADC refresh token must belong to that OAuth client |\n| Port already in use | Doctor lists the holder. Stop a leftover, or change config. Running your own services will also show as \u201Cin use\u201D |\n| Service crashes | Open Logs, filter `ERROR` (`e`), restart with `R` |\n| Health check failure | Confirm the health URL/port; `process` checks only PID liveness |\n| Proxy unavailable / missing listen port | Pin `proxy.listen.port` (required when `proxy.enabled` is true \u2014 validate exits **2**). Then `devctl proxy start` or TUI `n`. Starting with port `0` exits **7**. Bind is loopback only |\n| Token expired | Automatic refresh uses `auth.refresh_threshold_seconds`; run `devctl auth refresh`. Open Doctor if ADC itself expired |\n| Token audience incorrect | Set `auth.audience` on the IAP route; Doctor flags missing audiences |\n| IAP used a user token for an SA route | Confirm the route identity is `service_account`; Doctor probes impersonated IAP separately |\n| Leftover process after crash | Reopen `devctl` \u2014 adopt only when pid + command + cwd + startTime match `~/.devctl/state/<hash>/state.json`. A port-only leftover is never attached |\n| `devctl attach` fails | No supervisor. Use `devctl start` first; attach never starts one |\n| `devctl status` looks empty | If the socket is down, status prints persisted state and exits 0 when nothing is running. The TUI still starts a supervisor; leftover PIDs from the previous session appear on the idle dashboard |\n| Start exits 5 or 6 | 5 = spawn failed; 6 = health never passed. Doctor then Logs |\n| `start` brought up extra services | Empty start uses the active or first profile **alphabetically**, plus dependencies \u2014 YAML key order does not matter (demo: `backend`, not `data`). Pass `--profile` or explicit names to stay narrower |\n| Docker / Podman missing or daemon down | A service in config declares `container` (the demo's `postgres` always does, even if you never start `data`). Install and start that runtime, or drop the service. Default profiles do not start postgres |\n| Doctor offers to kill a busy port that is Docker | It should not: ports owned by a running container service are healthy, and Doctor never offers to terminate the Docker or Podman daemon. Re-run Doctor after the container is up |\n| MCP tools work but nothing starts | `get_status` reports `setup_mode: true` \u2014 there is no `.devctl` yet. Have the agent call `get_setup_guide`, `search_docs`, and `validate_config`, write the files, then `reload_config` |\n| `devctl exec --print-env` hides values | Secret-like names are redacted unless you also pass `--reveal` (TUI: `/exec <svc> --print-env --reveal` or `/reveal`). MCP `exec_service` never reveals them |\n| TUI env pane looks incomplete | It now loads the same resolved map as `devctl exec --print-env`. If the chip says `config fallback`, the daemon `exec` call failed \u2014 check `/daemon` and that the supervisor is up |\n| Supervisor will not start | `devctl daemon logs` or TUI `/daemon` is the bootstrap stderr, not the service log bus |\n| TUI stale / not updating | TUI follows the event bus (20\u201350ms batch). Quit and let a new supervisor start if an old one is still listening |\n| Reload needs a restart | `devctl reload` and `/reload` list services whose command, env, ports, or identity changed |\n| Configuration invalid | `devctl config validate` \u2014 unknown fields, cycles, and missing refs fail closed. TUI `v` / `/buffer` validates before write |\n| Config on disk is broken but the TUI still opens fine | Expected: it attached to an already-running daemon and is showing its `config_snapshot` (last-known-good), not a fresh reparse of the broken file. Fix the file and `/reload` |\n| `devctl update` says unavailable | GitHub Releases API could not be reached. For npm: `npm install --global @amr-m-abdelgawad/devctl@latest`. Homebrew and GitHub binaries: see [installation](installation.md) |\n| MCP agent cannot connect | Listener is off by default. `/mcp` or `devctl mcp --on`. URL is loopback only; snippets include the bearer token |\n| `devctl: command not found` | Run `npm install --global @amr-m-abdelgawad/devctl`, then ensure npm's global binary directory is on `PATH`. See [Installation](installation.md) |\n| Bundled Bun runtime was not installed | Reinstall the npm package without `--ignore-scripts`; Bun uses its install script to select the correct platform runtime |\n\nInternal `devctl` logs appear in the Logs screen with source `devctl`.\n\n## Related\n\n- [Doctor](doctor.md)\n- [Logs](logs.md)\n- [CLI](cli.md)\n- [How it fits together](overview.md)\n"},{path:"docs/tui.md",title:"TUI",body:"# TUI\n\nThe TUI is [OpenTUI](https://opentui.com/docs/) (`@opentui/core` + `@opentui/react`). It attaches to the supervisor and never owns child processes.\n\n```bash\ncd examples/demo-platform\nnpx @amr-m-abdelgawad/devctl@latest\n```\n\nFrom a source checkout (`bun` on PATH):\n\n```bash\ncd examples/demo-platform\nbun run ../../app/src/bin.ts\n```\n\nIf a supervisor session already exists, the TUI attaches to it. Preferences: `tui.json` / `DEVCTL_TUI_CONFIG` \u2014 see [Building from source](typescript.md).\n\n## First run\n\nWith no `.devctl` configuration the TUI opens **setup**: \u201CNo configuration found. Would you like to run setup? **[Enter] Setup [Esc] Exit**\u201D. Enter starts the same 9-step wizard as `devctl setup` (OpenTUI fields, then write and attach the daemon \u2014 no process restart). Invalid existing YAML still refuses overwrite.\n\nIf a `.devctl/config.yaml` exists but fails to parse or validate, the TUI shows **Configuration error** with the actual error instead \u2014 pressing Enter here does not run setup, since that would silently overwrite the file the error is about. Fix the file and restart devctl, or run `devctl config validate` for the same error from the CLI.\n\nWhen services exist but none are running, the dashboard empty state:\n\n- `enter` starts the default profile (first profile name alphabetically) after a plan overlay\n- `n` / `x` start or stop the highlighted row (or the space-selected set)\n- a **lifecycle panel** shows start and stop waves; later start waves wait for health and do not run if a wave fails\n- the panel stays open until `esc` so you can read the result\n- `o` picks a profile, then confirms start\n\n## Quit\n\n`q` / `/exit` / `esc` twice:\n\n| `shutdown.stop_services_on_exit` | Behavior |\n|----------------------------------|----------|\n| `true` | Stop managed services and leave |\n| `false` | Detach immediately |\n| unset | Confirm: `enter` stops services, `d` detaches (daemon stays), `k` stops the daemon and leaves services (`devctl down --keep-services`), `esc` stays |\n\n**Detach** (`d`) leaves the supervisor running. **`/down --keep-services`** (or quit `k`) stops the supervisor and persists PIDs so a later start can adopt them. **`/down`** stops services and the supervisor. `/stop` only stops selected services.\n\n## Interaction model\n\nKeyboard-first. Chords use **command** on macOS and **ctrl** on Linux and Windows. Help, the status bar, and empty-state hints label the modifier for the OS you are on.\n\n| Input | What it does |\n|-------|----------------|\n| `/` | Command overlay \u2014 ranked as you type (name, alias, fuzzy, then description). `/start api` still lists `start`. `\u2191`/`\u2193` to move, `enter` to run |\n| `command+p` / `ctrl+p` | Same command overlay as `/` |\n| `command+x` / `ctrl+x` | Leader key (2s), then a shortcut \u2014 keymap overlay |\n| `?` | Grouped help \u2014 `j`/`k` scroll when the list is taller than the terminal |\n| `tab` / `shift+tab` / `1`\u2013`4` | Cycle or jump the **four nav tabs**. Other screens are `/auth`, `/credentials`, `/doctor`, `/config`, `/profiles`, `/setup`, `/stats`, `/settings`, `/mcp`. On a secondary screen, `tab` returns to the dashboard. When the strip is wider than the terminal it slides (`\u2039` `\u203A`). |\n| `s` `l` `a` `p` `d` `c` `u` | Direct letter nav when no overlay owns keys (services, logs, identity, proxy, doctor, config, setup) |\n| `r` | Refresh snapshot (doctor `r` re-runs checks) |\n| `R` | Restart selected services |\n| `j` `k` / arrows | Move selection |\n| `enter` | Start (empty dashboard) or open service detail |\n| `space` | Multi-select a service |\n| `esc` | Back / close overlay. Twice (when nothing else is open) asks to quit |\n| `f` | Focus log search (`g` jumps to latest). Remap with `keybinds.search` |\n| `z` | Expand logs to fill the terminal. `z` or `esc` exits |\n| `w` | Cycle log wrap: clip, unwrap the selected row, or wrap every long line |\n| `command+c` / `ctrl+c` | Copy the highlighted selection (drag with the mouse). Remap with `keybinds.copy` |\n| `command+=` / `ctrl+=` (and `-` / `0`) | Display size (padding/row height, not the terminal font) |\n| `esc` `esc` | Twice to quit when no overlay or back target is open. The OS copy chord does not quit |\n| Mouse | Click nav, click a service, scroll logs (toggle in Settings) |\n\nThe status bar only lists keys that work **on the current screen**. There is no idle command row \u2014 `/` and the OS palette chord open the command overlay.\n\n## Nav tabs (4)\n\n1. dashboard \xB7 2. services \xB7 3. logs \xB7 4. proxy\n\nEverything else is a slash command (or a letter jump): `/auth`, `/credentials`, `/doctor`, `/config`, `/profiles`, `/setup`, `/stats`, `/settings`. **MCP** is `/mcp`, `/agent`, or Settings \u2192 **MCP \u2192 Settings page**.\n\n## Screens\n\n- **Dashboard** \u2014 services, proxy, live log tail. Identity lives on `/auth`; ADC status is in the header. When nothing is running, a **last session** panel shows leftover PIDs from the previous supervisor (same data `devctl status` prints when the socket is down)\n- **Services** \u2014 list plus a live inspector: status chips, two-column facts, then a scrollable **resolved** env pane (dotenv, profile, secrets, plugins, runtime ports). Narrow terminals stack the panes. `enter` opens the full detail screen\n- **Service detail** \u2014 same inspector; env pane is focused so `j`/`k` scroll. `/reveal` shows secrets. `n`/`x`/`R`/`l`\n- **Logs** \u2014 ANSI color codes are stripped so wrap uses visible width; `w` cycles clip / wrap selected / wrap all. `\\\\` / `/split` opens a second pane on the same live stream (independent service filter, shared search). `/trace <id>` or Enter on a log details request id jumps search to that id. See [Logs](logs.md)\n- **Identity** \u2014 user, project, source, ADC, gcloud, configured SAs, impersonation AVAILABLE/UNAVAILABLE, IAP (no tokens). `/auth login` suspends the TUI, runs `gcloud auth application-default login` on the real terminal, then restores the TUI. `/auth logout` revokes ADC without leaving the screen\n- **Credentials** \u2014 store backend and entry names only. Tokens stay in the OS keychain or `~/.devctl/credentials`\n- **Proxy** \u2014 status + routes (match and upstream wrap instead of clipping); click a route for full details. `n` start / `x` stop\n- **Doctor** \u2014 re-runs on every visit; \u2713 / ! / \u2717 with hints. `enter` on a busy host port asks to stop that process; it never offers to kill the Docker or Podman daemon. `r` reruns\n- **Config** \u2014 merged view including **tasks**. `v` / `/buffer` opens a validate/save overlay on `cfg.configPath` (invalid YAML is not written; `esc` discards). `e` / `/edit` still opens `$EDITOR` / `DEVCTL_EDITOR`. `/diff` shows provenance (`devctl config diff`). `/reload` re-reads after an external edit\n- **Profiles** \u2014 members; `enter` selects and offers start\n- **Setup** \u2014 onboarding checklist. First-run with no config still opens here\n- **Settings** \u2014 grouped prefs: theme, display size, mouse, leader timeout, **MCP settings page**, about, reset. `\u2190`/`\u2192` writes the highlighted cycle or toggles mouse. Reset asks before restoring defaults. Saves to `~/.devctl/tui.json` unless `DEVCTL_TUI_CONFIG` is set\n- **MCP** \u2014 Listen `[ ON ]` / `[ OFF ]`, port stepper `\u2039 N \u203A`, per-agent **Copy JSON** / **Copy TOML**, and a **Tools** list grouped by purpose (inspect, logs, diagnostics, control, setup) with each tool marked `read` or `write`; `space` enables or disables the highlighted one, all on by default. Off by default. See [MCP](mcp.md)\n\n`/reveal` toggles secret env values for this session only. The header shows `secrets shown`.\n\n## Slash commands\n\n```text\n/start [service\u2026] start selection, args, or the current profile\n/stop [service\u2026]\n/down [--keep-services] stop the supervisor (and services unless --keep-services)\n/restart [--cascade|-c] named services only; cascade also restarts dependents\n R with dependents: Enter = named, c = cascade\n/run [task] one-off task; empty /run opens a picker. Output is in Logs under task:<name>\n/exec [service] -- <command\u2026>\n empty /exec opens a service picker, then type the command\n/exec <service> --print-env [--reveal]\n resolved env (dotenv, profile, secrets, plugins, ports), not config-only vars\n/logs /services /auth /credentials /proxy /mcp /doctor /config /profiles /setup\n/stats system and service statistics (sparklines when the supervisor has samples)\n/split second log pane (`\\\\`); `|` focuses the other pane\n/trace <id> set log search to a request_id / trace_id\n/dashboard return home\n/themes [name] picker with live preview; enter saves to ~/.devctl/tui.json\n/settings theme, mouse, display size, MCP page\n/filter toggle ERROR+\n/pause freeze the live log stream\n/reveal show or hide secret env values\n/wrap cycle log wrap (selected / all / clip)\n/copy copy visible logs to the clipboard\n/export [path] write filtered logs to ~/.devctl/exports (or the given path)\n/exports open the export folder\n/regex /since /until /history /edit /buffer\n/clear\n/refresh\n/reload reload .devctl\n/diff winning config sources and what they shadowed (`devctl config diff`)\n/import compose [path] [--write]\n/daemon supervisor bootstrap stderr (`devctl daemon logs`)\n/auth login|logout|refresh\n/update install a newer GitHub Release when the method is known (npm/Homebrew)\n/version current version, then the same update check (does not install)\n/exit /quit /q\n```\n\nAliases include `/up`, `/identity`, `/creds`, `/agent`, `/init`, `/home`, `/prefs`, `/task`, `/provenance`, `/bootstrap`. `/down` is no longer an alias of `/stop`.\n\n## Leader key\n\nDefault leader is `command+x` on macOS and `ctrl+x` elsewhere (2 second timeout). Then:\n\n```text\nn start x stop R restart (c cascade if dependents) s services l logs\na auth p proxy d doctor c config o profiles\nt themes e export r refresh i setup h dashboard\nq quit z fullscreen\n```\n\nOverride in `tui.json` (`keybinds`) or `DEVCTL_TUI_CONFIG`.\n\n## Layout\n\n- **Header** \u2014 product + version as text, then project and profile; chips only for running count, live proxy, MCP when on, ADC, and secrets-shown\n- **Nav** \u2014 the four primary tabs; the active tab is highlighted, not filled\n- **Body** \u2014 dashboard or a focused screen\n- **Command overlay** \u2014 `/` and `command+p` / `ctrl+p` open the same grouped list with a real OpenTUI input\n- **Status bar** \u2014 live/paused, last human result, contextual keys\n\nStatus is never color-only: `\u2713` healthy, `\u25CF` running, `!` warning, `\u2717` failed, `\u25CB` stopped.\n\n## Themes\n\n`/themes` opens a picker with live preview. Built-ins:\n\n- Product: `devctl` (default), `ember`\n- Common dark: `tokyonight`, `catppuccin`, `nord`, `gruvbox`, `kanagawa`, `dracula`, `onedark`, `monokai`, `rose-pine`, `everforest`, `github-dark`, `iceberg`, `ayu-dark`, `oxocarbon`, `night-owl`\n- Light: `catppuccin-latte`, `solarized-light`\n- Other: `solarized-dark`, `terminal` (black + VGA ANSI chrome; aliases `ansi`, `xterm`, `console`), `system` (follows macOS `AppleInterfaceStyle` / `COLORFGBG`; light uses Solarized Light)\n\nAliases: `mocha` \u2192 Catppuccin Mocha, `latte`, `one-dark`, `solarized` (dark), `rosepine`, `github`, `ayu`, `night owl`.\n\nMCP agent chips use brand colors (Claude terracotta, Cursor blue, Kilo gold, Codex green) with light/dark variants.\n\n## Related\n\n- [Logs](logs.md)\n- [MCP](mcp.md)\n- [Building from source](typescript.md)\n- [CLI](cli.md)\n"},{path:"docs/typescript.md",title:"Building from source",body:'# Building from source\n\nThe application lives in `app/`. Runtime is [Bun](https://bun.sh). The TUI is [OpenTUI](https://opentui.com/docs/).\n\n```bash\nexport PATH="$HOME/.bun/bin:$PATH"\ncd app\nbun install\nbun run src/bin.ts --help\nbun test\nbun run check:coverage\nbunx tsc --noEmit\nbun run check:architecture\nbun run check:dead\nbun run check:dup\n```\n\nFrom the repository root (after `bun install` in `app/`):\n\n```bash\nbun run app/src/bin.ts\ncd app && bun link # optional: `devctl` on PATH\n```\n\n## Layout\n\n| Path | Role |\n|------|------|\n| `app/src/bin.ts` | Entry |\n| `app/src/bootstrap/` | Composition roots (daemon + client) |\n| `app/src/application/client-runtime.ts` | Injected CLI/TUI operations and controller contract |\n| `app/src/presentation/cli/` | Commander CLI |\n| `app/src/presentation/tui/` | OpenTUI screens and overlays |\n| `app/src/presentation/tui/hooks/` | Client queries, command execution, and TUI state |\n| `app/src/presentation/tui/helpers/` | Screen-specific formatting, navigation, logs, and plan helpers |\n| `app/src/presentation/mcp/` | Streamable HTTP MCP server |\n| `app/src/domain/` | Service, identity, config, log, session, and preference types and policies |\n| `app/src/adapters/config/` | Discover, decode, merge, validate |\n| `app/src/adapters/process/` | Host process runtime |\n| `app/src/adapters/google/` | Google / IAP / tokens |\n| `app/src/adapters/daemon/supervisor.ts` | Daemon host (socket, recovery, watch, listeners) |\n| `app/src/adapters/rpc/controller.ts` | Local supervisor or socket / named-pipe client |\n| `app/tui.json` | Starter TUI preferences |\n\nLayer rules: [architecture.md](architecture.md). Check with `bun run check:architecture`. Coverage: `bun run check:coverage` (aggregate funcs/lines; OpenTUI screens and hooks are ignored). Unused files and dependencies: `bun run check:dead` ([Knip](https://knip.dev)). Copy-paste clones: `bun run check:dup` ([jscpd](https://jscpd.dev)).\n\nThere is no separate Go tree.\n\n## TUI preferences\n\nConfiguration is **`tui.json` or `tui.jsonc`**: `theme`, `keybinds`, `leader_timeout`, `font_size`, `mouse`, `scroll_speed`, `log_timestamps`, `log_metadata`, `mcp_enabled`, `mcp_port`, `mcp_disabled_tools`.\n\n`mcp_disabled_tools` is a deny-list of MCP tool names (empty means every tool is available). See [MCP](mcp.md).\n\nSearch order:\n\n```mermaid\nflowchart TB\n env{"DEVCTL_TUI_CONFIG<br/>or OPENCODE_TUI_CONFIG exists?"}\n env -->|yes| only["Use that file only"]\n env -->|no| first["First existing of<br/>./tui.jsonc \xB7 .devctl/tui.jsonc \xB7 ~/.devctl/tui.jsonc"]\n first --> merge{"~/.devctl/tui.json is a different path?"}\n merge -->|yes| overlay["Merge user file on top"]\n merge -->|no| done["Use that file"]\n```\n\nSettings writes go to `~/.devctl/tui.json` unless the env override is set (then changes apply for this session only).\n\n`keybinds` merge with the built-in defaults, so you only override what you change. Defaults use `cmd` on macOS and `ctrl` on Linux/Windows (`command+c` / `ctrl+c` in the TUI).\n\n```json\n{\n "theme": "devctl",\n "leader_timeout": 2000,\n "keybinds": {\n "leader": "ctrl+x",\n "command_list": "ctrl+p"\n },\n "mouse": true,\n "mcp_enabled": false,\n "mcp_disabled_tools": []\n}\n```\n\n## Tests\n\n```bash\ncd app && bun test\n```\n\nIntegration tests that need Google stay skipped unless credentials are present.\n\n## Related\n\n- [Installation](installation.md)\n- [TUI](tui.md)\n- [How it fits together](overview.md)\n- [Contributing](../CONTRIBUTING.md)\n'}]});function BA(){return[{path:"skills/devctl-onboard/SKILL.md",title:"Onboard procedure",body:Tn.procedure},{path:"skills/devctl-onboard/references/authoring.md",title:"Onboard authoring",body:Tn.authoring},{path:"skills/devctl-onboard/references/discovery.md",title:"Onboard discovery",body:Tn.discovery}]}function qy(){return[...Uy,...BA()]}function Vy(e){let t=e.trim();if(t==="")throw Error("path is required");let r=qy(),n=r.find((s)=>s.path===t),o=n?[n]:r.filter((s)=>s.path.endsWith(`/${t}`));if(o.length!==1)throw Error(`no unique doc for path "${t}"; available: ${r.map((s)=>s.path).join(", ")}`);let i=o[0];return{path:i.path,title:i.title,body:i.body}}function UA(e){return e.toLowerCase().split(/[^a-z0-9_]+/).filter((t)=>t.length>=HA)}function WA(e,t){let r=e.title.toLowerCase(),n=e.path.toLowerCase(),o=e.body.toLowerCase(),i=0;for(let a of t){if(r.includes(a))i+=OA;if(n.includes(a))i+=$A;let c=o.split(a).length-1;i+=Math.min(c,FA)}let s=t.join(" ");if(s.length>=jA&&o.includes(s))i+=NA;return i}function GA(e,t){let r=e.split(`
2208
+ `),n=0,o=-1;for(let s=0;s<r.length;s++){let a=r.slice(s,s+Ky).join(`
2209
+ `).toLowerCase(),c=0;for(let l of t)if(a.includes(l))c+=1;if(c>o)o=c,n=s}let i=r.slice(n,n+Ky).join(`
2210
+ `).trim();if(i.length<=Gy)return i;return`${i.slice(0,Gy).trimEnd()}\u2026`}function KA(e){if(e===void 0||Number.isNaN(e))return IA;return Math.min(MA,Math.max(1,Math.floor(e)))}function zy(e,t){let r=e.trim(),n=UA(r);if(n.length===0)throw Error("query is required");let o=KA(t),i=qy().map((s)=>({path:s.path,title:s.title,score:WA(s,n),snippet:GA(s.body,n)})).filter((s)=>s.score>0).sort((s,a)=>a.score-s.score||s.path.localeCompare(a.path)).slice(0,o);return{query:r,hits:i}}var IA=5,MA=10,OA=12,$A=8,FA=8,NA=15,Gy=400,Ky=5,HA=2,jA=4;var Yy=I(()=>{sp();Wy()});import{existsSync as qA,readFileSync as VA}from"fs";function An(e,t){return!(t??[]).includes(e)}function Jy(e){return _c.filter((t)=>An(t.name,e))}function Rc(e){return _c.some((t)=>t.name===e)}function ps(e){return new mr(e.secrets.extra_markers,e.secrets.extra_patterns)}function Xy(e){return Object.values(e.services).map((t)=>({name:t.name,state:t.state,health:t.health,ports:t.ports,pid:t.pid,last_error:t.last_error}))}function YA(e,t){let r=e.config(),n=r.services[t];if(!n)throw Error(`unknown service ${t}`);let i=e.status().services[t],s=ps(r);return{name:t,state:i?.state,health:i?.health,pid:i?.pid,last_error:i?.last_error,command:n.command.args,cwd:n.working_dir,ports:i?.ports??Object.fromEntries(n.ports.map((a)=>[a.name,a.value])),environment:s.redactMap({...n.environment.defaults,...n.environment.vars}),container:n.container?{...n.container,env:s.redactMap(n.container.env)}:void 0}}function Qy(e){return{session_id:e.session_id,repo_root:e.repo_root,profile:e.profile,setup_mode:e.setup_mode===!0?!0:void 0,identity:{user:e.identity.user,project:e.identity.project,adc:e.identity.adc,iap:e.identity.iap,service_accounts:e.identity.service_accounts,service_account_status:e.identity.service_account_status},proxy:{running:e.proxy.running,address:e.proxy.address,routes:e.proxy.routes},logs:e.logs,mcp:e.mcp?{running:e.mcp.running,address:e.mcp.address,port:e.mcp.port}:{running:!1}}}async function Zy(e,t){let r=typeof t.service==="string"?t.service:"",n=typeof t.since==="string"?t.since:"",o=typeof t.cursor==="string"?t.cursor:void 0,i=await e.logsPage({services:r===""?[]:[r],level:typeof t.level==="string"?t.level:"",search:typeof t.search==="string"?t.search:"",source:typeof t.source==="string"?t.source:"",since:n,until:typeof t.until==="string"?t.until:"",cursor:o,direction:o?"forward":void 0,limit:zA}),s=ps(e.config());return{events:i.events.map((a)=>({timestamp:a.timestamp,service:a.service,source:a.source,level:a.level,message:s.redactText(a.message),pid:a.pid})),truncated:i.hasPrev,has_more:i.hasNext,next_since:i.events[i.events.length-1]?.timestamp??n,next_cursor:i.nextCursor,session_changed:i.sessionChanged}}function JA(e){return Object.entries(e.profiles).sort(([t],[r])=>t.localeCompare(r)).map(([t,r])=>({name:t,services:r.services}))}function oi(e){return e.trim()===""?void 0:e}function XA(e){let t={name:e.name,match:e.match,upstream:e.upstream.url!==""?e.upstream.url:oi(e.upstream.service??"")?`service:${e.upstream.service}${e.upstream.port?`/${e.upstream.port}`:""}`:"",auth:e.auth.type},r=oi(e.auth.identity.type),n=oi(e.auth.audience),o=oi(e.auth.identity.service_account||e.auth.service_account),i=oi(e.auth.client_id),s=Ba(e.auth.client_secret),a=oi(e.auth.credentials??"");if(r)t.identity=r;if(n)t.audience=n;if(o)t.service_account=o;if(i)t.client_id=i;if(s)t.client_secret=s;if(a)t.credentials=a;return t}function eb(e){let t=Object.entries(e.services).map(([r,n])=>({name:r,description:n.description,command:n.command.args,cwd:n.working_dir,ports:n.ports.map((o)=>({name:o.name,value:o.auto?0:o.value,auto:o.auto})),dependencies:n.dependencies,container:n.container?{image:n.container.image,runtime:n.container.runtime||"docker",ports:n.container.ports,volumes:n.container.volumes}:void 0}));return{project:e.project.name,config_path:e.configPath,repo_root:e.repoRoot,services:t,proxy:{enabled:e.proxy.enabled,gateway:e.proxy.gateway,credentials:e.proxy.credentials,listen:{host:e.proxy.listen.host,port:e.proxy.listen.port},routes:e.proxy.routes.map((r)=>XA(r))}}}function QA(e){let t=ps(e);return{entries:Pn(e).map((r)=>{let n=typeof r.value==="string"?r.value:JSON.stringify(r.value);return{...r,value:t.redactMap({[r.path]:n})[r.path]}})}}function Pc(e){if(!Array.isArray(e))return[];return e.filter((t)=>typeof t==="string")}async function tb(e,t,r){switch(t){case"list_services":return Xy(e.status());case"get_service":if(typeof r.name!=="string"||r.name==="")throw Error("name is required");return YA(e,r.name);case"get_status":return Qy(e.status());case"get_logs":return Zy(e,r);case"list_profiles":return JA(e.config());case"get_config":return eb(e.config());case"get_config_sources":return QA(e.config());case"run_doctor":return e.doctor();case"start_services":return e.start({services:Pc(r.services),profile:typeof r.profile==="string"&&r.profile!==""?r.profile:void 0});case"stop_services":return await e.stop(Pc(r.services)),{ok:!0};case"restart_services":return await e.restart(Pc(r.services),r.cascade===!0),{ok:!0};case"reload_config":return e.reload();case"run_task":{if(typeof r.name!=="string"||r.name==="")throw Error("name is required");let n=await e.runTask(r.name),o=ps(e.config());return{...n,stdout:o.redactText(n.stdout),stderr:o.redactText(n.stderr)}}case"start_proxy":return await e.startProxy(),{ok:!0};case"stop_proxy":return await e.stopProxy(),{ok:!0};case"exec_service":{if(typeof r.service!=="string"||r.service==="")throw Error("service is required");if(!e.exec)throw Error("exec is unavailable");let n=await e.exec(r.service,Pc(r.command),r.print_env===!0),o=ps(e.config());return{...n,stdout:o.redactText(n.stdout),stderr:o.redactText(n.stderr),environment:n.environment?o.redactMap(n.environment):void 0}}case"get_setup_guide":return ZA(r);case"search_docs":return zy(typeof r.query==="string"?r.query:"",typeof r.limit==="number"?r.limit:void 0);case"get_doc":return Vy(typeof r.path==="string"?r.path:"");case"validate_config":return eL(e,r);default:throw Error(`unknown tool ${t}`)}}function ZA(e){let t=typeof e.section==="string"?e.section:"procedure",r=t in Tn?t:"procedure";return{section:r,text:Tn[r],sections:Object.keys(Tn)}}function eL(e,t){let r=e.config();if(typeof t.text==="string"){let o=e.validateConfigText(t.text);return{valid:o.length===0,issues:o,source:"candidate",config_path:r.configPath}}if(!qA(r.configPath))return{valid:!1,issues:[`no configuration at ${r.configPath}`],source:"disk",config_path:r.configPath,setup_mode:!0};let n=e.validateConfigText(VA(r.configPath,"utf8"));return{valid:n.length===0,issues:n,source:"disk",config_path:r.configPath}}function rb(e){return ap.includes(e)}async function nb(e,t){switch(t){case"devctl://status":return Qy(e.status());case"devctl://services":return Xy(e.status());case"devctl://logs":return Zy(e,{});case"devctl://config":return eb(e.config());case"devctl://doctor":return e.doctor();default:throw Error(`unknown resource ${t}`)}}var zA=200,ap,cp,_c;var fs=I(()=>{ni();Yy();sp();ap=["devctl://status","devctl://services","devctl://logs","devctl://config","devctl://doctor"],cp=["inspect","logs","diagnostics","control","setup"],_c=[{name:"list_services",label:"List services",summary:"Name, state, health, ports, pid",category:"inspect",description:"List services with state, health, ports, pid, and last error",inputSchema:{type:"object",properties:{},additionalProperties:!1}},{name:"get_service",label:"Service detail",summary:"One service with command, cwd, ports",category:"inspect",description:"One service plus command, cwd, and ports. Environment values are redacted.",inputSchema:{type:"object",properties:{name:{type:"string"}},required:["name"],additionalProperties:!1}},{name:"get_status",label:"Status",summary:"Profile, identity, proxy, log counts",category:"inspect",description:"Profile, session, identity flags (no tokens), proxy, and log counts",inputSchema:{type:"object",properties:{},additionalProperties:!1}},{name:"get_logs",label:"Read logs",summary:"Filtered log pages, secrets redacted",category:"logs",description:"Recent log lines, optionally filtered. Capped at 200 events per page. Secrets are redacted. Pass cursor=next_cursor to page forward with no duplicate or same-millisecond-lost events; since/until are plain timestamp filters for a fresh query, not a follow cursor.",inputSchema:{type:"object",properties:{service:{type:"string"},level:{type:"string"},search:{type:"string"},source:{type:"string"},since:{type:"string",description:"Only events at or after this timestamp"},until:{type:"string",description:"Only events at or before this timestamp"},cursor:{type:"string",description:"Opaque cursor from a previous response's next_cursor; continues forward from exactly there"}},additionalProperties:!1}},{name:"list_profiles",label:"List profiles",summary:"Configured profiles and their members",category:"inspect",description:"Configured profiles and their member services",inputSchema:{type:"object",properties:{},additionalProperties:!1}},{name:"get_config",label:"Read config",summary:"Merged project summary, no secret values",category:"inspect",description:"Merged project summary: services, routes, and proxy listen paths. Route auth includes type, identity, audience, and optional IAP client_id. client_secret is returned only as a ${ENV} template, never a resolved secret.",inputSchema:{type:"object",properties:{},additionalProperties:!1}},{name:"get_config_sources",label:"Config sources",summary:"Winning and shadowed config sources",category:"inspect",description:"List effective configuration values with their winning source/layer and any shadowed sources. Secret-like values are redacted.",inputSchema:{type:"object",properties:{},additionalProperties:!1}},{name:"run_doctor",label:"Run doctor",summary:"Environment diagnostics",category:"diagnostics",description:"Run environment diagnostics",inputSchema:{type:"object",properties:{},additionalProperties:!1}},{name:"start_services",label:"Start services",summary:"Start named services or a profile",category:"control",mutates:!0,description:"Start named services, or a profile when names are omitted. Empty start uses profile, then the active session profile, then the first configured profile \u2014 never every service.",inputSchema:{type:"object",properties:{services:{type:"array",items:{type:"string"}},profile:{type:"string",description:"Profile to start when services is omitted"}},additionalProperties:!1}},{name:"stop_services",label:"Stop services",summary:"Stop services and their dependents",category:"control",mutates:!0,description:"Stop named services, or all started services when names are omitted. Also stops every service that transitively depends on a named one (never a named service's own dependencies, which other running services may still need).",inputSchema:{type:"object",properties:{services:{type:"array",items:{type:"string"}}},additionalProperties:!1}},{name:"restart_services",label:"Restart services",summary:"Restart services, optionally cascading",category:"control",mutates:!0,description:"Restart named services. By default this touches only the named services, not anything that depends on them. Pass cascade=true to also restart their transitive dependents (the same set stop_services would affect).",inputSchema:{type:"object",properties:{services:{type:"array",items:{type:"string"}},cascade:{type:"boolean",description:"Also restart transitive dependents; default false restarts only the named services"}},additionalProperties:!1}},{name:"reload_config",label:"Reload config",summary:"Re-read .devctl configuration",category:"control",mutates:!0,description:"Reload .devctl configuration",inputSchema:{type:"object",properties:{},additionalProperties:!1}},{name:"run_task",label:"Run task",summary:"Run a named one-off task",category:"control",mutates:!0,description:"Run a named task from configuration. Output is also written to the log ring as task:<name>.",inputSchema:{type:"object",properties:{name:{type:"string"}},required:["name"],additionalProperties:!1}},{name:"start_proxy",label:"Start proxy",summary:"Start the local reverse proxy",category:"control",mutates:!0,description:"Start the local reverse proxy. Same as CLI `devctl proxy start` and TUI start on the proxy screen.",inputSchema:{type:"object",properties:{},additionalProperties:!1}},{name:"stop_proxy",label:"Stop proxy",summary:"Stop the local reverse proxy",category:"control",mutates:!0,description:"Stop the local reverse proxy and leave it suppressed until start_proxy (or CLI/TUI start) runs again.",inputSchema:{type:"object",properties:{},additionalProperties:!1}},{name:"exec_service",label:"Execute in service",summary:"Run a command in a resolved service context",category:"control",mutates:!0,description:"Run an arbitrary command with a service's fully resolved environment and working directory, whether or not it is running. Output and print_env values are redacted.",inputSchema:{type:"object",properties:{service:{type:"string"},command:{type:"array",items:{type:"string"}},print_env:{type:"boolean",description:"Return the resolved environment without executing a command"}},required:["service"],additionalProperties:!1}},{name:"get_setup_guide",label:"Setup guide",summary:"How to author a .devctl for this repo",category:"setup",description:"The devctl onboarding guide: how to survey a repository and author a .devctl configuration for it. Read section=procedure first; read section=authoring BEFORE writing any YAML (it carries the rules the loader rejects on, which the JSON Schema does not state); read section=discovery for mapping compose/package.json/pyproject/Terraform/k8s/.env to services. Write the files with your own editing tools \u2014 this server does not write them \u2014 then check your work with validate_config.",inputSchema:{type:"object",properties:{section:{type:"string",enum:["procedure","authoring","discovery"],description:"Which part of the guide to return; defaults to procedure"}},additionalProperties:!1}},{name:"search_docs",label:"Search docs",summary:"Search the embedded product documentation",category:"setup",description:"Search the compiled-in product documentation (docs/*.md plus the onboarding skill). Returns ranked pages with short snippets for discovery. To read a full page, pass a hit's `path` to get_doc. Use this for IAP, proxy, MCP, configuration, and similar topics; use get_setup_guide for the full onboarding procedure.",inputSchema:{type:"object",properties:{query:{type:"string",description:"Keywords or a short phrase to search for"},limit:{type:"integer",description:"Maximum hits to return (default 5, max 10)"}},required:["query"],additionalProperties:!1}},{name:"get_doc",label:"Get doc",summary:"Read a full documentation page",category:"setup",description:"Return the complete text of one embedded documentation page. Pass the `path` from a search_docs hit (e.g. docs/proxy.md) \u2014 search_docs only returns short snippets, so use this to read the whole page. A basename like proxy.md also resolves when unambiguous.",inputSchema:{type:"object",properties:{path:{type:"string",description:"Doc path from a search_docs hit, e.g. docs/proxy.md"}},required:["path"],additionalProperties:!1}},{name:"validate_config",label:"Validate config",summary:"Check configuration or a draft for errors",category:"setup",description:"Validate devctl configuration and return the exact issues the loader would report. With no arguments, validates what is on disk. Pass text to validate a candidate config.yaml before writing it \u2014 the candidate is run through the real load pipeline (modular services/profiles, overlays, templates), so it works even when no configuration exists yet. This is the only way to validate over MCP; there is no CLI round-trip needed.",inputSchema:{type:"object",properties:{text:{type:"string",description:"Candidate config.yaml contents to validate instead of the file on disk"}},additionalProperties:!1}}]});function db(e){return{...e,runDoctor:(t,r,n)=>e.runDoctor.execute(t,r,n),startupPlan:(t,r,n)=>e.getStartupPlan.execute(t,r,n),shutdownPlan:(t,r)=>e.getShutdownPlan.execute(t,r),shutdownPlanExact:(t,r)=>ic(t,r),resolveStartRequest:(t,r)=>e.resolveStart.execute(t,{...r,profile:r.profile?ur(r.profile):void 0,activeProfile:r.activeProfile===void 0?void 0:ur(r.activeProfile)})}}var ub=I(()=>{At()});function Tc(e){let t=e.toLowerCase().trim(),r=fL[t];if(r)return r;return St.includes(t)?t:"devctl"}function mL(){if(process.env.DEVCTL_THEME_LIGHT==="1")return!0;if(process.env.DEVCTL_THEME_LIGHT==="0")return!1;if(process.platform==="darwin")try{let t=Bun.spawnSync(["defaults","read","-g","AppleInterfaceStyle"],{stdout:"pipe",stderr:"pipe"}),r=t.stdout.toString();if(t.exitCode===0&&r.toLowerCase().includes("dark"))return!1;if(t.exitCode!==0)return!0}catch{}let e=process.env.COLORFGBG??"";if(e.includes(";15")||e.endsWith(";7"))return!0;if(e.includes(";0")||e.endsWith(";8"))return!1;return!1}function pb(e){let t=Tc(e);if(t==="system"){if(mL())return{...dp["solarized-light"],name:"system"};return dp.system}return dp[t]}function hr(e){switch(e.toUpperCase()){case"HEALTHY":case"OK":return"\u2713";case"UNHEALTHY":case"WARNING":case"WARN":return"!";case"FAILED":case"ERROR":return"\u2717";case"STOPPED":case"UNKNOWN":return"\u25CB";case"RUNNING":case"STARTING":case"RESTARTING":return"\u25CF";default:return"\u25CB"}}function mo(e,t){return ys(e)>=vs?Ac:t}function ys(e){let t=e.startsWith("#")?e.slice(1):e,r=Number.parseInt(t,kL);if(Number.isNaN(r))return 0;let n=r>>CL&up,o=r>>PL&up,i=r&up;return(n*bL+o*xL+i*wL)/SL}function _L(e){let t=vL;for(let r=0;r<e.length;r+=1)t^=e.charCodeAt(r),t=Math.imul(t,yL);return t>>>0}function Lc(e){return ys(e.background)>=vs}function fp(e){if(!e||!/^#[0-9a-f]{6}$/i.test(e))return!1;return ys(e)<vs}function Dc(e,t){let r=RL[e];return Lc(t)?r.light:r.dark}function fb(e,t){return ys(Dc(e,t))>=vs?EL:TL}function Ze(e,t){if(e===""||e==="all")return t.muted;let r=ys(t.background)>=vs?gL:hL;return r[_L(e)%r.length]??t.info}function AL(e,t){switch(t.toUpperCase()){case"ERROR":case"FATAL":return e.error;case"WARN":case"WARNING":return e.warning;case"INFO":return e.text;case"DEBUG":case"TRACE":return e.muted;default:return e.text}}function mb(e,t,r){if(r==="search")return e.search;if(r==="string")return e.accent;if(r==="number")return e.info;if(r==="keyword")return e.error;return AL(e,t)}function et(e,t){switch(t.toUpperCase()){case"HEALTHY":case"OK":return e.success;case"UNHEALTHY":case"WARNING":case"WARN":return e.warning;case"FAILED":case"ERROR":return e.error;case"RUNNING":case"STARTING":case"RESTARTING":return e.primary;case"INFO":return e.info;case"FATAL":return e.error;case"DEBUG":case"TRACE":case"UNKNOWN":return e.muted;default:return e.muted}}var St,pp,dp,fL,hL,gL,vL=2166136261,yL=16777619,bL=299,xL=587,wL=114,SL=1000,vs=160,kL=16,up=255,CL=16,PL=8,Ac="#1A1A1A",RL,EL="#1A1A1A",TL="#F5F5F5";var tt=I(()=>{St=["devctl","ember","tokyonight","catppuccin","catppuccin-latte","nord","gruvbox","kanagawa","dracula","onedark","monokai","solarized-dark","solarized-light","rose-pine","everforest","github-dark","iceberg","ayu-dark","oxocarbon","night-owl","terminal","voltage","voltage-light","ironclad","ironclad-light","signal","signal-light","system"],pp={devctl:"forest + mint, website palette",ember:"warm orange",tokyonight:"Tokyo Night storm blues",catppuccin:"Catppuccin Mocha","catppuccin-latte":"Catppuccin Latte (light)",nord:"Arctic Nord frost",gruvbox:"Gruvbox dark hard",kanagawa:"Kanagawa wave / gold",dracula:"classic Dracula purple",onedark:"Atom / VS Code One Dark",monokai:"Monokai Sublime","solarized-dark":"Solarized dark","solarized-light":"Solarized light","rose-pine":"Ros\xE9 Pine moon",everforest:"Everforest dark medium","github-dark":"GitHub dimmed canvas",iceberg:"Iceberg navy","ayu-dark":"Ayu Mirage-adjacent dark",oxocarbon:"IBM Oxocarbon","night-owl":"Sarah Drasner Night Owl",terminal:"black + VGA ANSI chrome",voltage:"max contrast, electric neon (dark)","voltage-light":"max contrast, electric neon (light)",ironclad:"max contrast, steel & amber (dark)","ironclad-light":"max contrast, steel & amber (light)",signal:"max contrast, traffic-light semantics (dark)","signal-light":"max contrast, traffic-light semantics (light)",system:"neutral dark (system default)"},dp={tokyonight:{name:"tokyonight",primary:"#82AAFF",accent:"#C792EA",error:"#FF5370",warning:"#FFCB6B",search:"#FFCB6B",success:"#C3E88D",info:"#89DDFF",text:"#F4F7FF",muted:"#A8B2CC",background:"#0F111A",panel:"#1A1B2E",element:"#24283B",highlight:"#2E3350",inverse:"#0F111A",border:"#3D4466",borderActive:"#82AAFF"},catppuccin:{name:"catppuccin",primary:"#89B4FA",accent:"#F5C2E7",error:"#F38BA8",warning:"#F9E2AF",search:"#F9E2AF",success:"#A6E3A1",info:"#89DCEB",text:"#E4E8F7",muted:"#A6A9BE",background:"#11111B",panel:"#1E1E2E",element:"#313244",highlight:"#45475A",inverse:"#11111B",border:"#585B70",borderActive:"#89B4FA"},"catppuccin-latte":{name:"catppuccin-latte",primary:"#1E66F5",accent:"#EA76CB",error:"#D20F39",warning:"#DF8E1D",search:"#DF8E1D",success:"#40A02B",info:"#179299",text:"#4C4F69",muted:"#6C6F85",background:"#EFF1F5",panel:"#E6E9EF",element:"#DCE0E8",highlight:"#CCD0DA",inverse:"#EFF1F5",border:"#9CA0B0",borderActive:"#1E66F5"},nord:{name:"nord",primary:"#88C0D0",accent:"#B48EAD",error:"#BF616A",warning:"#EBCB8B",search:"#EBCB8B",success:"#A3BE8C",info:"#8FBCBB",text:"#F2F5FA",muted:"#A4AEBF",background:"#2E3440",panel:"#3B4252",element:"#434C5E",highlight:"#4C566A",inverse:"#2E3440",border:"#5E6A7D",borderActive:"#88C0D0"},gruvbox:{name:"gruvbox",primary:"#FE8019",accent:"#FABD2F",error:"#FB4934",warning:"#FE8019",search:"#FE8019",success:"#B8BB26",info:"#8EC07C",text:"#F2E5BC",muted:"#C4B394",background:"#1D2021",panel:"#282828",element:"#3C3836",highlight:"#504945",inverse:"#1D2021",border:"#665C54",borderActive:"#FE8019"},kanagawa:{name:"kanagawa",primary:"#7FB4CA",accent:"#E6C384",error:"#E82424",warning:"#FFA066",search:"#FFA066",success:"#98BB6C",info:"#7FB4CA",text:"#E8E4C9",muted:"#B4C0D6",background:"#16161D",panel:"#1F1F28",element:"#2A2A37",highlight:"#363646",inverse:"#16161D",border:"#54546D",borderActive:"#E6C384"},dracula:{name:"dracula",primary:"#BD93F9",accent:"#FF79C6",error:"#FF5555",warning:"#F1FA8C",search:"#F1FA8C",success:"#50FA7B",info:"#8BE9FD",text:"#F8F8F2",muted:"#8B9DC9",background:"#21222C",panel:"#282A36",element:"#343746",highlight:"#44475A",inverse:"#21222C",border:"#6272A4",borderActive:"#BD93F9"},onedark:{name:"onedark",primary:"#61AFEF",accent:"#C678DD",error:"#E06C75",warning:"#E5C07B",search:"#E5C07B",success:"#98C379",info:"#56B6C2",text:"#ABB2BF",muted:"#9DA5B4",background:"#21252B",panel:"#282C34",element:"#2C323C",highlight:"#3E4451",inverse:"#21252B",border:"#4B5263",borderActive:"#61AFEF"},monokai:{name:"monokai",primary:"#66D9EF",accent:"#AE81FF",error:"#F92672",warning:"#E6DB74",search:"#E6DB74",success:"#A6E22E",info:"#66D9EF",text:"#F8F8F2",muted:"#A39E80",background:"#1D1E19",panel:"#272822",element:"#3E3D32",highlight:"#49483E",inverse:"#1D1E19",border:"#75715E",borderActive:"#F92672"},"solarized-dark":{name:"solarized-dark",primary:"#268BD2",accent:"#6C71C4",error:"#DC322F",warning:"#B58900",search:"#B58900",success:"#859900",info:"#2AA198",text:"#93A1A1",muted:"#586E75",background:"#002B36",panel:"#073642",element:"#0A4A58",highlight:"#1A5A68",inverse:"#002B36",border:"#586E75",borderActive:"#268BD2"},"solarized-light":{name:"solarized-light",primary:"#268BD2",accent:"#D33682",error:"#DC322F",warning:"#CB4B16",search:"#CB4B16",success:"#859900",info:"#2AA198",text:"#586E75",muted:"#839496",background:"#FDF6E3",panel:"#EEE8D5",element:"#E4DCC8",highlight:"#D5CDB6",inverse:"#FDF6E3",border:"#93A1A1",borderActive:"#268BD2"},"rose-pine":{name:"rose-pine",primary:"#C4A7E7",accent:"#EBBCBA",error:"#EB6F92",warning:"#F6C177",search:"#F6C177",success:"#31748F",info:"#9CCFD8",text:"#E0DEF4",muted:"#908CAA",background:"#191724",panel:"#1F1D2E",element:"#26233A",highlight:"#403D52",inverse:"#191724",border:"#6E6A86",borderActive:"#C4A7E7"},everforest:{name:"everforest",primary:"#7FBBB3",accent:"#D699B6",error:"#E67E80",warning:"#DBBC7F",search:"#DBBC7F",success:"#A7C080",info:"#83C092",text:"#D3C6AA",muted:"#9DA9A0",background:"#232A2E",panel:"#2D353B",element:"#343F44",highlight:"#3D484D",inverse:"#232A2E",border:"#4F585E",borderActive:"#A7C080"},"github-dark":{name:"github-dark",primary:"#58A6FF",accent:"#A371F7",error:"#F85149",warning:"#D29922",search:"#D29922",success:"#3FB950",info:"#79C0FF",text:"#E6EDF3",muted:"#8B949E",background:"#0D1117",panel:"#161B22",element:"#21262D",highlight:"#30363D",inverse:"#0D1117",border:"#30363D",borderActive:"#58A6FF"},iceberg:{name:"iceberg",primary:"#84A0C6",accent:"#A093C7",error:"#E27878",warning:"#E2A478",search:"#E2A478",success:"#B4BE82",info:"#89B8C2",text:"#C6C8D1",muted:"#9AA0B8",background:"#161821",panel:"#1E2132",element:"#2A3150",highlight:"#3D425B",inverse:"#161821",border:"#444B71",borderActive:"#84A0C6"},"ayu-dark":{name:"ayu-dark",primary:"#59C2FF",accent:"#FFB454",error:"#D95757",warning:"#FF8F40",search:"#FF8F40",success:"#C2D94C",info:"#95E6CB",text:"#B3B1AD",muted:"#8B939C",background:"#0A0E14",panel:"#0D1017",element:"#131721",highlight:"#1B212C",inverse:"#0A0E14",border:"#2D3640",borderActive:"#FFB454"},oxocarbon:{name:"oxocarbon",primary:"#78A9FF",accent:"#EE5396",error:"#FA4D56",warning:"#F1C21B",search:"#F1C21B",success:"#42BE65",info:"#3DDBD9",text:"#F2F4F8",muted:"#A8A8A8",background:"#161616",panel:"#262626",element:"#393939",highlight:"#525252",inverse:"#161616",border:"#6F6F6F",borderActive:"#78A9FF"},"night-owl":{name:"night-owl",primary:"#82AAFF",accent:"#C792EA",error:"#EF5350",warning:"#ECC48D",search:"#ECC48D",success:"#ADDB67",info:"#7FDBCA",text:"#D6DEEB",muted:"#8FA4A4",background:"#011627",panel:"#0B2942",element:"#1D3B53",highlight:"#1E4B6B",inverse:"#011627",border:"#5F7E97",borderActive:"#82AAFF"},terminal:{name:"terminal",primary:"#55FFFF",accent:"#FF55FF",error:"#FF5555",warning:"#FFFF55",search:"#FFFF55",success:"#55FF55",info:"#5555FF",text:"#C0C0C0",muted:"#808080",background:"#000000",panel:"#000000",element:"#000000",highlight:"#404040",inverse:"#000000",border:"#555555",borderActive:"#55FFFF"},voltage:{name:"voltage",primary:"#00E5FF",accent:"#FF3EA5",error:"#FF1744",warning:"#FFEA00",search:"#FFEA00",success:"#00E676",info:"#40C4FF",text:"#FFFFFF",muted:"#C0C0C0",background:"#000000",panel:"#000000",element:"#0D0D0D",highlight:"#262626",inverse:"#000000",border:"#808080",borderActive:"#FFFFFF"},"voltage-light":{name:"voltage-light",primary:"#0057B7",accent:"#C2185B",error:"#D50000",warning:"#FF6D00",search:"#FF6D00",success:"#2E7D32",info:"#00838F",text:"#000000",muted:"#424242",background:"#FFFFFF",panel:"#FFFFFF",element:"#EDEDED",highlight:"#D6D6D6",inverse:"#FFFFFF",border:"#616161",borderActive:"#000000"},ironclad:{name:"ironclad",primary:"#4FC3F7",accent:"#FFD54F",error:"#FF5252",warning:"#FFB300",search:"#FFB300",success:"#69F0AE",info:"#64B5F6",text:"#FFFFFF",muted:"#BDBDBD",background:"#000000",panel:"#000000",element:"#101010",highlight:"#242424",inverse:"#000000",border:"#757575",borderActive:"#4FC3F7"},"ironclad-light":{name:"ironclad-light",primary:"#01579B",accent:"#E65100",error:"#B71C1C",warning:"#EF6C00",search:"#EF6C00",success:"#1B5E20",info:"#0277BD",text:"#000000",muted:"#4A4A4A",background:"#FFFFFF",panel:"#FFFFFF",element:"#EFEFEF",highlight:"#DADADA",inverse:"#FFFFFF",border:"#6E6E6E",borderActive:"#01579B"},signal:{name:"signal",primary:"#29B6F6",accent:"#AB47BC",error:"#FF0000",warning:"#FFC400",search:"#FFC400",success:"#00E400",info:"#00B8D4",text:"#FFFFFF",muted:"#B3B3B3",background:"#000000",panel:"#000000",element:"#0A0A0A",highlight:"#202020",inverse:"#000000",border:"#8A8A8A",borderActive:"#29B6F6"},"signal-light":{name:"signal-light",primary:"#01579B",accent:"#6A1B9A",error:"#C62828",warning:"#F9A825",search:"#F9A825",success:"#2E7D32",info:"#00796B",text:"#000000",muted:"#3D3D3D",background:"#FFFFFF",panel:"#FFFFFF",element:"#ECECEC",highlight:"#D2D2D2",inverse:"#FFFFFF",border:"#5C5C5C",borderActive:"#01579B"},system:{name:"system",primary:"#60A5FA",accent:"#C084FC",error:"#F87171",warning:"#FBBF24",search:"#FBBF24",success:"#34D399",info:"#22D3EE",text:"#F9FAFB",muted:"#C4CAD3",background:"#030712",panel:"#111827",element:"#1F2937",highlight:"#374151",inverse:"#030712",border:"#4B5563",borderActive:"#60A5FA"},devctl:{name:"devctl",primary:"#77DDBA",accent:"#9EE9CF",error:"#F28B91",warning:"#E8C586",search:"#E8C586",success:"#91D6A0",info:"#8EC8D8",text:"#DEEEE5",muted:"#A4B9AE",background:"#15201D",panel:"#202923",element:"#293B32",highlight:"#365448",inverse:"#15201D",border:"#526D60",borderActive:"#77DDBA"},ember:{name:"ember",primary:"#FF9F43",accent:"#2EE6FF",error:"#FF4D6D",warning:"#FFE566",search:"#FFE566",success:"#3DFF9A",info:"#2EE6FF",text:"#F7F9FF",muted:"#B0B8CC",background:"#07090F",panel:"#12161F",element:"#1A2130",highlight:"#243044",inverse:"#0B0D12",border:"#334155",borderActive:"#FF9F43"}},fL={opencode:"ember",light:"system",mocha:"catppuccin",latte:"catppuccin-latte","one-dark":"onedark","one dark":"onedark",solarized:"solarized-dark",rosepine:"rose-pine","rose pine":"rose-pine",github:"github-dark",ayu:"ayu-dark","night owl":"night-owl",ansi:"terminal",xterm:"terminal",console:"terminal",classic:"terminal",vga:"terminal",contrast:"voltage","contrast-light":"voltage-light",hc:"voltage","hc-light":"voltage-light",a11y:"voltage","a11y-light":"voltage-light"};hL=["#7AA2F7","#9ECE6A","#E0AF68","#BB9AF7","#2AC3DE","#FF9E64","#F7768E","#73DACA","#C0CAF5","#FF79C6","#7DCFFF","#C3E88D"],gL=["#2E5AAC","#3D7A1F","#9A6B12","#6B3FA0","#0E7A86","#B85C1A","#B4234A","#1A7A6D","#4A5080","#9A3480","#1565C0","#4A7C19"];RL={claude:{dark:"#E08A6A",light:"#B54328"},cursor:{dark:"#B8D4FF",light:"#1E4EBF"},kilo:{dark:"#F0C94A",light:"#8A6400"},codex:{dark:"#4ADE80",light:"#0B7A4B"}}});var Rr=I(()=>{ta()});function vb(e){return e!==void 0}function hb(e){let t=e/1000;if(Number.isInteger(t))return`${t}s`;return`${e}ms`}function gp(e,t,r){let n=e.indexOf(t),i=((n<0?0:n)+r+e.length)%e.length;return e[i]??e[0]}function yb(e,t){let r=[...St],n=r.includes(e)?e:hp;return gp(r,n,t)}function bb(e,t){let r=[...LL],n=r.reduce((o,i)=>Math.abs(i-e)<Math.abs(o-e)?i:o,r[0]);return gp(r,n,t)}function bs(e){let t=[...mp];return t.reduce((r,n)=>Math.abs(n-e)<Math.abs(r-e)?n:r,t[0])}function xs(e,t){return gp([...mp],bs(e),t)}function ws(e){let t=bs(e),r=gb[t]??gb[Lo];return{px:t,...r}}function ho(e){return e.label==="compact"}function Ss(e){return e.steps<=2}function xb(e){return mp.map((r,n)=>n<e?"\u2588":"\u2591").join(" ")}function Ic(e){return ws(e).label}function wb(e){let t=e.locked?"Applies this session only. DEVCTL_TUI_CONFIG overrides the user file.":`Saved to ${e.configPath}.`;return[{id:"theme",group:"Appearance",kind:"cycle",name:"Theme",value:e.themeName,hint:"\u2190 \u2192 save enter picker",detail:`Arrows write the theme to the user file. Enter opens the picker. ${t}`},{id:"font",group:"Appearance",kind:"cycle",name:"Display size",value:Ic(e.fontSize),hint:"\u2190 \u2192 save",detail:`Scales content padding and list-row height. Default keeps a thin rule between chrome and panes. Compact sits toolbars flush against borders. Header, nav, and status stay one line. Does not change the terminal font. ${t}`},{id:"mouse",group:"Input",kind:"toggle",name:"Mouse",value:e.mouse?"on":"off",hint:"space or enter toggle",detail:`Clicks on nav and lists. Restart the TUI after a change. ${t}`},{id:"leader",group:"Input",kind:"cycle",name:"Leader",value:hb(e.leaderMs),hint:"\u2190 \u2192 save",detail:`How long ${It("x")} waits for the next key. ${t}`},{id:"mcp",group:"MCP",kind:"page",name:"Settings page",value:e.mcpRunning?"running \u2192 /mcp":"off \u2192 /mcp",hint:"enter open the MCP page",detail:"Opens a dedicated MCP page (also /mcp or /agent). Start or stop the localhost server, change the port, and copy Claude, Cursor, Codex, and Kilo Code config."},{id:"file",group:"About",kind:"info",name:"File",value:e.configPath,hint:"read-only",detail:e.locked?`Using ${e.configPath} from DEVCTL_TUI_CONFIG.`:`User preferences live in ${e.configPath}.`},{id:"version",group:"About",kind:"info",name:"Version",value:jt,hint:"read-only",detail:er()},{id:"reset",group:"About",kind:"action",name:"Reset",value:"defaults",hint:"enter confirm reset",detail:`Restore theme ${hp}, display ${Ic(Lo)}, mouse on, leader ${hb(sd)}. Enter asks first. ${t}`}]}function Mc(){return{theme:hp,mouse:!0,leader_timeout:sd,font_size:Lo}}function Sb(e){return["Appearance","Input","MCP","About"].flatMap((r)=>{let n=e.filter((o)=>o.group===r);if(n.length===0)return[];return[{group:r,items:n}]})}function vp(e,t){if(e.length===0)return 0;return Math.min(Math.max(t,0),e.length-1)}function Ln(e,t){return e[vp(e,t)]}function yp(e,t){return e??t}var LL,mp,hp="devctl",gb;var Er=I(()=>{tt();Rr();LL=[1000,2000,3000],mp=[12,14,16,18,20,22];gb={12:{pad:0,rowH:1,gap:0,steps:1,label:"compact"},14:{pad:0,rowH:1,gap:0,steps:2,label:"default"},16:{pad:1,rowH:1,gap:1,steps:3,label:"comfortable"},18:{pad:1,rowH:2,gap:1,steps:4,label:"large"},20:{pad:2,rowH:2,gap:1,steps:5,label:"xl"},22:{pad:2,rowH:2,gap:2,steps:6,label:"xxl"}}});import{createContext as DL,useContext as IL}from"react";function ye(){return IL(bp)}var bp;var Mt=I(()=>{Er();Rr();bp=DL(ws(Lo))});function Re(e,t){if(t<=0)return"";if(e.length<=t)return e;if(t===1)return"\u2026";return`${e.slice(0,t-1)}\u2026`}function oe(e,t){if(t<=0)return"";if(e.length>=t)return Re(e,t);return e.padEnd(t)}function si(e,t=20){let r=Number.isFinite(e)?Math.min(1,Math.max(0,e)):0,n=Math.round(r*t);return"\u2588".repeat(n)+"\u2591".repeat(Math.max(0,t-n))}function ks(e){if(!Number.isFinite(e)||e<xp)return"< 1s";let t=Math.floor(e/kb),r=Math.floor(e%kb/Sp),n=Math.floor(e%Sp/wp),o=Math.floor(e%wp/xp);if(t>0)return`${t}d ${r}h`;if(r>0)return`${r}h ${n}m`;if(n>0)return`${n}m ${String(o).padStart(2,"0")}s`;return`${o}s`}function Cs(e){if(!Number.isFinite(e)||e<0)return"\u2014";let t=e/ML;if(t>=Cb)return`${(t/Cb).toFixed(1)}G`;if(t>=1)return`${Math.round(t)}M`;return`${Math.round(e)}K`}function Pb(e){if(!Number.isFinite(e)||e<0)return"\u2014";return`${e.toFixed(1)}%`}var xp=1000,wp,Sp,kb,ML=1024,Cb=1024;var Ot=I(()=>{wp=60*xp,Sp=60*wp,kb=24*Sp});function Cp(e){return e>=FL}function Pp(e){return e>=NL}function _p(e){return e>=HL}function Nc(e,t=0){return Math.max(1,e-Rb-t*2)}function Eb(e){let t=_b+Tr+$t;if(Cp(e))t+=Ur;if(Pp(e))t+=$c;if(_p(e))t+=sn;return Math.max(Fc,e-t)}function Hc(e,t,r){if(r)return e;let n=t.reduce((a,c)=>Math.max(a,c.length),0),o=Math.min(kp,Math.max(Fc,n+OL)),i=_b+Tr+$t+o+Rb+$L,s=Math.max(Oc,Math.floor(e*jL));return Math.min(s,Math.max(Oc,i))}function Tb(e){if(!e)return"";return Object.keys(e.profiles).sort()[0]??""}function jc(e){if(!e)return!0;let t=Object.values(e.services);if(t.length===0)return!0;return!t.some((r)=>vo(r))}function Rp(e,t){if(e.length>0)return e;return[...t]}function Bc(e,t){if(e.length>0)return[...e];return t===""?[]:[t]}function Uc(e){return e.command.args.join(" ")||"\u2014"}function Es(e,t){let r=go(t);if(r!=="")return r;if(e.ports.length===0)return"\u2014";return e.ports.map((n)=>`${n.name}:${n.auto?"auto":n.value}`).join(" ")}function Wc(e){let t=e.health.type||"none",r=e.health.url||e.health.address;return r===""?t:`${t} ${r}`}function Gc(e,t){if(t?.identity)return t.identity;let r=e.identity.type||"none",n=e.identity.service_account;return n===""?r:`${r} ${n}`}function Kc(e){let t=e.restart.policy||"none";if(e.restart.max_retries>0)return`${t} \xD7${e.restart.max_retries}`;return t}function Ab(e,t,r,n,o){let i={...e.environment.defaults,...e.environment.vars},a=BL(o??i,t,r,n);return[...new Set([...Object.keys(a),...e.environment.required])].map((l)=>({key:l,value:a[l]??"",required:e.environment.required.includes(l),fromConfig:Object.prototype.hasOwnProperty.call(i,l)})).sort((l,d)=>l.fromConfig===d.fromConfig?l.key.localeCompare(d.key):l.fromConfig?-1:1)}function Lb(e,t){if(!e||e.processes.length===0)return;if(t!==void 0&&t!==""&&e.session_id===t)return;return e}function go(e){if(!e)return"";if(e.ports.http!==void 0)return String(e.ports.http);let t=Object.values(e.ports)[0];return t!==void 0?String(t):""}function tr(e){if(!e)return"STOPPED";return Jo(e)}function BL(e,t,r,n){if(t)return e;return new mr(r,n).redactMap(e)}function Ts(e){return UL.has(e)}function vo(e){if(!e)return!1;return Ts(e.state)}var _b=8,Tr=12,Ur=10,$c=8,sn=8,Ps=9,_s=7,Rs=8,Fc=12,kp=40,$t=2,OL=2,Rb=2,$L=2,Oc=34,FL=48,NL=60,HL=72,jL=0.48,UL;var Lt=I(()=>{At();ni();UL=new Set(["RUNNING","STARTING","RESTARTING","HEALTHY","UNHEALTHY"])});function Yc(e){return e===ma?"\u2014":e}function Ep(e,t,r){let n=r?Ib:WL+Ib+Math.max(0,t)*2;return Math.max(1,e-n)}function VL(e){if(e.startsWith('"')||e.startsWith("'"))return"string";if(/^\d+$/.test(e))return"number";return"keyword"}function Jc(e){return e.replace(zL,"")}function Fb(e){try{return JSON.stringify(JSON.parse(e),null,2)}catch{return e}}function Xc(e,t){let r=Math.max(1,t),n=Jc(e).replace(/\r\n/g,`
2139
2211
  `).replace(/\r/g,`
2140
2212
  `).split(`
2141
- `),o=[];for(let i of n)if(i.length<=r)o.push(i);else o.push(...WL(i,r));return o.length>0?o:[""]}function Dx(e,t,r){let n=e.length>0?[...e]:[""];if(r||n.length<=1)return{visible:n,mark:"",folded:!1,hidden:0};let o=n.length-1,i=` ${NL}${o}`,s=Math.max(1,t-i.length);return{visible:[_e(n[0]??"",s)],mark:i,folded:!0,hidden:o}}function Ix(e,t){return e==="all"||e==="focus"&&t}function Xc(e){if(e==="clip")return"focus";if(e==="focus")return"all";return"clip"}function _p(e,t,r,n=Ar){if(e.length===0)return{start:0,items:[],newer:0};if(!t){let a=Math.max(0,e.length-n);return{start:a,items:e.slice(a),newer:0}}let o=e.length-1,i=Math.max(0,Math.min(r,o)),s=e.slice(i,i+n);return{start:i,items:s,newer:Math.max(0,e.length-i-s.length)}}function Qc(e,t=Ar){return Math.max(0,e-t)}function Ep(e,t,r,n){let o=Math.max(t-1,0);if(e<0&&r>0)return{selected:0,startDelta:Math.max(-r,e)};if(e>o&&n>0)return{selected:o,startDelta:Math.min(n,e-o)};return{selected:Math.min(o,Math.max(0,e)),startDelta:0}}function Zc(e){if(e==="all")return"wrap all";if(e==="focus")return"wrap selected";return"clip"}function WL(e,t){let r=[],n=e;while(n.length>t){let o=n.slice(0,t),i=GL(o);if(i>0)r.push(n.slice(0,i).trimEnd()),n=n.slice(i).trimStart();else r.push(n.slice(0,t)),n=n.slice(t)}if(n.length>0)r.push(n);return r}function GL(e){let t=e.lastIndexOf(" "),r=e.lastIndexOf("\t"),n=e.lastIndexOf(","),o=Math.max(t,r,n);if(o<Math.floor(e.length*HL))return-1;return e[o]===","?o+1:o}function KL(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function qL(e,t=!1){let r=e.trim();if(r==="")return;if(t){let n=Di(r);if(n)return new RegExp(n.source,n.flags.includes("g")?n.flags:`${n.flags}g`)}return new RegExp(KL(r),"gi")}function VL(e,t){let r=e.map((a)=>a.text).join(""),n=[];t.lastIndex=0;let o=t.exec(r);while(o){let a=o[0]??"";if(a.length>0)n.push({start:o.index,end:o.index+a.length});if(a.length===0)t.lastIndex+=1;o=t.exec(r)}if(n.length===0)return e;let i=[],s=0;for(let a of e){let c=s,l=s+a.text.length,d=0;for(let u of n){let f=Math.max(u.start,c),p=Math.min(u.end,l);if(p<=f)continue;let m=f-c,h=p-c;if(m>d)i.push({text:a.text.slice(d,m),kind:a.kind});i.push({text:a.text.slice(m,h),kind:"search"}),d=h}if(d<a.text.length)i.push({text:a.text.slice(d),kind:a.kind});s=l}return i.filter((a)=>a.text!=="")}function Mx(e,t="",r=!1){let n=Yc(e),o=[],i=new RegExp(jL.source,"gi"),s=0,a=i.exec(n);while(a){let d=a[0]??"";if(a.index>s)o.push({text:n.slice(s,a.index),kind:"text"});o.push({text:d,kind:BL(d)}),s=a.index+d.length,a=i.exec(n)}if(s<n.length)o.push({text:n.slice(s),kind:"text"});let c=o.length===0?[{text:n,kind:"text"}]:o,l=qL(t,r);return l?VL(c,l):c}function Ox(e){let t=e.showTimestamps?qc:0,r=e.showMeta?Kc:0;return Math.max(Ex,e.width-t-e.serviceWidth-Vc-r-Tx-Es*Ax)}function $x(e,t){let r=t.reduce((s,a)=>Math.max(s,a.length),0),n=qc+Vc+Tx+Ex+Es*Ax,o=Math.max(Rx,e-n),i=Math.max(Rx,r);return Math.min(wp,o,i)}function si(e){return zL.has(e)}function Nx(e,t,r,n){let o=r===""?t:t.filter((a)=>a.timestamp>=r);if(o.length===0)return e;let i=Math.max(1,n);if(o.length>=i)return o.slice(-i);let s=Math.max(0,e.length+o.length-i);return e.slice(s).concat(o)}function Hx(e,t){let r=t.length>0?t[t.length-1].seq:-1,n=e.filter((o)=>o.seq>r);return[...t,...n]}function jx(e,t){if(t.length===0)return e;let r=new Set(e.map((o)=>o.seq)),n=t.filter((o)=>!r.has(o.seq));return n.length===0?e:[...n,...e]}function Tp(e,t,r){return e&&t<=0&&r}function Bx(e){return e.filter((t)=>t.level==="ERROR"||t.level==="FATAL").length}function xo(e,t){let r=t.services?.filter((l)=>l!=="")??[],n=t.service??"",o=(t.search??"").trim(),i=t.source??"",s=t.since??"",a=t.until??"",c;if(o!=="")if(t.regex===!0){let l=Di(o);if(l)c=(d)=>l.test(d);else c=(d)=>d.toLowerCase().includes(o.toLowerCase())}else{let l=o.toLowerCase();c=(d)=>d.toLowerCase().includes(l)}return e.filter((l)=>{if(r.length>0&&!r.includes(l.service))return!1;if(n!==""&&l.service!==n)return!1;if(i!==""&&l.source!==i)return!1;if(t.systemLogs===!1&&si(l.source))return!1;if(s!==""&&l.timestamp<s)return!1;if(a!==""&&l.timestamp>a)return!1;if(t.errorOnly===!0&&l.level!=="ERROR"&&l.level!=="FATAL")return!1;if(!c)return!0;return c(l.message)||c(l.service)})}function YL(e,t){let r={};for(let i of e)r[i.service]=(r[i.service]??0)+1;let n=t.map((i)=>({name:i,count:r[i]??0})),o=Object.keys(r).filter((i)=>!t.includes(i)).sort().map((i)=>({name:i,count:r[i]??0}));return[...n,...o]}function Ux(e,t,r=[]){let n=el(e,t,r);return[{name:"",count:t.length},...YL(t,n)]}function JL(e,t){let r=e.map((o)=>({name:o,count:t[o]??0})),n=Object.keys(t).filter((o)=>!e.includes(o)).sort().map((o)=>({name:o,count:t[o]??0}));return[...r,...n]}function Wx(e,t,r=[]){let n=Object.keys(t.byService).map((i)=>({service:i})),o=el(e,n,r);return[{name:"",count:t.total},...JL(o,t.byService)]}function Gx(e,t){if(e<=0)return"none started";return`${e}/${t} running`}function el(e,t,r=[]){let n=[...e],o=(i)=>{if(i!==""&&!n.includes(i))n.push(i)};for(let i of r)o(i);for(let i of t)o(i.service);return n}function Ap(e,t,r){let n=["",...e],o=n.indexOf(t),s=((o<0?0:o)+r+n.length)%n.length;return n[s]??""}var Kc=8,qc=9,Vc=7,Rx=10,Ex=16,Tx=2,Es=1,Ar=200,FL=2,_x=1,NL="\u25B8",Ax=2,HL=0.4,jL,UL,zL,Fx;var gr=D(()=>{ma();Ot();At();jL=/("[^"]*"|'[^']*'|\b(?:ERROR|FATAL|WARN(?:ING)?|FAIL(?:ED)?)\b|\b\d{3,5}\b)/gi;UL=/\x1b\[[0-9;?]*[ -/]*[@-~]|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|\x1b[@-Z\\-_]/g;zL=new Set(["auth","devctl","proxy"]),Fx=["devctl","mcp","auth"]});function ai(e){return e.length+QL}function Kx(e,t){if(t<eD)return _e(e,3);if(t<ZL)return _e(e,4);return e}function qx(e,t,r){let n=e.length;if(n===0||r<=0)return{start:0,end:-1};let o=t>=0&&t<n?t:0,i=Lp(e,o,r);if(i.start===0&&i.end===n-1)return i;let s=o>0?tl:0,a=o<n-1?tl:0,c=s+a,l=Lp(e,o,Math.max(e[o]??0,r-c)),d=(s>0&&l.start===0?tl:0)+(a>0&&l.end===n-1?tl:0);if(d===0)return l;return Lp(e,o,Math.max(e[o]??0,r-c+d))}function Lp(e,t,r){let n=e.length,o=t,i=t,s=e[t]??0;if(s>=r)return{start:t,end:t};while(o>0||i<n-1){let a=o>0?e[o-1]??0:Number.POSITIVE_INFINITY,c=i<n-1?e[i+1]??0:Number.POSITIVE_INFINITY,l=o>0&&s+a<=r,d=i<n-1&&s+c<=r;if(!l&&!d)break;let u=t-(o-1),f=i+1-t;if(l&&(!d||u<=f))o-=1,s+=a;else if(d)i+=1,s+=c;else break}return{start:o,end:i}}function Vx(e){if(e==="detail")return bo.findIndex((t)=>t.id==="services");return bo.findIndex((t)=>t.id===e)}function zx(e,t){if(e==="doctor")return t.doctor;if(e==="settings")return t.settings;if(e==="profiles")return t.profiles;if(e==="dashboard"||e==="services")return t.services;if(e==="logs")return t.logs??0;if(e==="mcp")return t.mcp??0;if(e==="setup")return XL;if(e==="config")return t.config??0;return 0}function Yx(e){if(e.length===1&&e>="1"&&e<="9")return bo[Number(e)-1]?.id;return}var XL=9,QL=2,tl=2,ZL=80,eD=56,bo,Dn;var In=D(()=>{Ot();bo=[{id:"dashboard",label:"dashboard"},{id:"services",label:"services"},{id:"logs",label:"logs"},{id:"proxy",label:"proxy"}];Dn=bo.map((e)=>e.id)});function Qx(e,t=!0){let r=e<rl?Jx+1:Jx,n=t?sD:0;return r+n+oD+n+iD+n}function li(e){return Math.max(rD,e-nD*2)}function Zx(e){let t=e.width<tD,r=e.proxyOn?`\u25CF ${_e(e.proxyAddress,t?10:18)}`:"";return[{label:t?`${e.running}/${e.total}`:Gx(e.running,e.total),tone:e.running>0?"success":"idle"},{label:r,tone:"info",hide:!e.proxyOn},{label:"MCP",tone:"info",hide:!e.mcpOn},{label:e.adc?t?"ADC":"ADC ok":t?"!ADC":"ADC missing",tone:e.adc?"success":"error"},{label:t?"sec":"secrets shown",tone:"warning",hide:!e.reveal}]}function Xx(e){return e.key==="/"&&e.label==="command"}function aD(e){return e.key.length+e.label.length+3}function eb(e,t){let r=e.filter(Xx),n=e.filter((s)=>!Xx(s)),o=[],i=0;for(let s of[...r,...n]){let a=aD(s);if(i+a>t)break;o.push(s),i+=a}return o}function nl(e,t,r,n,o="center",i=!0){let s=Math.min(r,Math.max(1,e-4)),a=Math.min(Qx(e,i),Math.max(0,t-1)),c=Math.min(n,Math.max(1,t-a)),l=Math.max(0,Math.min(Math.floor((e-s)/2),Math.max(0,e-s))),d=o==="bottom"?t-2-c:Math.floor((t-c)/2),u=Math.max(0,Math.min(d,t-c));return{left:l,top:u,width:s,height:c}}function tb(e){if(e==="")return"idle";if(lD.test(e))return"error";if(e.includes("session only"))return"warning";if(cD.test(e))return"success";return"info"}function rb(e,t,r){if(e==="restart-cascade")return{title:"Restart dependents?",body:`${r?.services?.join(", ")||"selected services"} has dependents. Enter restarts only the named services. Press c to cascade (same as /restart --cascade).`};if(e==="quit")return{title:"Quit",body:"Stop managed services and leave the TUI? Press d to detach (daemon stays). Press k to stop the daemon and leave services running (devctl down --keep-services)."};if(e==="reload")return{title:"Reload applied",body:t===""?"Configuration changed. Restart marked services?":`Restart required: ${t}`};if(e==="reset-prefs")return{title:"Reset preferences",body:"Restore theme, display size, mouse, and leader timeout to defaults? This overwrites your saved tui.json values."};if(e==="free-port"){let n=r?.port??0,o=r?.process||"process",i=r?.pid??0;return{title:`Free port ${n}`,body:`Stop ${o} (pid ${i}) so port ${n} can be used? This sends SIGTERM, then SIGKILL if it stays up.`}}return{title:"Start profile",body:t===""?"Start the configured services?":`Start profile ${t}?`}}function nb(e){if(e==="quit")return[{key:"enter",label:"stop services"},{key:"d",label:"detach"},{key:"k",label:"down, keep services"},{key:"esc",label:"stay"}];if(e==="restart-cascade")return[{key:"enter",label:"named only"},{key:"c",label:"cascade"},{key:"esc",label:"stay"}];return[{key:"enter",label:"confirm"},{key:"esc",label:"stay"}]}function ob(e){let t=e.payload?.error;return typeof t==="string"&&t!==""?t:"configuration reload failed"}var ci=100,rl=90,tD=60,rD=8,nD=6,Jx=1,oD=1,iD=1,sD=1,XU,cD,lD;var vr=D(()=>{Ot();gr();In();XU=Qx(rl-1);cD=/^(started|stopped|restarted|refreshed|cleared|copied|exported|jumped|proxy started|proxy stopped|secrets|re-running|theme |mouse |leader |display |restored)/i,lD=/\b(fail|error|unknown|not found|no configuration|blocked|already in use|is using port)\b/i});function ol(){return[{name:"start",aliases:["up"],desc:"Start selected services or the current profile",leader:"n",group:"services",usage:"<service>"},{name:"stop",aliases:[],desc:"Stop selected services",leader:"x",group:"services",usage:"<service>"},{name:"restart",aliases:[],desc:"Restart selected services",leader:"R",group:"services",usage:"<service>",suggest:[{token:"--cascade",desc:"Restart selected services and their dependents"}]},{name:"run",aliases:["task"],desc:"Run a one-off task; empty /run opens a picker",leader:"",group:"services",usage:"<task>"},{name:"exec",aliases:[],desc:"Run a command in a service context; empty /exec opens a picker",leader:"",group:"services",usage:"<service>"},{name:"services",aliases:["s"],desc:"Open the services screen",leader:"s",group:"nav"},{name:"logs",aliases:["l"],desc:"Open the log viewer",leader:"l",group:"nav"},{name:"auth",aliases:["identity","a"],desc:"Open identity",leader:"a",group:"nav",suggest:[{token:"login",desc:"Run gcloud ADC login"},{token:"logout",desc:"Revoke application-default credentials"},{token:"refresh",desc:"Probe identities"}]},{name:"credentials",aliases:["creds"],desc:"Open credential store status",leader:"",group:"nav"},{name:"reload",aliases:[],desc:"Reload configuration",leader:"",group:"ui"},{name:"proxy",aliases:["p"],desc:"Open the proxy screen",leader:"p",group:"nav"},{name:"mcp",aliases:["agent"],desc:"Open the MCP server screen for coding agents",leader:"",group:"nav"},{name:"doctor",aliases:["d"],desc:"Run environment diagnostics",leader:"d",group:"nav"},{name:"stats",aliases:["metrics"],desc:"View system and service statistics",leader:"m",group:"nav"},{name:"config",aliases:["c"],desc:"View merged configuration",leader:"c",group:"nav"},{name:"import",aliases:[],desc:"Preview or write a Compose mapping",leader:"",group:"ui",suggest:[{token:"compose",desc:"Preview a Compose mapping; add --write to save"}]},{name:"diff",aliases:["provenance"],desc:"Show winning config sources and what they shadowed",leader:"",group:"ui"},{name:"daemon",aliases:["bootstrap"],desc:"Show supervisor bootstrap logs (same file as devctl daemon logs)",leader:"",group:"app"},{name:"update",aliases:[],desc:"Install a newer GitHub Release when the install method is known",leader:"",group:"app"},{name:"profiles",aliases:["o"],desc:"Select a development profile",leader:"o",group:"nav"},{name:"setup",aliases:["init"],desc:"Open setup guidance",leader:"i",group:"nav"},{name:"dashboard",aliases:["home"],desc:"Return to the dashboard",leader:"h",group:"nav"},{name:"themes",aliases:["theme"],desc:"List available themes",leader:"t",group:"ui"},{name:"settings",aliases:["prefs","preferences"],desc:"Open TUI settings (theme, mouse, MCP page)",leader:"",group:"ui"},{name:"help",aliases:["?"],desc:"Show the help dialog",leader:"",group:"ui"},{name:"refresh",aliases:[],desc:"Refresh status and logs",leader:"r",group:"ui"},{name:"regex",aliases:[],desc:"Toggle regex log search",leader:"",group:"logs"},{name:"since",aliases:[],desc:"Filter logs after an ISO timestamp",leader:"",group:"logs",usage:"<timestamp>"},{name:"until",aliases:[],desc:"Filter logs before an ISO timestamp",leader:"",group:"logs",usage:"<timestamp>"},{name:"history",aliases:[],desc:"Load a persisted log session",leader:"",group:"logs",usage:"<session>"},{name:"edit",aliases:[],desc:"Open configuration in $EDITOR",leader:"",group:"ui"},{name:"buffer",aliases:[],desc:"Edit configuration in a validate/save buffer",leader:"",group:"ui"},{name:"pause",aliases:[],desc:"Pause or resume live logs",leader:"",group:"logs"},{name:"fullscreen",aliases:["zen","expand"],desc:"Expand logs to fill the terminal",leader:"z",group:"logs"},{name:"split",aliases:[],desc:"Split the logs screen into two service panes",leader:"",group:"logs"},{name:"trace",aliases:[],desc:"Search logs for a request or trace id",leader:"",group:"logs",usage:"<id>"},{name:"filter",aliases:[],desc:"Toggle ERROR+ log filter",leader:"",group:"logs"},{name:"system",aliases:["internal"],desc:"Show or hide internal auth/mcp/devctl/proxy logs",leader:"",group:"logs"},{name:"reveal",aliases:[],desc:"Reveal or hide secret environment values",leader:"",group:"ui"},{name:"wrap",aliases:[],desc:"Cycle log wrap: selected, all lines, or clip",leader:"",group:"logs"},{name:"copy",aliases:[],desc:"Copy the highlighted selection to the clipboard",leader:"",group:"ui"},{name:"export",aliases:[],desc:"Write filtered logs to ~/.devctl/exports",leader:"e",group:"logs"},{name:"exports",aliases:["open-exports"],desc:"Open the log export folder",leader:"",group:"logs"},{name:"clear",aliases:["new"],desc:"Clear the on-screen log buffer",leader:"",group:"logs"},{name:"version",aliases:["v"],desc:"Show the current devctl version",leader:"",group:"app"},{name:"down",aliases:[],desc:"Stop the supervisor",leader:"",group:"app",suggest:[{token:"--keep-services",desc:"Stop the supervisor and leave processes running"}]},{name:"exit",aliases:["quit","q"],desc:"Exit (detach or stop services)",leader:"q",group:"app"}]}function wo(e){let t=e.trim().toLowerCase().replace(/^\//,""),r=t.indexOf(" ");return r===-1?t:t.slice(0,r)}function lb(e){let t=wo(e);if(t==="")return ol();return ol().map((r)=>({command:r,score:CD(r,t)})).filter((r)=>r.score>0).sort((r,n)=>n.score-r.score||r.command.name.localeCompare(n.command.name)).map((r)=>r.command)}function CD(e,t){if(t==="")return 1;let r=e.name.toLowerCase(),n=e.aliases.map((o)=>o.toLowerCase());return RD(r,n,t)||_D(r,n,t)||ED(r,n,t)||TD(r,n,t)||PD(e.suggest,t)||AD(e.desc.toLowerCase(),t)}function PD(e,t){if(!e||e.length===0)return 0;let r=e.map((n)=>n.token.toLowerCase());if(r.some((n)=>n===t||n.replace(/^-+/,"")===t))return sb;if(t.length<cb)return 0;return r.some((n)=>n.includes(t)||n.replace(/^-+/,"").startsWith(t))?ab:0}function RD(e,t,r){if(e===r)return dD;return t.includes(r)?uD:0}function _D(e,t,r){if(e.startsWith(r))return pD;if(t.some((i)=>i.startsWith(r)))return fD;let n=e.split("-");return n.length>1&&n.some((i)=>i.startsWith(r))?mD:0}function ED(e,t,r){if(r.length<cb)return 0;if(e.includes(r))return sb;return t.some((n)=>n.includes(r))?ab:0}function TD(e,t,r){if(r.length<xD)return 0;let n=ib(e,r);if(n>0)return hD+Math.min(n,kD);return t.some((o)=>ib(o,r)>0)?gD:0}function ib(e,t){let r=0,n=0,o=0;for(let i=0;i<t.length;i+=1){let s=t[i];if(s===void 0)return 0;let a=e.indexOf(s,r);if(a<0)return 0;if(r>0&&a===r)n+=1,o+=2+n;else n=0,o+=a===0?4:1;r=a+1}return o}function AD(e,t){if(t.length<bD)return 0;if(e.split(/[^a-z0-9+]+/).filter((n)=>n.length>=wD).some((n)=>n.startsWith(t)))return vD;return t.length>=SD&&e.includes(t)?yD:0}function Wr(e){let t=e.trim().toLowerCase().replace(/^\//,"").split(" ")[0]??"";return ol().find((r)=>r.name===t||r.aliases.includes(t))}function Dp(e){return e.trim().replace(/^\//,"").split(/\s+/).slice(1).filter((r)=>r!=="")}function db(e){return ol().find((r)=>r.leader===e)?.name??""}function ub(e){let t=e.includes("--cascade")||e.includes("-c");return{services:e.filter((n)=>n!=="--cascade"&&n!=="-c"),cascade:t}}function pb(e){let t=e.includes("--print-env"),r=e.includes("--reveal"),n=e.filter((o)=>o!=="--print-env"&&o!=="--reveal"&&o!=="--");return{service:n[0]??"",printEnv:t,reveal:r,command:n.slice(1)}}var dD=100,uD=90,pD=80,fD=70,mD=60,sb=50,ab=45,hD=30,gD=28,vD=20,yD=10,cb=2,xD=3,bD=4,wD=4,SD=4,kD=9;function gb(e){return e==="detail"?"services":e}function vb(e){let t=Dn.indexOf(gb(e));if(t<0)return"dashboard";return Dn[(t+1)%Dn.length]??"dashboard"}function yb(e){let t=Dn.indexOf(gb(e));if(t<0)return"dashboard";return Dn[(t-1+Dn.length)%Dn.length]??"dashboard"}function Mp(e){return["services","nav","logs","ui","app","tasks"].flatMap((r)=>{let n=e.filter((o)=>o.group===r);if(n.length===0)return[];return[{group:r,items:n}]})}function il(e){return e.hint?`${e.name} ${e.hint}`:e.name}function Op(e){return e.name}function xb(e){if(e.hint)return`${e.hint} \xB7 ${e.desc}`;let t=[e.usage,...(e.suggest??[]).map((r)=>r.token)].filter((r)=>Boolean(r));if(t.length===0)return e.desc;return`${e.desc} \xB7 ${t.join(" \xB7 ")}`}function bb(e){let t=e.reduce((n,o)=>Math.max(n,Op(o).length),0),r=Math.min(Ip,Math.max(t,1));return LD+r+DD}function wb(e){return e.hint?`${e.name} ${e.hint} `:`${e.name} `}function Sb(e,t){if(e.hint)return e.hint.split(/\s+/);return Wr(t)?.name===e.name?Dp(t):[]}function fb(e,t){if(t==="")return!0;let r=t.toLowerCase(),n=e.toLowerCase();return n.startsWith(r)||n.includes(r)}function ID(e,t){if(e.name==="start"||e.name==="stop"||e.name==="restart")return(t.services??[]).map((r)=>({token:r,desc:`${e.name} ${r}`}));if(e.name==="run")return(t.tasks??[]).map((r)=>({token:r,desc:`run ${r}`}));if(e.name==="exec")return(t.services??[]).map((r)=>({token:r,desc:`exec in ${r}`}));return[]}function mb(e,t){return{...e,hint:t.token,desc:t.desc,usage:void 0,suggest:void 0}}function MD(e,t,r={}){let n=Dp(t).join(" "),o=t.trim().includes(" ")||t.endsWith(" "),i=wo(t),s=[];for(let a of e){if(s.push({...a,hint:void 0}),i==="")continue;let c=a.name===i||a.aliases.includes(i),l=c?n:i;for(let d of a.suggest??[])if(fb(d.token,l))s.push(mb(a,d));if(!c||!o)continue;for(let d of ID(a,r))if(fb(d.token,n))s.push(mb(a,d))}return s}function kb(e,t={}){let r=lb(e),n=wo(e)===""?Mp(r).flatMap((o)=>o.items):r;return MD(n,e,t)}function $p(e,t,r,n){let o=wo(t),i=[...e].sort().map((s)=>({name:s,aliases:[],desc:n,leader:"",group:r}));if(o==="")return i;return i.filter((s)=>s.name.toLowerCase().includes(o))}function Cb(e,t,r=Zr()){if(t==="slash")return[{key:"\u2191\u2193",label:"suggest"},{key:"tab",label:"complete"},{key:"enter",label:"run"},{key:"esc",label:"cancel"}];if(t==="themes")return[Gr,{key:"\u2191\u2193",label:"move"},{key:"enter",label:"save"},{key:"esc",label:"revert"}];if(t==="help")return[Gr,{key:"j/k",label:"scroll"},{key:"esc",label:"close"}];if(t==="log-details"||t==="scroll-text")return[Gr,{key:"j/k",label:"scroll"},{key:Ri(r),label:"copy"},{key:"esc",label:"close"}];if(t==="confirm")return[{key:"enter",label:"confirm"},{key:"esc",label:"stay"}];if(t==="plan")return[Gr,{key:"esc",label:"back to dashboard"},{key:"enter",label:"done"}];if(t==="leader")return[Gr,...Fp()];if(t==="config-edit")return[{key:It("s"),label:"save"},{key:"esc",label:"discard"}];return OD(e,r)}function Fp(){return[{key:"n",label:"start"},{key:"x",label:"stop"},{key:"R",label:"restart"},{key:"s",label:"services"},{key:"l",label:"logs"},{key:"t",label:"themes"},{key:"q",label:"quit"}]}function OD(e,t){let r=[{key:Ri(t),label:"copy"},Gr,{key:"?",label:"help"}];switch(e){case"dashboard":return[{key:"space",label:"select"},{key:"*",label:"all"},{key:"-",label:"none"},{key:"enter",label:"start or open"},{key:"n",label:"start"},{key:"x",label:"stop"},{key:"r",label:"refresh"},{key:"R",label:"restart"},{key:"\u2190\u2192",label:"log filter"},{key:"g",label:"latest"},{key:"z",label:"full logs"},{key:"i",label:"internal logs"},{key:It("l"),label:"clear logs"},{key:"j/k",label:"move"},...r];case"services":return[{key:"enter",label:"detail"},{key:"space",label:"select"},{key:"*",label:"all"},{key:"-",label:"none"},{key:"n",label:"start"},{key:"x",label:"stop"},{key:"r",label:"refresh"},{key:"R",label:"restart"},...r];case"detail":return[{key:"j/k",label:"scroll env"},{key:"n",label:"start"},{key:"x",label:"stop"},{key:"o",label:"config"},{key:"l",label:"logs"},{key:"esc",label:"back"},...r];case"logs":return[{key:"\u2190\u2192",label:"filter"},{key:"e",label:"errors"},{key:"i",label:"internal logs"},{key:It("l"),label:"clear logs"},{key:"g",label:"latest"},{key:"f",label:"search"},{key:"t",label:"time"},{key:"m",label:"meta"},{key:"w",label:"wrap"},{key:"j/k",label:"move"},{key:"p",label:"pause"},{key:"z",label:"full screen"},{key:"\\",label:"split"},{key:"/exports",label:"open folder"},...r];case"profiles":return[{key:"space",label:"set current"},{key:"enter",label:"set and start"},{key:"j/k",label:"move"},...r];case"proxy":return[{key:"n",label:"start proxy"},{key:"x",label:"stop proxy"},...r];case"mcp":return[{key:"j/k",label:"move"},{key:"space",label:"start or copy"},{key:"\u2190\u2192",label:"change port"},{key:"enter",label:"start or copy"},...r];case"config":return[{key:"enter",label:"run task"},{key:"v",label:"buffer"},{key:"e",label:"editor"},{key:"/diff",label:"sources"},{key:"/reload",label:"reload"},{key:"j/k",label:"scroll or select task"},...r];case"setup":return[{key:"j/k",label:"steps"},{key:"enter",label:"continue"},{key:"esc",label:"back"},{key:"esc\xD72",label:"quit"},...r];case"doctor":return[{key:"r",label:"run doctor again"},{key:"j/k",label:"move"},{key:"enter",label:"fix port"},...r];case"auth":return[{key:"r",label:"probe identities"},{key:"/auth login",label:"ADC login"},{key:"/auth logout",label:"revoke ADC"},{key:"/auth refresh",label:"probe"},...r];case"settings":return[{key:"j/k",label:"move"},{key:"\u2190\u2192",label:"save"},{key:"enter",label:"apply or open page"},{key:"space",label:"toggle"},...r];default:return[{key:"tab",label:"screens"},...r]}}function Pb(e,t,r){if(r<=t||e<t)return 0;return Math.min(e-t+1,r-t)}function hb(e,t,r){let n=e.slice(t,r+1),o=new Set(n.map((i)=>i.group));return n.length+o.size}function Rb(e,t,r){if(e.length===0)return[];let n=Math.min(Math.max(t,0),e.length-1),o=n,i=n;for(;;){let s=i+1<e.length&&hb(e,o,i+1)<=r,a=o>0&&hb(e,o-1,i)<=r;if(s)i+=1;else if(a)o-=1;else break}return e.slice(o,i+1)}function di(e,t){if(e.length===0)return;let r=Math.min(Math.max(t,0),e.length-1);return e[r]}var Gr,LD=3,DD=2,Ip=22;var So=D(()=>{_r();In();Gr={key:"/",label:"command"}});function al(e,t){let r=t??Object.keys(e?.services??{});return{running:r.filter((o)=>yo(e?.services[o])).length,total:r.length}}function _b(e,t){let r=0,n=0,o=0,i=0,s=0,a=0;for(let c of e){let l=t?.services[c];if(l?.health==="HEALTHY")o+=1;let d=l?.state??"STOPPED";if(d==="FAILED")i+=1;else if(d==="STOPPING")s+=1;else if(d==="STARTING"||d==="RESTARTING")n+=1;else if(_s(d))r+=1;else a+=1}return{total:e.length,live:r+n,running:r,starting:n,healthy:o,failed:i,stopping:s,stopped:a}}function Eb(e,t){if(!Number.isFinite(e)||!Number.isFinite(t)||t<=0)return 0;return e/t}function BD(e,t,r){if(!Number.isFinite(e))return"success";if(e>r)return"error";if(e>t)return"warning";return"success"}function UD(e,t){return BD(Eb(e,t),$D,FD)}function Bp(e,t=24){if(e.length===0||t<=0)return"";let r=e.length>t?e.slice(e.length-t):e,n=Math.max(...r,0);if(n<=0)return Ts[0]?.repeat(r.length)??"";return r.map((o)=>{let i=Number.isFinite(o)?Math.min(1,Math.max(0,o/n)):0,s=Math.min(Ts.length-1,Math.round(i*(Ts.length-1)));return Ts[s]??Ts[0]}).join("")}function WD(e,t){let r=t>0?e/t:1;if(r<HD)return"error";if(r<ND)return"warning";return"success"}function Np(e){if(!Number.isFinite(e)||e<0)return"\u2014";return e.toFixed(2)}function GD(e){if(!Number.isFinite(e)||e<0)return"\u2014";return`${Math.round(e*100)}%`}function Tb(e,t=Date.now()){if(!e?.startTime)return"\u2014";let r=new Date(e.startTime).getTime();if(!Number.isFinite(r))return"\u2014";return ws(t-r)}function Ab(e,t=jD){return Object.entries(e).sort((r,n)=>n[1]-r[1]||r[0].localeCompare(n[0])).slice(0,t)}function Lb(e,t=1){return Math.max(24,e-XD-t*2-QD-ZD-eI)}function Db(e){let t=e>=KD,r=e>=qD,n=e>=VD,o=e>=zD,i=e>=YD,s=e>=JD,a=Tr+$t;if(t)a+=Ur+$t;if(r)a+=Cs+$t;if(n)a+=Ps+$t;if(o)a+=ks+$t;if(i)a+=cl+$t;if(s)a+=sn;return{name:Math.max($c,e-a),health:t,cpu:r,mem:n,up:o,rst:i,pid:s}}function Ib(e){let t=Number.isFinite(e)&&e>0?e:0;return{ratio:Math.min(1,t),label:GD(t)}}function Mb(e,t=nI){return`[${ii(e.ratio,t)}] ${e.label.padStart(4)}`}function Up(e,t){let r=e.reduce((l,d)=>Math.max(l,d.what.length),4),n=e.reduce((l,d)=>Math.max(l,d.reading.length),7),o=e.some((l)=>l.meter)?oI:0,i=ui*(o>0?3:2),s=Math.min(tI,Math.max(Hp,r)),a=Math.min(rI,Math.max(jp,n)),c=t-s-a-o-i;if(c<sl&&a>jp)a=Math.max(jp,a-(sl-c)),c=t-s-a-o-i;if(c<sl&&s>Hp)s=Math.max(Hp,s-(sl-c)),c=t-s-a-o-i;return{what:s,reading:a,meaning:Math.max(0,c),meter:o}}function Ob(e){return iI.has((e?.health.type??"").toLowerCase())}function $b(e){if(e==="darwin")return"macOS";if(e==="win32")return"Windows";if(e==="linux")return"Linux";return e||"this computer"}function Fb(e,t){let r=Math.max(1,t),n=UD(e,r),o=n==="error"?"overloaded":n==="warning"?"busy":"not busy",i=n==="error"?`${r} cores are queued up \u2014 last minute ${Np(e)}`:n==="warning"?`${r} cores nearly full \u2014 last minute ${Np(e)}`:`${r} cores. Last minute ${Np(e)} (under ${r} is fine)`;return{what:"CPU work",reading:o,meaning:i,tone:n,meter:Ib(Eb(e,r))}}function Nb(e,t){let r=WD(e,t),n=`${Ss(e)} of ${Ss(t)}`,o=r==="error"?"Almost no RAM left for new work":r==="warning"?"RAM is getting tight":"RAM the computer can still give out",i=t>0?Math.max(0,(t-e)/t):0;return{what:"RAM leftover",reading:n,meaning:o,tone:r,meter:Ib(i)}}function Hb(e){let t=e?.state??"STOPPED";if(t==="FAILED")return"crashed";if(t==="STOPPING")return"stopping";if(t==="STARTING"||t==="RESTARTING")return"starting";if(_s(t))return"up";return"off"}function jb(e){if(!e||!_s(e.state))return"\u2014";if(e.health==="HEALTHY")return"ready";if(e.health==="UNHEALTHY")return"failing";if(e.state==="STARTING"||e.state==="RESTARTING")return"waiting";return"checking"}function Bb(e){if(e==="keychain")return"password store";if(e==="file")return"local file";return e||"unknown store"}function Ub(e,t){let r=[{what:"Started",reading:String(e.live),meaning:e.live===0?"nothing is running yet":"process is up",tone:e.live>0?"success":"muted"}];if(t)r.push({what:"Ready",reading:String(e.healthy),meaning:e.live===0?"start services first":"passed their health check",tone:e.live>0&&e.healthy<e.live?"warning":e.healthy>0?"success":"muted"});if(e.starting>0)r.push({what:"Still starting",reading:String(e.starting),meaning:"not ready for traffic yet",tone:"warning"});if(e.stopping>0)r.push({what:"Stopping",reading:String(e.stopping),meaning:"shutting down",tone:"warning"});return r.push({what:"Crashed",reading:String(e.failed),meaning:e.failed>0?"open the service for the error":"none",tone:e.failed>0?"error":"muted"}),r.push({what:"Not started",reading:String(e.stopped),meaning:e.stopped>0?"off until you start them":"all are up",tone:"muted"}),r}var $D=0.85,FD=1.5,ND=0.2,HD=0.1,jD=5,Ts="\u2581\u2582\u2583\u2584\u2585\u2586\u2587\u2588",KD=52,qD=64,VD=72,zD=82,YD=90,JD=100,XD=2,QD=2,ZD=2,eI=1,cl=8,Hp=8,tI=22,jp=6,rI=28,sl=12,ui=2,nI=10,oI=18,iI;var ll=D(()=>{Ot();At();iI=new Set(["http","tcp","command"])});import{useEffect as sI,useRef as aI}from"react";import{jsxDEV as me,Fragment as lI}from"@opentui/react/jsx-dev-runtime";function cI(e,t){switch(t){case"primary":return{bg:e.primary,fg:ho(e.primary,e.inverse)};case"accent":return{bg:e.accent,fg:Ac(e)?Tc:ho(e.accent,e.inverse)};case"success":return{bg:e.success,fg:ho(e.success,e.inverse)};case"warning":return{bg:e.warning,fg:Ac(e)?Tc:ho(e.warning,e.inverse)};case"error":return{bg:e.error,fg:ho(e.error,e.inverse)};case"info":return{bg:e.info,fg:ho(e.info,e.inverse)};case"muted":return{bg:e.element,fg:e.text};case"ghost":return{bg:e.element,fg:e.muted};default:return{bg:e.element,fg:e.text}}}function Wb(e){let{palette:t,dir:r}=e;return me("box",{width:2,height:1,flexShrink:0,overflow:"hidden",children:me("text",{fg:t.muted,children:r==="left"?"\u2039":"\u203A"},void 0,!1,void 0,this)},void 0,!1,void 0,this)}function Ut(e){let{palette:t,children:r,backgroundColor:n,edge:o="bottom"}=e,i=ye(),s=e.ruled??!go(i),a=n??t.element,c=me("box",{flexShrink:0,flexDirection:"column",backgroundColor:a,overflow:"hidden",children:r},void 0,!1,void 0,this);if(!s)return c;return me("box",{flexShrink:0,flexDirection:"column",overflow:"hidden",border:[...o==="both"?["top","bottom"]:[o]],borderStyle:"single",borderColor:t.border,children:c},void 0,!1,void 0,this)}function dl(e){let{palette:t,items:r,active:n,width:o,onPick:i,emphasis:s="ghost"}=e,a=r.map((u)=>ai(u.label)),c=qx(a,n,Math.max(1,o)),l=c.start>0,d=c.end>=0&&c.end<r.length-1;return me("box",{height:1,flexDirection:"row",overflow:"hidden",backgroundColor:t.element,children:[l?me(Wb,{palette:t,dir:"left"},void 0,!1,void 0,this):null,r.map((u,f)=>{if(f<c.start||f>c.end)return null;let p=f===n,m=s==="fill"&&p;return me(Be,{palette:t,label:u.label,tone:m?"primary":"ghost",bg:m?void 0:p?t.highlight:t.element,fg:m?void 0:p?t.primary:u.color??t.muted,onMouseDown:()=>i(f)},u.id,!1,void 0,this)}),d?me(Wb,{palette:t,dir:"right"},void 0,!1,void 0,this):null]},void 0,!0,void 0,this)}function Be(e){let{palette:t,label:r,tone:n="idle",fg:o,bg:i,onMouseDown:s}=e,a=cI(t,n);return me("box",{height:1,paddingLeft:1,paddingRight:1,backgroundColor:i??a.bg,overflow:"hidden",flexShrink:0,onMouseDown:s,children:me("text",{fg:o??a.fg,children:r},void 0,!1,void 0,this)},void 0,!1,void 0,this)}function it(e){let{palette:t,hints:r}=e;return me("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,children:r.map((n)=>me("box",{height:1,paddingLeft:1,paddingRight:1,flexShrink:0,overflow:"hidden",children:me("text",{wrapMode:"none",children:[me("span",{fg:t.primary,children:n.key},void 0,!1,void 0,this),me("span",{fg:t.muted,children:` ${n.label}`},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},`${n.key}-${n.label}`,!1,void 0,this))},void 0,!1,void 0,this)}function ut(e){let{palette:t,items:r,hints:n,ruled:o=!0}=e,i=me("box",{height:1,flexDirection:"row",overflow:"hidden",backgroundColor:t.element,children:[r.map((s,a)=>me(Be,{palette:t,label:s.text,tone:s.tone,onMouseDown:s.onMouseDown},`${s.text}-${a}`,!1,void 0,this)),n&&n.length>0?me(lI,{children:[me("box",{flexGrow:1,backgroundColor:t.element},void 0,!1,void 0,this),me(it,{palette:t,hints:n},void 0,!1,void 0,this)]},void 0,!0,void 0,this):null]},void 0,!0,void 0,this);if(!o)return i;return me(Ut,{palette:t,backgroundColor:t.element,children:i},void 0,!1,void 0,this)}function pt(e){let{palette:t,title:r,termW:n,termH:o,preferW:i,preferH:s,anchor:a="center",borderColor:c,bottomTitle:l,gap:d,children:u}=e,f=go(ye()),p=nl(n,o,i,s,a,!f);return me("box",{position:"absolute",left:p.left,top:p.top,width:p.width,height:p.height,border:!0,borderStyle:Wp,borderColor:c??t.borderActive,backgroundColor:t.panel,padding:f?0:1,title:r,titleColor:t.primary,bottomTitle:l,gap:f?0:d,flexDirection:"column",overflow:"hidden",children:u},void 0,!1,void 0,this)}function Ue(e){return{rootOptions:{flexGrow:1,height:"100%",overflow:"hidden",backgroundColor:e.panel},viewportOptions:{backgroundColor:e.panel},contentOptions:{backgroundColor:e.panel},scrollbarOptions:{trackOptions:{foregroundColor:e.primary,backgroundColor:e.element}}}}function rt(e,t){if(!e)return;e.scrollBy({x:0,y:t})}function pi(e,t){let r=aI(null);return sI(()=>{let n=r.current;if(!n||e<0)return;let o=requestAnimationFrame(()=>{n.scrollChildIntoView(`${t}-${e}`)});return()=>cancelAnimationFrame(o)},[t,e]),r}function kt(e){let t=ye(),r=bs(t),n=e.scroll?me("scrollbox",{focused:!1,stickyScroll:!1,scrollX:!1,style:Ue(e.palette),children:me("box",{flexDirection:"column",overflow:"hidden",children:e.children},void 0,!1,void 0,this)},void 0,!1,void 0,this):e.children;return me("box",{flexGrow:1,border:!0,borderStyle:Wp,borderColor:e.borderColor??e.palette.border,title:e.title,titleColor:e.palette.primary,paddingLeft:t.pad,paddingRight:t.pad,paddingTop:r?0:t.pad,paddingBottom:r?0:t.pad,backgroundColor:e.palette.panel,flexDirection:"column",overflow:"hidden",children:n},void 0,!1,void 0,this)}function ko(e){let{palette:t,label:r,value:n,tone:o="text"}=e,i=ye(),s=o==="success"?t.success:o==="warning"?t.warning:o==="error"?t.error:o==="muted"?t.muted:t.text;return me("box",{height:i.rowH,flexDirection:"row",overflow:"hidden",children:[me("box",{width:14,flexShrink:0,overflow:"hidden",children:me("text",{fg:t.muted,children:r},void 0,!1,void 0,this)},void 0,!1,void 0,this),me("box",{flexGrow:1,overflow:"hidden",children:me("text",{fg:s,wrapMode:"none",children:n},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function ul(e){let{palette:t,title:r,body:n,hint:o}=e;return me("box",{paddingLeft:1,paddingRight:1,flexDirection:"column",overflow:"hidden",border:!0,borderStyle:Wp,borderColor:t.borderActive,titleColor:t.primary,children:[me("text",{fg:t.primary,children:r},void 0,!1,void 0,this),me("text",{fg:t.text,wrapMode:"word",children:n},void 0,!1,void 0,this),o?me("text",{fg:t.muted,wrapMode:"word",children:o},void 0,!1,void 0,this):null]},void 0,!0,void 0,this)}var Wp="rounded";var ke=D(()=>{Mt();vr();In();Er();tt()});import{jsxDEV as $e,Fragment as uI}from"@opentui/react/jsx-dev-runtime";function Kb(e){let{palette:t,cfg:r,snap:n,google:o,profile:i,reveal:s,width:a}=e,c=al(n,r?Object.keys(r.services):void 0),l=n?.proxy.running===!0,d=o?.adcAvailable===!0,u=a<rl,f=_e(r?.project.name||"local",u?18:22),p=_e(i||n?.profile||"no profile",16),m=Zx({width:a,running:c.running,total:c.total,proxyOn:l,proxyAddress:n?.proxy.address??"",mcpOn:n?.mcp?.running===!0,adc:d,reveal:s}).filter((x)=>!x.hide&&x.label!=="").map((x)=>$e(Be,{palette:t,label:x.label,tone:x.tone},x.label,!1,void 0,this)),h=bs(ye()),v=$e("box",{height:1,flexGrow:1,overflow:"hidden",backgroundColor:t.panel,paddingLeft:1,children:$e("text",{wrapMode:"none",children:[$e("span",{fg:t.primary,children:er()},void 0,!1,void 0,this),$e("span",{fg:t.muted,children:` ${f}`},void 0,!1,void 0,this),$e("span",{fg:t.accent,children:` ${p}`},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this);return $e(Ut,{palette:t,backgroundColor:t.panel,ruled:!h,children:$e("box",{height:u?2:1,flexDirection:"column",backgroundColor:t.panel,overflow:"hidden",children:[$e("box",{height:1,flexDirection:"row",overflow:"hidden",children:[v,u?null:m]},void 0,!0,void 0,this),u?$e("box",{height:1,flexDirection:"row",overflow:"hidden",backgroundColor:t.panel,children:m},void 0,!1,void 0,this):null]},void 0,!0,void 0,this)},void 0,!1,void 0,this)}function qb(e){let{palette:t,screen:r,width:n,onSelect:o}=e,i=n>=Gb+dI,s=i?Math.max(1,n-Gb):n,a=bo.map((l)=>({id:l.id,label:Kx(l.label,s)})),c=Vx(r);return $e(Ut,{palette:t,backgroundColor:t.element,children:$e("box",{height:1,flexDirection:"row",overflow:"hidden",backgroundColor:t.element,children:[$e(dl,{palette:t,items:a,active:c,width:s,onPick:(l)=>{let d=bo[l];if(d)o(d.id)}},void 0,!1,void 0,this),i?$e(uI,{children:[$e("box",{flexGrow:1,backgroundColor:t.element},void 0,!1,void 0,this),$e(it,{palette:t,hints:[Gr]},void 0,!1,void 0,this)]},void 0,!0,void 0,this):null]},void 0,!0,void 0,this)},void 0,!1,void 0,this)}function Vb(e){let{palette:t,screen:r,overlay:n,status:o,paused:i,errorOnly:s,width:a,copyKey:c}=e,l=bs(ye()),d=Math.max(18,Math.floor(a*0.42)),u=eb(Cb(r,n,c),d),f=tb(o);return $e(Ut,{palette:t,backgroundColor:t.panel,edge:"top",ruled:!l,children:$e("box",{height:1,backgroundColor:t.panel,overflow:"hidden",children:$e(ut,{ruled:!1,palette:t,items:[{text:r,tone:"ghost"},{text:i?"PAUSED":"LIVE",tone:i?"warning":"success"},...s?[{text:"ERROR+",tone:"error"}]:[],...o===""?[]:[{text:_e(o,Math.max(16,a-d-28)),tone:f}]],hints:u},void 0,!1,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this)}function ze(e){return $e("box",{flexGrow:1,padding:1,overflow:"hidden",children:$e(ul,{palette:e.palette,title:e.title,body:e.body,hint:e.hint},void 0,!1,void 0,this)},void 0,!1,void 0,this)}function pl(e){return $e("box",{flexGrow:1,padding:1,overflow:"hidden",children:$e("text",{fg:e.palette.text,children:e.label},void 0,!1,void 0,this)},void 0,!1,void 0,this)}function zb(e){return $e("box",{flexGrow:1,padding:1,flexDirection:"column",overflow:"hidden",children:[$e("text",{fg:e.palette.error,children:e.title},void 0,!1,void 0,this),$e("text",{fg:e.palette.text,wrapMode:"word",children:e.body},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}var Gb,dI=8;var Yt=D(()=>{Mt();vr();So();Ot();In();ll();ke();Er();Gb=Gr.key.length+Gr.label.length+3});function pI(){if(process.platform==="darwin")return[["pbcopy"]];if(process.platform==="win32")return[["clip"]];return[["wl-copy"],["xclip","-selection","clipboard"],["xsel","--clipboard","--input"]]}function fI(){return pI().filter((e)=>{let t=e[0];return typeof t==="string"&&Bun.which(t)!==null})}function mI(e){return`\x1B]52;c;${Buffer.from(e,"utf8").toString("base64")}\x07`}function Yb(){if(process.platform==="linux")return"install wl-clipboard (Wayland) or xclip (X11)";return"no clipboard helper found"}async function fl(e){let t=fI(),r;for(let n of t)try{await gI(n,e);return}catch(o){r=o instanceof Error?o:Error(String(o))}if(process.platform!=="win32")try{await hI(e);return}catch(n){r=n instanceof Error?n:Error(String(n))}throw Te(lt,"clipboard unavailable",r?`${Yb()} (${r.message})`:Yb())}async function hI(e){let t=mI(e);try{await Bun.write("/dev/tty",t)}catch{if(!process.stderr.write(t))throw Error("terminal clipboard write failed")}}async function gI(e,t){let r=Bun.spawn(e,{stdin:"pipe",stdout:"ignore",stderr:"ignore"});r.stdin.write(t),await r.stdin.end();let n=await r.exited;if(n!==0)throw Error(`${e[0]} exited ${n}`)}var Gp=D(()=>{te()});function Kp(e){return e==="slash"||e==="config-edit"||e==="setup-wizard"}function Jb(e,t){return mn(e,t.keybinds.leader??Lo("x"))}function Xb(e,t){return mn(e,t.keybinds.command_list??Lo("p"))}function an(e,t){return mn(e,t.keybinds.command??"/")||e.name==="/"}function Qb(e,t){return mn(e,t.keybinds.help??"?")||e.sequence==="?"}function Zb(e,t){return mn(e,t.keybinds.search??"f")}function ew(e){return(e.name??"").toLowerCase()==="q"&&!e.ctrl&&!e.meta&&!e.super&&!e.alt&&!e.option}function tw(e){let t=e.name??"";return t==="R"||e.shift===!0&&t.toLowerCase()==="r"}function rw(e){return Or(e)&&(e.name??"").toLowerCase()==="l"}function nw(e,t){return mn(e,t.keybinds.copy??Zr())}function ow(e,t,r=vI){return t>0&&e-t<=r}function Mn(e,t,r,n){return mn(e,t.keybinds[r]??n)}function As(e){let t=(e.name??"").toLowerCase();return t==="pagedown"||Or(e)&&t==="d"}function Ls(e){let t=(e.name??"").toLowerCase();return t==="pageup"||Or(e)&&t==="u"}var vI=2000;var ml=D(()=>{_r()});function fi(e){return`http://127.0.0.1:${e}/mcp`}function qp(e){if(e==="")return{};return{Authorization:`Bearer ${e}`}}function Vp(e,t){return JSON.stringify({mcpServers:{devctl:{type:"http",url:e,headers:qp(t)}}},null,2)}function zp(e,t){return JSON.stringify({mcpServers:{devctl:{url:e,headers:qp(t)}}},null,2)}function Yp(e,t){return JSON.stringify({mcp:{devctl:{type:"remote",url:e,headers:qp(t),enabled:!0}}},null,2)}function Jp(e,t){let r=t===""?"":`
2213
+ `),o=[];for(let i of n)if(i.length<=r)o.push(i);else o.push(...YL(i,r));return o.length>0?o:[""]}function Nb(e,t,r){let n=e.length>0?[...e]:[""];if(r||n.length<=1)return{visible:n,mark:"",folded:!1,hidden:0};let o=n.length-1,i=` ${GL}${o}`,s=Math.max(1,t-i.length);return{visible:[Re(n[0]??"",s)],mark:i,folded:!0,hidden:o}}function Hb(e,t){return e==="all"||e==="focus"&&t}function Qc(e){if(e==="clip")return"focus";if(e==="focus")return"all";return"clip"}function Tp(e,t,r,n=Ar){if(e.length===0)return{start:0,items:[],newer:0};if(!t){let a=Math.max(0,e.length-n);return{start:a,items:e.slice(a),newer:0}}let o=e.length-1,i=Math.max(0,Math.min(r,o)),s=e.slice(i,i+n);return{start:i,items:s,newer:Math.max(0,e.length-i-s.length)}}function Zc(e,t=Ar){return Math.max(0,e-t)}function Ap(e,t,r,n){let o=Math.max(t-1,0);if(e<0&&r>0)return{selected:0,startDelta:Math.max(-r,e)};if(e>o&&n>0)return{selected:o,startDelta:Math.min(n,e-o)};return{selected:Math.min(o,Math.max(0,e)),startDelta:0}}function el(e){if(e==="all")return"wrap all";if(e==="focus")return"wrap selected";return"clip"}function YL(e,t){let r=[],n=e;while(n.length>t){let o=n.slice(0,t),i=JL(o);if(i>0)r.push(n.slice(0,i).trimEnd()),n=n.slice(i).trimStart();else r.push(n.slice(0,t)),n=n.slice(t)}if(n.length>0)r.push(n);return r}function JL(e){let t=e.lastIndexOf(" "),r=e.lastIndexOf("\t"),n=e.lastIndexOf(","),o=Math.max(t,r,n);if(o<Math.floor(e.length*KL))return-1;return e[o]===","?o+1:o}function XL(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function QL(e,t=!1){let r=e.trim();if(r==="")return;if(t){let n=Mi(r);if(n)return new RegExp(n.source,n.flags.includes("g")?n.flags:`${n.flags}g`)}return new RegExp(XL(r),"gi")}function ZL(e,t){let r=e.map((a)=>a.text).join(""),n=[];t.lastIndex=0;let o=t.exec(r);while(o){let a=o[0]??"";if(a.length>0)n.push({start:o.index,end:o.index+a.length});if(a.length===0)t.lastIndex+=1;o=t.exec(r)}if(n.length===0)return e;let i=[],s=0;for(let a of e){let c=s,l=s+a.text.length,d=0;for(let u of n){let f=Math.max(u.start,c),p=Math.min(u.end,l);if(p<=f)continue;let m=f-c,h=p-c;if(m>d)i.push({text:a.text.slice(d,m),kind:a.kind});i.push({text:a.text.slice(m,h),kind:"search"}),d=h}if(d<a.text.length)i.push({text:a.text.slice(d),kind:a.kind});s=l}return i.filter((a)=>a.text!=="")}function jb(e,t="",r=!1){let n=Jc(e),o=[],i=new RegExp(qL.source,"gi"),s=0,a=i.exec(n);while(a){let d=a[0]??"";if(a.index>s)o.push({text:n.slice(s,a.index),kind:"text"});o.push({text:d,kind:VL(d)}),s=a.index+d.length,a=i.exec(n)}if(s<n.length)o.push({text:n.slice(s),kind:"text"});let c=o.length===0?[{text:n,kind:"text"}]:o,l=QL(t,r);return l?ZL(c,l):c}function Bb(e){let t=e.showTimestamps?Vc:0,r=e.showMeta?qc:0;return Math.max(Mb,e.width-t-e.serviceWidth-zc-r-Ob-As*$b)}function Ub(e,t){let r=t.reduce((s,a)=>Math.max(s,a.length),0),n=Vc+zc+Ob+Mb+As*$b,o=Math.max(Db,e-n),i=Math.max(Db,r);return Math.min(kp,o,i)}function ai(e){return eD.has(e)}function Gb(e,t,r,n){let o=r===""?t:t.filter((a)=>a.timestamp>=r);if(o.length===0)return e;let i=Math.max(1,n);if(o.length>=i)return o.slice(-i);let s=Math.max(0,e.length+o.length-i);return e.slice(s).concat(o)}function Kb(e,t){let r=t.length>0?t[t.length-1].seq:-1,n=e.filter((o)=>o.seq>r);return[...t,...n]}function qb(e,t){if(t.length===0)return e;let r=new Set(e.map((o)=>o.seq)),n=t.filter((o)=>!r.has(o.seq));return n.length===0?e:[...n,...e]}function Lp(e,t,r){return e&&t<=0&&r}function Vb(e){return e.filter((t)=>t.level==="ERROR"||t.level==="FATAL").length}function yo(e,t){let r=t.services?.filter((l)=>l!=="")??[],n=t.service??"",o=(t.search??"").trim(),i=t.source??"",s=t.since??"",a=t.until??"",c;if(o!=="")if(t.regex===!0){let l=Mi(o);if(l)c=(d)=>l.test(d);else c=(d)=>d.toLowerCase().includes(o.toLowerCase())}else{let l=o.toLowerCase();c=(d)=>d.toLowerCase().includes(l)}return e.filter((l)=>{if(r.length>0&&!r.includes(l.service))return!1;if(n!==""&&l.service!==n)return!1;if(i!==""&&l.source!==i)return!1;if(t.systemLogs===!1&&ai(l.source))return!1;if(s!==""&&l.timestamp<s)return!1;if(a!==""&&l.timestamp>a)return!1;if(t.errorOnly===!0&&l.level!=="ERROR"&&l.level!=="FATAL")return!1;if(!c)return!0;return c(l.message)||c(l.service)})}function tD(e,t){let r={};for(let i of e)r[i.service]=(r[i.service]??0)+1;let n=t.map((i)=>({name:i,count:r[i]??0})),o=Object.keys(r).filter((i)=>!t.includes(i)).sort().map((i)=>({name:i,count:r[i]??0}));return[...n,...o]}function zb(e,t,r=[]){let n=tl(e,t,r);return[{name:"",count:t.length},...tD(t,n)]}function rD(e,t){let r=e.map((o)=>({name:o,count:t[o]??0})),n=Object.keys(t).filter((o)=>!e.includes(o)).sort().map((o)=>({name:o,count:t[o]??0}));return[...r,...n]}function Yb(e,t,r=[]){let n=Object.keys(t.byService).map((i)=>({service:i})),o=tl(e,n,r);return[{name:"",count:t.total},...rD(o,t.byService)]}function Jb(e,t){if(e<=0)return"none started";return`${e}/${t} running`}function tl(e,t,r=[]){let n=[...e],o=(i)=>{if(i!==""&&!n.includes(i))n.push(i)};for(let i of r)o(i);for(let i of t)o(i.service);return n}function Dp(e,t,r){let n=["",...e],o=n.indexOf(t),s=((o<0?0:o)+r+n.length)%n.length;return n[s]??""}var qc=8,Vc=9,zc=7,Db=10,Mb=16,Ob=2,As=1,Ar=200,WL=2,Ib=1,GL="\u25B8",$b=2,KL=0.4,qL,zL,eD,Wb;var gr=I(()=>{ga();Ot();Lt();qL=/("[^"]*"|'[^']*'|\b(?:ERROR|FATAL|WARN(?:ING)?|FAIL(?:ED)?)\b|\b\d{3,5}\b)/gi;zL=/\x1b\[[0-9;?]*[ -/]*[@-~]|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|\x1b[@-Z\\-_]/g;eD=new Set(["auth","devctl","proxy"]),Wb=["devctl","mcp","auth"]});function ci(e){return e.length+oD}function Xb(e,t){if(t<sD)return Re(e,3);if(t<iD)return Re(e,4);return e}function Qb(e,t,r){let n=e.length;if(n===0||r<=0)return{start:0,end:-1};let o=t>=0&&t<n?t:0,i=Ip(e,o,r);if(i.start===0&&i.end===n-1)return i;let s=o>0?rl:0,a=o<n-1?rl:0,c=s+a,l=Ip(e,o,Math.max(e[o]??0,r-c)),d=(s>0&&l.start===0?rl:0)+(a>0&&l.end===n-1?rl:0);if(d===0)return l;return Ip(e,o,Math.max(e[o]??0,r-c+d))}function Ip(e,t,r){let n=e.length,o=t,i=t,s=e[t]??0;if(s>=r)return{start:t,end:t};while(o>0||i<n-1){let a=o>0?e[o-1]??0:Number.POSITIVE_INFINITY,c=i<n-1?e[i+1]??0:Number.POSITIVE_INFINITY,l=o>0&&s+a<=r,d=i<n-1&&s+c<=r;if(!l&&!d)break;let u=t-(o-1),f=i+1-t;if(l&&(!d||u<=f))o-=1,s+=a;else if(d)i+=1,s+=c;else break}return{start:o,end:i}}function Zb(e){if(e==="detail")return bo.findIndex((t)=>t.id==="services");return bo.findIndex((t)=>t.id===e)}function ex(e,t){if(e==="doctor")return t.doctor;if(e==="settings")return t.settings;if(e==="profiles")return t.profiles;if(e==="dashboard"||e==="services")return t.services;if(e==="logs")return t.logs??0;if(e==="mcp")return t.mcp??0;if(e==="setup")return nD;if(e==="config")return t.config??0;return 0}function tx(e){if(e.length===1&&e>="1"&&e<="9")return bo[Number(e)-1]?.id;return}var nD=9,oD=2,rl=2,iD=80,sD=56,bo,Dn;var In=I(()=>{Ot();bo=[{id:"dashboard",label:"dashboard"},{id:"services",label:"services"},{id:"logs",label:"logs"},{id:"proxy",label:"proxy"}];Dn=bo.map((e)=>e.id)});function ox(e,t=!0){let r=e<nl?rx+1:rx,n=t?pD:0;return r+n+dD+n+uD+n}function di(e){return Math.max(cD,e-lD*2)}function ix(e){let t=e.width<aD,r=e.proxyOn?`\u25CF ${Re(e.proxyAddress,t?10:18)}`:"";return[{label:t?`${e.running}/${e.total}`:Jb(e.running,e.total),tone:e.running>0?"success":"idle"},{label:r,tone:"info",hide:!e.proxyOn},{label:"MCP",tone:"info",hide:!e.mcpOn},{label:e.adc?t?"ADC":"ADC ok":t?"!ADC":"ADC missing",tone:e.adc?"success":"error"},{label:t?"sec":"secrets shown",tone:"warning",hide:!e.reveal}]}function nx(e){return e.key==="/"&&e.label==="command"}function fD(e){return e.key.length+e.label.length+3}function sx(e,t){let r=e.filter(nx),n=e.filter((s)=>!nx(s)),o=[],i=0;for(let s of[...r,...n]){let a=fD(s);if(i+a>t)break;o.push(s),i+=a}return o}function ol(e,t,r,n,o="center",i=!0){let s=Math.min(r,Math.max(1,e-4)),a=Math.min(ox(e,i),Math.max(0,t-1)),c=Math.min(n,Math.max(1,t-a)),l=Math.max(0,Math.min(Math.floor((e-s)/2),Math.max(0,e-s))),d=o==="bottom"?t-2-c:Math.floor((t-c)/2),u=Math.max(0,Math.min(d,t-c));return{left:l,top:u,width:s,height:c}}function ax(e){if(e==="")return"idle";if(hD.test(e))return"error";if(e.includes("session only"))return"warning";if(mD.test(e))return"success";return"info"}function cx(e,t,r){if(e==="restart-cascade")return{title:"Restart dependents?",body:`${r?.services?.join(", ")||"selected services"} has dependents. Enter restarts only the named services. Press c to cascade (same as /restart --cascade).`};if(e==="quit")return{title:"Quit",body:"Stop managed services and leave the TUI? Press d to detach (daemon stays). Press k to stop the daemon and leave services running (devctl down --keep-services)."};if(e==="reload")return{title:"Reload applied",body:t===""?"Configuration changed. Restart marked services?":`Restart required: ${t}`};if(e==="reset-prefs")return{title:"Reset preferences",body:"Restore theme, display size, mouse, and leader timeout to defaults? This overwrites your saved tui.json values."};if(e==="free-port"){let n=r?.port??0,o=r?.process||"process",i=r?.pid??0;return{title:`Free port ${n}`,body:`Stop ${o} (pid ${i}) so port ${n} can be used? This sends SIGTERM, then SIGKILL if it stays up.`}}return{title:"Start profile",body:t===""?"Start the configured services?":`Start profile ${t}?`}}function lx(e){if(e==="quit")return[{key:"enter",label:"stop services"},{key:"d",label:"detach"},{key:"k",label:"down, keep services"},{key:"esc",label:"stay"}];if(e==="restart-cascade")return[{key:"enter",label:"named only"},{key:"c",label:"cascade"},{key:"esc",label:"stay"}];return[{key:"enter",label:"confirm"},{key:"esc",label:"stay"}]}function dx(e){let t=e.payload?.error;return typeof t==="string"&&t!==""?t:"configuration reload failed"}var li=100,nl=90,aD=60,cD=8,lD=6,rx=1,dD=1,uD=1,pD=1,oW,mD,hD;var vr=I(()=>{Ot();gr();In();oW=ox(nl-1);mD=/^(started|stopped|restarted|refreshed|cleared|copied|exported|jumped|proxy started|proxy stopped|secrets|re-running|theme |mouse |leader |display |restored)/i,hD=/\b(fail|error|unknown|not found|no configuration|blocked|already in use|is using port)\b/i});function il(){return[{name:"start",aliases:["up"],desc:"Start selected services or the current profile",leader:"n",group:"services",usage:"<service>"},{name:"stop",aliases:[],desc:"Stop selected services",leader:"x",group:"services",usage:"<service>"},{name:"restart",aliases:[],desc:"Restart selected services",leader:"R",group:"services",usage:"<service>",suggest:[{token:"--cascade",desc:"Restart selected services and their dependents"}]},{name:"run",aliases:["task"],desc:"Run a one-off task; empty /run opens a picker",leader:"",group:"services",usage:"<task>"},{name:"exec",aliases:[],desc:"Run a command in a service context; empty /exec opens a picker",leader:"",group:"services",usage:"<service>"},{name:"services",aliases:["s"],desc:"Open the services screen",leader:"s",group:"nav"},{name:"logs",aliases:["l"],desc:"Open the log viewer",leader:"l",group:"nav"},{name:"auth",aliases:["identity","a"],desc:"Open identity",leader:"a",group:"nav",suggest:[{token:"login",desc:"Run gcloud ADC login"},{token:"logout",desc:"Revoke application-default credentials"},{token:"refresh",desc:"Probe identities"}]},{name:"credentials",aliases:["creds"],desc:"Open credential store status",leader:"",group:"nav"},{name:"reload",aliases:[],desc:"Reload configuration",leader:"",group:"ui"},{name:"proxy",aliases:["p"],desc:"Open the proxy screen",leader:"p",group:"nav"},{name:"mcp",aliases:["agent"],desc:"Open the MCP server screen for coding agents",leader:"",group:"nav"},{name:"doctor",aliases:["d"],desc:"Run environment diagnostics",leader:"d",group:"nav"},{name:"stats",aliases:["metrics"],desc:"View system and service statistics",leader:"m",group:"nav"},{name:"config",aliases:["c"],desc:"View merged configuration",leader:"c",group:"nav"},{name:"import",aliases:[],desc:"Preview or write a Compose mapping",leader:"",group:"ui",suggest:[{token:"compose",desc:"Preview a Compose mapping; add --write to save"}]},{name:"diff",aliases:["provenance"],desc:"Show winning config sources and what they shadowed",leader:"",group:"ui"},{name:"daemon",aliases:["bootstrap"],desc:"Show supervisor bootstrap logs (same file as devctl daemon logs)",leader:"",group:"app"},{name:"update",aliases:[],desc:"Install a newer GitHub Release when the install method is known",leader:"",group:"app"},{name:"profiles",aliases:["o"],desc:"Select a development profile",leader:"o",group:"nav"},{name:"setup",aliases:["init"],desc:"Open setup guidance",leader:"i",group:"nav"},{name:"dashboard",aliases:["home"],desc:"Return to the dashboard",leader:"h",group:"nav"},{name:"themes",aliases:["theme"],desc:"List available themes",leader:"t",group:"ui"},{name:"settings",aliases:["prefs","preferences"],desc:"Open TUI settings (theme, mouse, MCP page)",leader:"",group:"ui"},{name:"help",aliases:["?"],desc:"Show the help dialog",leader:"",group:"ui"},{name:"refresh",aliases:[],desc:"Refresh status and logs",leader:"r",group:"ui"},{name:"regex",aliases:[],desc:"Toggle regex log search",leader:"",group:"logs"},{name:"since",aliases:[],desc:"Filter logs after an ISO timestamp",leader:"",group:"logs",usage:"<timestamp>"},{name:"until",aliases:[],desc:"Filter logs before an ISO timestamp",leader:"",group:"logs",usage:"<timestamp>"},{name:"history",aliases:[],desc:"Load a persisted log session",leader:"",group:"logs",usage:"<session>"},{name:"edit",aliases:[],desc:"Open configuration in $EDITOR",leader:"",group:"ui"},{name:"buffer",aliases:[],desc:"Edit configuration in a validate/save buffer",leader:"",group:"ui"},{name:"pause",aliases:[],desc:"Pause or resume live logs",leader:"",group:"logs"},{name:"fullscreen",aliases:["zen","expand"],desc:"Expand logs to fill the terminal",leader:"z",group:"logs"},{name:"split",aliases:[],desc:"Split the logs screen into two service panes",leader:"",group:"logs"},{name:"trace",aliases:[],desc:"Search logs for a request or trace id",leader:"",group:"logs",usage:"<id>"},{name:"filter",aliases:[],desc:"Toggle ERROR+ log filter",leader:"",group:"logs"},{name:"system",aliases:["internal"],desc:"Show or hide internal auth/mcp/devctl/proxy logs",leader:"",group:"logs"},{name:"reveal",aliases:[],desc:"Reveal or hide secret environment values",leader:"",group:"ui"},{name:"wrap",aliases:[],desc:"Cycle log wrap: selected, all lines, or clip",leader:"",group:"logs"},{name:"copy",aliases:[],desc:"Copy the highlighted selection to the clipboard",leader:"",group:"ui"},{name:"export",aliases:[],desc:"Write filtered logs to ~/.devctl/exports",leader:"e",group:"logs"},{name:"exports",aliases:["open-exports"],desc:"Open the log export folder",leader:"",group:"logs"},{name:"clear",aliases:["new"],desc:"Clear the on-screen log buffer",leader:"",group:"logs"},{name:"version",aliases:["v"],desc:"Show the current devctl version",leader:"",group:"app"},{name:"down",aliases:[],desc:"Stop the supervisor",leader:"",group:"app",suggest:[{token:"--keep-services",desc:"Stop the supervisor and leave processes running"}]},{name:"exit",aliases:["quit","q"],desc:"Exit (detach or stop services)",leader:"q",group:"app"}]}function xo(e){let t=e.trim().toLowerCase().replace(/^\//,""),r=t.indexOf(" ");return r===-1?t:t.slice(0,r)}function hx(e){let t=xo(e);if(t==="")return il();return il().map((r)=>({command:r,score:AD(r,t)})).filter((r)=>r.score>0).sort((r,n)=>n.score-r.score||r.command.name.localeCompare(n.command.name)).map((r)=>r.command)}function AD(e,t){if(t==="")return 1;let r=e.name.toLowerCase(),n=e.aliases.map((o)=>o.toLowerCase());return DD(r,n,t)||ID(r,n,t)||MD(r,n,t)||OD(r,n,t)||LD(e.suggest,t)||$D(e.desc.toLowerCase(),t)}function LD(e,t){if(!e||e.length===0)return 0;let r=e.map((n)=>n.token.toLowerCase());if(r.some((n)=>n===t||n.replace(/^-+/,"")===t))return px;if(t.length<mx)return 0;return r.some((n)=>n.includes(t)||n.replace(/^-+/,"").startsWith(t))?fx:0}function DD(e,t,r){if(e===r)return gD;return t.includes(r)?vD:0}function ID(e,t,r){if(e.startsWith(r))return yD;if(t.some((i)=>i.startsWith(r)))return bD;let n=e.split("-");return n.length>1&&n.some((i)=>i.startsWith(r))?xD:0}function MD(e,t,r){if(r.length<mx)return 0;if(e.includes(r))return px;return t.some((n)=>n.includes(r))?fx:0}function OD(e,t,r){if(r.length<PD)return 0;let n=ux(e,r);if(n>0)return wD+Math.min(n,TD);return t.some((o)=>ux(o,r)>0)?SD:0}function ux(e,t){let r=0,n=0,o=0;for(let i=0;i<t.length;i+=1){let s=t[i];if(s===void 0)return 0;let a=e.indexOf(s,r);if(a<0)return 0;if(r>0&&a===r)n+=1,o+=2+n;else n=0,o+=a===0?4:1;r=a+1}return o}function $D(e,t){if(t.length<_D)return 0;if(e.split(/[^a-z0-9+]+/).filter((n)=>n.length>=RD).some((n)=>n.startsWith(t)))return kD;return t.length>=ED&&e.includes(t)?CD:0}function Wr(e){let t=e.trim().toLowerCase().replace(/^\//,"").split(" ")[0]??"";return il().find((r)=>r.name===t||r.aliases.includes(t))}function Mp(e){return e.trim().replace(/^\//,"").split(/\s+/).slice(1).filter((r)=>r!=="")}function gx(e){return il().find((r)=>r.leader===e)?.name??""}function vx(e){let t=e.includes("--cascade")||e.includes("-c");return{services:e.filter((n)=>n!=="--cascade"&&n!=="-c"),cascade:t}}function yx(e){let t=e.includes("--print-env"),r=e.includes("--reveal"),n=e.filter((o)=>o!=="--print-env"&&o!=="--reveal"&&o!=="--");return{service:n[0]??"",printEnv:t,reveal:r,command:n.slice(1)}}var gD=100,vD=90,yD=80,bD=70,xD=60,px=50,fx=45,wD=30,SD=28,kD=20,CD=10,mx=2,PD=3,_D=4,RD=4,ED=4,TD=9;function Sx(e){return e==="detail"?"services":e}function kx(e){let t=Dn.indexOf(Sx(e));if(t<0)return"dashboard";return Dn[(t+1)%Dn.length]??"dashboard"}function Cx(e){let t=Dn.indexOf(Sx(e));if(t<0)return"dashboard";return Dn[(t-1+Dn.length)%Dn.length]??"dashboard"}function $p(e){return["services","nav","logs","ui","app","tasks"].flatMap((r)=>{let n=e.filter((o)=>o.group===r);if(n.length===0)return[];return[{group:r,items:n}]})}function sl(e){return e.hint?`${e.name} ${e.hint}`:e.name}function Fp(e){return e.name}function Px(e){if(e.hint)return`${e.hint} \xB7 ${e.desc}`;let t=[e.usage,...(e.suggest??[]).map((r)=>r.token)].filter((r)=>Boolean(r));if(t.length===0)return e.desc;return`${e.desc} \xB7 ${t.join(" \xB7 ")}`}function _x(e){let t=e.reduce((n,o)=>Math.max(n,Fp(o).length),0),r=Math.min(Op,Math.max(t,1));return FD+r+ND}function Rx(e){return e.hint?`${e.name} ${e.hint} `:`${e.name} `}function Ex(e,t){if(e.hint)return e.hint.split(/\s+/);return Wr(t)?.name===e.name?Mp(t):[]}function bx(e,t){if(t==="")return!0;let r=t.toLowerCase(),n=e.toLowerCase();return n.startsWith(r)||n.includes(r)}function HD(e,t){if(e.name==="start"||e.name==="stop"||e.name==="restart")return(t.services??[]).map((r)=>({token:r,desc:`${e.name} ${r}`}));if(e.name==="run")return(t.tasks??[]).map((r)=>({token:r,desc:`run ${r}`}));if(e.name==="exec")return(t.services??[]).map((r)=>({token:r,desc:`exec in ${r}`}));return[]}function xx(e,t){return{...e,hint:t.token,desc:t.desc,usage:void 0,suggest:void 0}}function jD(e,t,r={}){let n=Mp(t).join(" "),o=t.trim().includes(" ")||t.endsWith(" "),i=xo(t),s=[];for(let a of e){if(s.push({...a,hint:void 0}),i==="")continue;let c=a.name===i||a.aliases.includes(i),l=c?n:i;for(let d of a.suggest??[])if(bx(d.token,l))s.push(xx(a,d));if(!c||!o)continue;for(let d of HD(a,r))if(bx(d.token,n))s.push(xx(a,d))}return s}function Tx(e,t={}){let r=hx(e),n=xo(e)===""?$p(r).flatMap((o)=>o.items):r;return jD(n,e,t)}function Np(e,t,r,n){let o=xo(t),i=[...e].sort().map((s)=>({name:s,aliases:[],desc:n,leader:"",group:r}));if(o==="")return i;return i.filter((s)=>s.name.toLowerCase().includes(o))}function Ax(e,t,r=Zr()){if(t==="slash")return[{key:"\u2191\u2193",label:"suggest"},{key:"tab",label:"complete"},{key:"enter",label:"run"},{key:"esc",label:"cancel"}];if(t==="themes")return[Gr,{key:"\u2191\u2193",label:"move"},{key:"enter",label:"save"},{key:"esc",label:"revert"}];if(t==="help")return[Gr,{key:"j/k",label:"scroll"},{key:"esc",label:"close"}];if(t==="log-details"||t==="scroll-text")return[Gr,{key:"j/k",label:"scroll"},{key:Ei(r),label:"copy"},{key:"esc",label:"close"}];if(t==="confirm")return[{key:"enter",label:"confirm"},{key:"esc",label:"stay"}];if(t==="plan")return[Gr,{key:"esc",label:"back to dashboard"},{key:"enter",label:"done"}];if(t==="leader")return[Gr,...Hp()];if(t==="config-edit")return[{key:It("s"),label:"save"},{key:"esc",label:"discard"}];return BD(e,r)}function Hp(){return[{key:"n",label:"start"},{key:"x",label:"stop"},{key:"R",label:"restart"},{key:"s",label:"services"},{key:"l",label:"logs"},{key:"t",label:"themes"},{key:"q",label:"quit"}]}function BD(e,t){let r=[{key:Ei(t),label:"copy"},Gr,{key:"?",label:"help"}];switch(e){case"dashboard":return[{key:"space",label:"select"},{key:"*",label:"all"},{key:"-",label:"none"},{key:"enter",label:"start or open"},{key:"n",label:"start"},{key:"x",label:"stop"},{key:"r",label:"refresh"},{key:"R",label:"restart"},{key:"\u2190\u2192",label:"log filter"},{key:"g",label:"latest"},{key:"z",label:"full logs"},{key:"i",label:"internal logs"},{key:It("l"),label:"clear logs"},{key:"j/k",label:"move"},...r];case"services":return[{key:"enter",label:"detail"},{key:"space",label:"select"},{key:"*",label:"all"},{key:"-",label:"none"},{key:"n",label:"start"},{key:"x",label:"stop"},{key:"r",label:"refresh"},{key:"R",label:"restart"},...r];case"detail":return[{key:"j/k",label:"scroll env"},{key:"n",label:"start"},{key:"x",label:"stop"},{key:"o",label:"config"},{key:"l",label:"logs"},{key:"esc",label:"back"},...r];case"logs":return[{key:"\u2190\u2192",label:"filter"},{key:"e",label:"errors"},{key:"i",label:"internal logs"},{key:It("l"),label:"clear logs"},{key:"g",label:"latest"},{key:"f",label:"search"},{key:"t",label:"time"},{key:"m",label:"meta"},{key:"w",label:"wrap"},{key:"j/k",label:"move"},{key:"p",label:"pause"},{key:"z",label:"full screen"},{key:"\\",label:"split"},{key:"/exports",label:"open folder"},...r];case"profiles":return[{key:"space",label:"set current"},{key:"enter",label:"set and start"},{key:"j/k",label:"move"},...r];case"proxy":return[{key:"n",label:"start proxy"},{key:"x",label:"stop proxy"},...r];case"mcp":return[{key:"j/k",label:"move"},{key:"space",label:"start or copy"},{key:"\u2190\u2192",label:"change port"},{key:"enter",label:"start or copy"},...r];case"config":return[{key:"enter",label:"run task"},{key:"v",label:"buffer"},{key:"e",label:"editor"},{key:"/diff",label:"sources"},{key:"/reload",label:"reload"},{key:"j/k",label:"scroll or select task"},...r];case"setup":return[{key:"j/k",label:"steps"},{key:"enter",label:"continue"},{key:"esc",label:"back"},{key:"esc\xD72",label:"quit"},...r];case"doctor":return[{key:"r",label:"run doctor again"},{key:"j/k",label:"move"},{key:"enter",label:"fix port"},...r];case"auth":return[{key:"r",label:"probe identities"},{key:"/auth login",label:"ADC login"},{key:"/auth logout",label:"revoke ADC"},{key:"/auth refresh",label:"probe"},...r];case"settings":return[{key:"j/k",label:"move"},{key:"\u2190\u2192",label:"save"},{key:"enter",label:"apply or open page"},{key:"space",label:"toggle"},...r];default:return[{key:"tab",label:"screens"},...r]}}function Lx(e,t,r){if(r<=t||e<t)return 0;return Math.min(e-t+1,r-t)}function wx(e,t,r){let n=e.slice(t,r+1),o=new Set(n.map((i)=>i.group));return n.length+o.size}function Dx(e,t,r){if(e.length===0)return[];let n=Math.min(Math.max(t,0),e.length-1),o=n,i=n;for(;;){let s=i+1<e.length&&wx(e,o,i+1)<=r,a=o>0&&wx(e,o-1,i)<=r;if(s)i+=1;else if(a)o-=1;else break}return e.slice(o,i+1)}function ui(e,t){if(e.length===0)return;let r=Math.min(Math.max(t,0),e.length-1);return e[r]}var Gr,FD=3,ND=2,Op=22;var wo=I(()=>{Rr();In();Gr={key:"/",label:"command"}});function cl(e,t){let r=t??Object.keys(e?.services??{});return{running:r.filter((o)=>vo(e?.services[o])).length,total:r.length}}function Ix(e,t){let r=0,n=0,o=0,i=0,s=0,a=0;for(let c of e){let l=t?.services[c];if(l?.health==="HEALTHY")o+=1;let d=l?.state??"STOPPED";if(d==="FAILED")i+=1;else if(d==="STOPPING")s+=1;else if(d==="STARTING"||d==="RESTARTING")n+=1;else if(Ts(d))r+=1;else a+=1}return{total:e.length,live:r+n,running:r,starting:n,healthy:o,failed:i,stopping:s,stopped:a}}function Mx(e,t){if(!Number.isFinite(e)||!Number.isFinite(t)||t<=0)return 0;return e/t}function VD(e,t,r){if(!Number.isFinite(e))return"success";if(e>r)return"error";if(e>t)return"warning";return"success"}function zD(e,t){return VD(Mx(e,t),UD,WD)}function Wp(e,t=24){if(e.length===0||t<=0)return"";let r=e.length>t?e.slice(e.length-t):e,n=Math.max(...r,0);if(n<=0)return Ls[0]?.repeat(r.length)??"";return r.map((o)=>{let i=Number.isFinite(o)?Math.min(1,Math.max(0,o/n)):0,s=Math.min(Ls.length-1,Math.round(i*(Ls.length-1)));return Ls[s]??Ls[0]}).join("")}function YD(e,t){let r=t>0?e/t:1;if(r<KD)return"error";if(r<GD)return"warning";return"success"}function jp(e){if(!Number.isFinite(e)||e<0)return"\u2014";return e.toFixed(2)}function JD(e){if(!Number.isFinite(e)||e<0)return"\u2014";return`${Math.round(e*100)}%`}function Ox(e,t=Date.now()){if(!e?.startTime)return"\u2014";let r=new Date(e.startTime).getTime();if(!Number.isFinite(r))return"\u2014";return ks(t-r)}function $x(e,t=qD){return Object.entries(e).sort((r,n)=>n[1]-r[1]||r[0].localeCompare(n[0])).slice(0,t)}function Fx(e,t=1){return Math.max(24,e-nI-t*2-oI-iI-sI)}function Nx(e){let t=e>=XD,r=e>=QD,n=e>=ZD,o=e>=eI,i=e>=tI,s=e>=rI,a=Tr+$t;if(t)a+=Ur+$t;if(r)a+=_s+$t;if(n)a+=Rs+$t;if(o)a+=Ps+$t;if(i)a+=ll+$t;if(s)a+=sn;return{name:Math.max(Fc,e-a),health:t,cpu:r,mem:n,up:o,rst:i,pid:s}}function Hx(e){let t=Number.isFinite(e)&&e>0?e:0;return{ratio:Math.min(1,t),label:JD(t)}}function jx(e,t=lI){return`[${si(e.ratio,t)}] ${e.label.padStart(4)}`}function Gp(e,t){let r=e.reduce((l,d)=>Math.max(l,d.what.length),4),n=e.reduce((l,d)=>Math.max(l,d.reading.length),7),o=e.some((l)=>l.meter)?dI:0,i=pi*(o>0?3:2),s=Math.min(aI,Math.max(Bp,r)),a=Math.min(cI,Math.max(Up,n)),c=t-s-a-o-i;if(c<al&&a>Up)a=Math.max(Up,a-(al-c)),c=t-s-a-o-i;if(c<al&&s>Bp)s=Math.max(Bp,s-(al-c)),c=t-s-a-o-i;return{what:s,reading:a,meaning:Math.max(0,c),meter:o}}function Bx(e){return uI.has((e?.health.type??"").toLowerCase())}function Ux(e){if(e==="darwin")return"macOS";if(e==="win32")return"Windows";if(e==="linux")return"Linux";return e||"this computer"}function Wx(e,t){let r=Math.max(1,t),n=zD(e,r),o=n==="error"?"overloaded":n==="warning"?"busy":"not busy",i=n==="error"?`${r} cores are queued up \u2014 last minute ${jp(e)}`:n==="warning"?`${r} cores nearly full \u2014 last minute ${jp(e)}`:`${r} cores. Last minute ${jp(e)} (under ${r} is fine)`;return{what:"CPU work",reading:o,meaning:i,tone:n,meter:Hx(Mx(e,r))}}function Gx(e,t){let r=YD(e,t),n=`${Cs(e)} of ${Cs(t)}`,o=r==="error"?"Almost no RAM left for new work":r==="warning"?"RAM is getting tight":"RAM the computer can still give out",i=t>0?Math.max(0,(t-e)/t):0;return{what:"RAM leftover",reading:n,meaning:o,tone:r,meter:Hx(i)}}function Kx(e){let t=e?.state??"STOPPED";if(t==="FAILED")return"crashed";if(t==="STOPPING")return"stopping";if(t==="STARTING"||t==="RESTARTING")return"starting";if(Ts(t))return"up";return"off"}function qx(e){if(!e||!Ts(e.state))return"\u2014";if(e.health==="HEALTHY")return"ready";if(e.health==="UNHEALTHY")return"failing";if(e.state==="STARTING"||e.state==="RESTARTING")return"waiting";return"checking"}function Vx(e){if(e==="keychain")return"password store";if(e==="file")return"local file";return e||"unknown store"}function zx(e,t){let r=[{what:"Started",reading:String(e.live),meaning:e.live===0?"nothing is running yet":"process is up",tone:e.live>0?"success":"muted"}];if(t)r.push({what:"Ready",reading:String(e.healthy),meaning:e.live===0?"start services first":"passed their health check",tone:e.live>0&&e.healthy<e.live?"warning":e.healthy>0?"success":"muted"});if(e.starting>0)r.push({what:"Still starting",reading:String(e.starting),meaning:"not ready for traffic yet",tone:"warning"});if(e.stopping>0)r.push({what:"Stopping",reading:String(e.stopping),meaning:"shutting down",tone:"warning"});return r.push({what:"Crashed",reading:String(e.failed),meaning:e.failed>0?"open the service for the error":"none",tone:e.failed>0?"error":"muted"}),r.push({what:"Not started",reading:String(e.stopped),meaning:e.stopped>0?"off until you start them":"all are up",tone:"muted"}),r}var UD=0.85,WD=1.5,GD=0.2,KD=0.1,qD=5,Ls="\u2581\u2582\u2583\u2584\u2585\u2586\u2587\u2588",XD=52,QD=64,ZD=72,eI=82,tI=90,rI=100,nI=2,oI=2,iI=2,sI=1,ll=8,Bp=8,aI=22,Up=6,cI=28,al=12,pi=2,lI=10,dI=18,uI;var dl=I(()=>{Ot();Lt();uI=new Set(["http","tcp","command"])});import{useEffect as pI,useRef as fI}from"react";import{jsxDEV as me,Fragment as hI}from"@opentui/react/jsx-dev-runtime";function mI(e,t){switch(t){case"primary":return{bg:e.primary,fg:mo(e.primary,e.inverse)};case"accent":return{bg:e.accent,fg:Lc(e)?Ac:mo(e.accent,e.inverse)};case"success":return{bg:e.success,fg:mo(e.success,e.inverse)};case"warning":return{bg:e.warning,fg:Lc(e)?Ac:mo(e.warning,e.inverse)};case"error":return{bg:e.error,fg:mo(e.error,e.inverse)};case"info":return{bg:e.info,fg:mo(e.info,e.inverse)};case"muted":return{bg:e.element,fg:e.text};case"ghost":return{bg:e.element,fg:e.muted};default:return{bg:e.element,fg:e.text}}}function Yx(e){let{palette:t,dir:r}=e;return me("box",{width:2,height:1,flexShrink:0,overflow:"hidden",children:me("text",{fg:t.muted,children:r==="left"?"\u2039":"\u203A"},void 0,!1,void 0,this)},void 0,!1,void 0,this)}function Ut(e){let{palette:t,children:r,backgroundColor:n,edge:o="bottom"}=e,i=ye(),s=e.ruled??!ho(i),a=n??t.element,c=me("box",{flexShrink:0,flexDirection:"column",backgroundColor:a,overflow:"hidden",children:r},void 0,!1,void 0,this);if(!s)return c;return me("box",{flexShrink:0,flexDirection:"column",overflow:"hidden",border:[...o==="both"?["top","bottom"]:[o]],borderStyle:"single",borderColor:t.border,children:c},void 0,!1,void 0,this)}function ul(e){let{palette:t,items:r,active:n,width:o,onPick:i,emphasis:s="ghost"}=e,a=r.map((u)=>ci(u.label)),c=Qb(a,n,Math.max(1,o)),l=c.start>0,d=c.end>=0&&c.end<r.length-1;return me("box",{height:1,flexDirection:"row",overflow:"hidden",backgroundColor:t.element,children:[l?me(Yx,{palette:t,dir:"left"},void 0,!1,void 0,this):null,r.map((u,f)=>{if(f<c.start||f>c.end)return null;let p=f===n,m=s==="fill"&&p;return me(Be,{palette:t,label:u.label,tone:m?"primary":"ghost",bg:m?void 0:p?t.highlight:t.element,fg:m?void 0:p?t.primary:u.color??t.muted,onMouseDown:()=>i(f)},u.id,!1,void 0,this)}),d?me(Yx,{palette:t,dir:"right"},void 0,!1,void 0,this):null]},void 0,!0,void 0,this)}function Be(e){let{palette:t,label:r,tone:n="idle",fg:o,bg:i,onMouseDown:s}=e,a=mI(t,n);return me("box",{height:1,paddingLeft:1,paddingRight:1,backgroundColor:i??a.bg,overflow:"hidden",flexShrink:0,onMouseDown:s,children:me("text",{fg:o??a.fg,children:r},void 0,!1,void 0,this)},void 0,!1,void 0,this)}function it(e){let{palette:t,hints:r}=e;return me("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,children:r.map((n)=>me("box",{height:1,paddingLeft:1,paddingRight:1,flexShrink:0,overflow:"hidden",children:me("text",{wrapMode:"none",children:[me("span",{fg:t.primary,children:n.key},void 0,!1,void 0,this),me("span",{fg:t.muted,children:` ${n.label}`},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},`${n.key}-${n.label}`,!1,void 0,this))},void 0,!1,void 0,this)}function ut(e){let{palette:t,items:r,hints:n,ruled:o=!0}=e,i=me("box",{height:1,flexDirection:"row",overflow:"hidden",backgroundColor:t.element,children:[r.map((s,a)=>me(Be,{palette:t,label:s.text,tone:s.tone,onMouseDown:s.onMouseDown},`${s.text}-${a}`,!1,void 0,this)),n&&n.length>0?me(hI,{children:[me("box",{flexGrow:1,backgroundColor:t.element},void 0,!1,void 0,this),me(it,{palette:t,hints:n},void 0,!1,void 0,this)]},void 0,!0,void 0,this):null]},void 0,!0,void 0,this);if(!o)return i;return me(Ut,{palette:t,backgroundColor:t.element,children:i},void 0,!1,void 0,this)}function pt(e){let{palette:t,title:r,termW:n,termH:o,preferW:i,preferH:s,anchor:a="center",borderColor:c,bottomTitle:l,gap:d,children:u}=e,f=ho(ye()),p=ol(n,o,i,s,a,!f);return me("box",{position:"absolute",left:p.left,top:p.top,width:p.width,height:p.height,border:!0,borderStyle:Kp,borderColor:c??t.borderActive,backgroundColor:t.panel,padding:f?0:1,title:r,titleColor:t.primary,bottomTitle:l,gap:f?0:d,flexDirection:"column",overflow:"hidden",children:u},void 0,!1,void 0,this)}function Ue(e){return{rootOptions:{flexGrow:1,height:"100%",overflow:"hidden",backgroundColor:e.panel},viewportOptions:{backgroundColor:e.panel},contentOptions:{backgroundColor:e.panel},scrollbarOptions:{trackOptions:{foregroundColor:e.primary,backgroundColor:e.element}}}}function rt(e,t){if(!e)return;e.scrollBy({x:0,y:t})}function fi(e,t){let r=fI(null);return pI(()=>{let n=r.current;if(!n||e<0)return;let o=requestAnimationFrame(()=>{n.scrollChildIntoView(`${t}-${e}`)});return()=>cancelAnimationFrame(o)},[t,e]),r}function kt(e){let t=ye(),r=Ss(t),n=e.scroll?me("scrollbox",{focused:!1,stickyScroll:!1,scrollX:!1,style:Ue(e.palette),children:me("box",{flexDirection:"column",overflow:"hidden",children:e.children},void 0,!1,void 0,this)},void 0,!1,void 0,this):e.children;return me("box",{flexGrow:1,border:!0,borderStyle:Kp,borderColor:e.borderColor??e.palette.border,title:e.title,titleColor:e.palette.primary,paddingLeft:t.pad,paddingRight:t.pad,paddingTop:r?0:t.pad,paddingBottom:r?0:t.pad,backgroundColor:e.palette.panel,flexDirection:"column",overflow:"hidden",children:n},void 0,!1,void 0,this)}function So(e){let{palette:t,label:r,value:n,tone:o="text"}=e,i=ye(),s=o==="success"?t.success:o==="warning"?t.warning:o==="error"?t.error:o==="muted"?t.muted:t.text;return me("box",{height:i.rowH,flexDirection:"row",overflow:"hidden",children:[me("box",{width:14,flexShrink:0,overflow:"hidden",children:me("text",{fg:t.muted,children:r},void 0,!1,void 0,this)},void 0,!1,void 0,this),me("box",{flexGrow:1,overflow:"hidden",children:me("text",{fg:s,wrapMode:"none",children:n},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function pl(e){let{palette:t,title:r,body:n,hint:o}=e;return me("box",{paddingLeft:1,paddingRight:1,flexDirection:"column",overflow:"hidden",border:!0,borderStyle:Kp,borderColor:t.borderActive,titleColor:t.primary,children:[me("text",{fg:t.primary,children:r},void 0,!1,void 0,this),me("text",{fg:t.text,wrapMode:"word",children:n},void 0,!1,void 0,this),o?me("text",{fg:t.muted,wrapMode:"word",children:o},void 0,!1,void 0,this):null]},void 0,!0,void 0,this)}var Kp="rounded";var ke=I(()=>{Mt();vr();In();Er();tt()});import{jsxDEV as $e,Fragment as vI}from"@opentui/react/jsx-dev-runtime";function Xx(e){let{palette:t,cfg:r,snap:n,google:o,profile:i,reveal:s,width:a}=e,c=cl(n,r?Object.keys(r.services):void 0),l=n?.proxy.running===!0,d=o?.adcAvailable===!0,u=a<nl,f=Re(r?.project.name||"local",u?18:22),p=Re(i||n?.profile||"no profile",16),m=ix({width:a,running:c.running,total:c.total,proxyOn:l,proxyAddress:n?.proxy.address??"",mcpOn:n?.mcp?.running===!0,adc:d,reveal:s}).filter((b)=>!b.hide&&b.label!=="").map((b)=>$e(Be,{palette:t,label:b.label,tone:b.tone},b.label,!1,void 0,this)),h=Ss(ye()),v=$e("box",{height:1,flexGrow:1,overflow:"hidden",backgroundColor:t.panel,paddingLeft:1,children:$e("text",{wrapMode:"none",children:[$e("span",{fg:t.primary,children:er()},void 0,!1,void 0,this),$e("span",{fg:t.muted,children:` ${f}`},void 0,!1,void 0,this),$e("span",{fg:t.accent,children:` ${p}`},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this);return $e(Ut,{palette:t,backgroundColor:t.panel,ruled:!h,children:$e("box",{height:u?2:1,flexDirection:"column",backgroundColor:t.panel,overflow:"hidden",children:[$e("box",{height:1,flexDirection:"row",overflow:"hidden",children:[v,u?null:m]},void 0,!0,void 0,this),u?$e("box",{height:1,flexDirection:"row",overflow:"hidden",backgroundColor:t.panel,children:m},void 0,!1,void 0,this):null]},void 0,!0,void 0,this)},void 0,!1,void 0,this)}function Qx(e){let{palette:t,screen:r,width:n,onSelect:o}=e,i=n>=Jx+gI,s=i?Math.max(1,n-Jx):n,a=bo.map((l)=>({id:l.id,label:Xb(l.label,s)})),c=Zb(r);return $e(Ut,{palette:t,backgroundColor:t.element,children:$e("box",{height:1,flexDirection:"row",overflow:"hidden",backgroundColor:t.element,children:[$e(ul,{palette:t,items:a,active:c,width:s,onPick:(l)=>{let d=bo[l];if(d)o(d.id)}},void 0,!1,void 0,this),i?$e(vI,{children:[$e("box",{flexGrow:1,backgroundColor:t.element},void 0,!1,void 0,this),$e(it,{palette:t,hints:[Gr]},void 0,!1,void 0,this)]},void 0,!0,void 0,this):null]},void 0,!0,void 0,this)},void 0,!1,void 0,this)}function Zx(e){let{palette:t,screen:r,overlay:n,status:o,paused:i,errorOnly:s,width:a,copyKey:c}=e,l=Ss(ye()),d=Math.max(18,Math.floor(a*0.42)),u=sx(Ax(r,n,c),d),f=ax(o);return $e(Ut,{palette:t,backgroundColor:t.panel,edge:"top",ruled:!l,children:$e("box",{height:1,backgroundColor:t.panel,overflow:"hidden",children:$e(ut,{ruled:!1,palette:t,items:[{text:r,tone:"ghost"},{text:i?"PAUSED":"LIVE",tone:i?"warning":"success"},...s?[{text:"ERROR+",tone:"error"}]:[],...o===""?[]:[{text:Re(o,Math.max(16,a-d-28)),tone:f}]],hints:u},void 0,!1,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this)}function ze(e){return $e("box",{flexGrow:1,padding:1,overflow:"hidden",children:$e(pl,{palette:e.palette,title:e.title,body:e.body,hint:e.hint},void 0,!1,void 0,this)},void 0,!1,void 0,this)}function fl(e){return $e("box",{flexGrow:1,padding:1,overflow:"hidden",children:$e("text",{fg:e.palette.text,children:e.label},void 0,!1,void 0,this)},void 0,!1,void 0,this)}function ew(e){return $e("box",{flexGrow:1,padding:1,flexDirection:"column",overflow:"hidden",children:[$e("text",{fg:e.palette.error,children:e.title},void 0,!1,void 0,this),$e("text",{fg:e.palette.text,wrapMode:"word",children:e.body},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}var Jx,gI=8;var Yt=I(()=>{Mt();vr();wo();Ot();In();dl();ke();Er();Jx=Gr.key.length+Gr.label.length+3});function yI(){if(process.platform==="darwin")return[["pbcopy"]];if(process.platform==="win32")return[["clip"]];return[["wl-copy"],["xclip","-selection","clipboard"],["xsel","--clipboard","--input"]]}function bI(){return yI().filter((e)=>{let t=e[0];return typeof t==="string"&&Bun.which(t)!==null})}function xI(e){return`\x1B]52;c;${Buffer.from(e,"utf8").toString("base64")}\x07`}function tw(){if(process.platform==="linux")return"install wl-clipboard (Wayland) or xclip (X11)";return"no clipboard helper found"}async function ml(e){let t=bI(),r;for(let n of t)try{await SI(n,e);return}catch(o){r=o instanceof Error?o:Error(String(o))}if(process.platform!=="win32")try{await wI(e);return}catch(n){r=n instanceof Error?n:Error(String(n))}throw Te(lt,"clipboard unavailable",r?`${tw()} (${r.message})`:tw())}async function wI(e){let t=xI(e);try{await Bun.write("/dev/tty",t)}catch{if(!process.stderr.write(t))throw Error("terminal clipboard write failed")}}async function SI(e,t){let r=Bun.spawn(e,{stdin:"pipe",stdout:"ignore",stderr:"ignore"});r.stdin.write(t),await r.stdin.end();let n=await r.exited;if(n!==0)throw Error(`${e[0]} exited ${n}`)}var qp=I(()=>{te()});function Vp(e){return e==="slash"||e==="config-edit"||e==="setup-wizard"}function rw(e,t){return mn(e,t.keybinds.leader??Ao("x"))}function nw(e,t){return mn(e,t.keybinds.command_list??Ao("p"))}function an(e,t){return mn(e,t.keybinds.command??"/")||e.name==="/"}function ow(e,t){return mn(e,t.keybinds.help??"?")||e.sequence==="?"}function iw(e,t){return mn(e,t.keybinds.search??"f")}function sw(e){return(e.name??"").toLowerCase()==="q"&&!e.ctrl&&!e.meta&&!e.super&&!e.alt&&!e.option}function aw(e){let t=e.name??"";return t==="R"||e.shift===!0&&t.toLowerCase()==="r"}function cw(e){return Or(e)&&(e.name??"").toLowerCase()==="l"}function lw(e,t){return mn(e,t.keybinds.copy??Zr())}function dw(e,t,r=kI){return t>0&&e-t<=r}function Mn(e,t,r,n){return mn(e,t.keybinds[r]??n)}function Ds(e){let t=(e.name??"").toLowerCase();return t==="pagedown"||Or(e)&&t==="d"}function Is(e){let t=(e.name??"").toLowerCase();return t==="pageup"||Or(e)&&t==="u"}var kI=2000;var hl=I(()=>{Rr()});function mi(e){return`http://127.0.0.1:${e}/mcp`}function zp(e){if(e==="")return{};return{Authorization:`Bearer ${e}`}}function Yp(e,t){return JSON.stringify({mcpServers:{devctl:{type:"http",url:e,headers:zp(t)}}},null,2)}function Jp(e,t){return JSON.stringify({mcpServers:{devctl:{url:e,headers:zp(t)}}},null,2)}function Xp(e,t){return JSON.stringify({mcp:{devctl:{type:"remote",url:e,headers:zp(t),enabled:!0}}},null,2)}function Qp(e,t){let r=t===""?"":`
2142
2214
  http_headers = { Authorization = "Bearer ${t}" }`;return`[mcp_servers.devctl]
2143
2215
  url = "${e}"${r}
2144
- `}function yI(e){return`codex mcp add --url ${e}`}function Ds(e,t){return[{kind:"claude",title:"Claude",path:".mcp.json",language:"json",text:Vp(e,t),hint:"claude mcp add-json"},{kind:"cursor",title:"Cursor",path:".cursor/mcp.json",language:"json",text:zp(e,t)},{kind:"kilo",title:"Kilo Code",path:"kilo.jsonc",language:"json",text:Yp(e,t)},{kind:"codex",title:"Codex",path:"~/.codex/config.toml",language:"toml",text:Jp(e,t),hint:yI(e)}]}function iw(e,t){let r=[`URL ${e}`,""];for(let n of Ds(e,t))r.push(`# ${n.title} ${n.path}${n.hint?` (${n.hint})`:""}`),r.push(n.text.trimEnd()),r.push("");return r.join(`
2145
- `)}import{jsxDEV as q}from"@opentui/react/jsx-dev-runtime";function ef(){return sp.flatMap((e)=>Pc.filter((t)=>t.category===e))}function gl(){return hl+ef().length}function cw(){return gl()+aw}function Is(e){if(e<hl||e>=gl())return;return ef()[e-hl]}function tf(e){let t=e-gl();return t>=0&&t<aw?t:void 0}function lw(e){let{palette:t,snap:r,port:n,portDraft:o="",selected:i,onPick:s,onToggle:a,onCopy:c,onToggleTool:l}=e,d=ye(),u=r?.mcp?.running===!0,f=r?.mcp?.port??n,p=r?.mcp?.address??fi(f),m=r?.mcp?.token??"",h=Ds(p,m),v=r?.mcp?.disabled_tools??[],x=ef(),g=x.filter((S)=>!An(S.name,v)).length,R=h[tf(i)??-1],_=R??h[0];return q(kt,{palette:t,title:"mcp settings",children:[q(ut,{palette:t,items:[{text:u?"RUNNING":"STOPPED",tone:u?"success":"idle"},{text:`127.0.0.1:${f}`,tone:"info"}]},void 0,!1,void 0,this),q("box",{flexGrow:1,overflow:"hidden",children:q("scrollbox",{focused:!1,stickyScroll:!1,scrollX:!1,style:Ue(t),children:q("box",{flexDirection:"column",overflow:"hidden",children:[q(Xp,{palette:t,text:"Server"},void 0,!1,void 0,this),q(sw,{palette:t,active:i===Qp,rowH:d.rowH,label:"Listen",value:u?"[ ON ]":"[ OFF ]",valueTone:u?"success":"muted",hint:u?"space or enter stop":"space or enter start",onPick:()=>s(Qp),onActivate:a},void 0,!1,void 0,this),q(sw,{palette:t,active:i===Zp,rowH:d.rowH,label:"Port",value:`\u2039 ${o===""?n:o} \u203A`,valueTone:"info",hint:o===""?"\u2190 \u2192 change type digits enter apply":"typing\u2026 enter apply \u232B erase",onPick:()=>s(Zp)},void 0,!1,void 0,this),q("box",{height:1,overflow:"hidden",children:q("text",{fg:t.muted,wrapMode:"none",children:u?"Listening on loopback. Agents use the URL and token in the JSON below.":"Server is stopped. Highlight Listen and press space to start it."},void 0,!1,void 0,this)},void 0,!1,void 0,this),q(Xp,{palette:t,text:"Tools",extra:g===0?"all enabled":`${g} disabled`},void 0,!1,void 0,this),q("box",{height:1,overflow:"hidden",children:q("text",{fg:t.muted,wrapMode:"none",children:"Disabled tools are hidden from agents and refused if called anyway."},void 0,!1,void 0,this)},void 0,!1,void 0,this),sp.map((S)=>{let y=x.filter((b)=>b.category===S);if(y.length===0)return null;return q("box",{flexDirection:"column",overflow:"hidden",children:[q("box",{height:1,marginTop:1,overflow:"hidden",children:q("text",{fg:t.muted,wrapMode:"none",children:xI[S]??S},void 0,!1,void 0,this)},void 0,!1,void 0,this),y.map((b)=>{let w=hl+x.indexOf(b);return q(kI,{palette:t,tool:b,enabled:An(b.name,v),active:i===w,rowH:d.rowH,onPick:()=>s(w),onToggle:()=>l(b)},b.name,!1,void 0,this)})]},S,!0,void 0,this)}),q(Xp,{palette:t,text:"Copy agent config",extra:"highlight a row, then enter or click Copy"},void 0,!1,void 0,this),h.map((S,y)=>{let b=gl()+y;return q(SI,{palette:t,snippet:S,active:i===b,rowH:d.rowH,onPick:()=>s(b),onCopy:()=>c(S)},S.kind,!1,void 0,this)}),q(CI,{palette:t,snippet:_,selected:Boolean(R),brand:_?Lc(_.kind,t):t.muted},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this),q(Ut,{palette:t,backgroundColor:t.element,edge:"top",children:q(it,{palette:t,hints:wI(i,u)},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function wI(e,t){if(e===Qp)return[{key:"space",label:t?"stop server":"start server"},{key:"j/k",label:"move"}];if(e===Zp)return[{key:"\u2190\u2192",label:"change port"},{key:"0-9",label:"type port"},{key:"enter",label:"apply port"},{key:"j/k",label:"move"}];if(Is(e))return[{key:"space",label:"enable / disable"},{key:"enter",label:"enable / disable"},{key:"j/k",label:"move"}];return[{key:"enter",label:"copy JSON"},{key:"space",label:"copy JSON"},{key:"j/k",label:"move"}]}function Xp(e){let t=e.extra?` \xB7 ${e.extra}`:"";return q("box",{height:1,marginTop:1,overflow:"hidden",children:q("text",{wrapMode:"none",children:[q("span",{fg:e.palette.primary,children:e.text},void 0,!1,void 0,this),q("span",{fg:e.palette.muted,children:t},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this)}function sw(e){let{palette:t,active:r,rowH:n,label:o,value:i,valueTone:s,hint:a,onPick:c,onActivate:l}=e,d=s==="success"?t.success:s==="info"?t.info:t.muted;return q("box",{height:n,flexDirection:"row",overflow:"hidden",backgroundColor:r?t.highlight:t.panel,onMouseDown:()=>{if(r&&l){l();return}c()},children:[q("box",{width:2,flexShrink:0,overflow:"hidden",children:q("text",{fg:t.primary,children:r?"\u203A":" "},void 0,!1,void 0,this)},void 0,!1,void 0,this),q("box",{width:rf,flexShrink:0,overflow:"hidden",children:q("text",{fg:r?t.primary:t.text,wrapMode:"none",children:o},void 0,!1,void 0,this)},void 0,!1,void 0,this),q("box",{width:bI,flexShrink:0,overflow:"hidden",children:q("text",{fg:d,wrapMode:"none",children:i},void 0,!1,void 0,this)},void 0,!1,void 0,this),q("box",{flexGrow:1,overflow:"hidden",children:q("text",{fg:r?t.text:t.muted,wrapMode:"none",children:a},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function SI(e){let{palette:t,snippet:r,active:n,rowH:o,onPick:i,onCopy:s}=e,a=Lc(r.kind,t),c=ax(r.kind,t),l=r.language==="toml"?"Copy TOML":"Copy JSON";return q("box",{height:o,flexDirection:"row",overflow:"hidden",backgroundColor:n?t.highlight:t.panel,onMouseDown:i,children:[q("box",{width:2,flexShrink:0,overflow:"hidden",children:q("text",{fg:a,children:n?"\u203A":" "},void 0,!1,void 0,this)},void 0,!1,void 0,this),q("box",{width:rf,flexShrink:0,overflow:"hidden",children:q("text",{fg:a,wrapMode:"none",children:r.title},void 0,!1,void 0,this)},void 0,!1,void 0,this),q("box",{flexGrow:1,overflow:"hidden",children:q("text",{fg:t.muted,wrapMode:"none",children:r.path},void 0,!1,void 0,this)},void 0,!1,void 0,this),q(Be,{palette:t,label:l,bg:a,fg:c,onMouseDown:()=>{i(),s()}},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function kI(e){let{palette:t,tool:r,enabled:n,active:o,rowH:i,onPick:s,onToggle:a}=e;return q("box",{height:i,flexDirection:"row",overflow:"hidden",backgroundColor:o?t.highlight:t.panel,onMouseDown:()=>{if(o){a();return}s()},children:[q("box",{width:2,flexShrink:0,overflow:"hidden",children:q("text",{fg:t.primary,children:o?"\u203A":" "},void 0,!1,void 0,this)},void 0,!1,void 0,this),q("box",{width:5,flexShrink:0,overflow:"hidden",children:q("text",{fg:n?t.success:t.muted,wrapMode:"none",children:n?" ON ":" OFF "},void 0,!1,void 0,this)},void 0,!1,void 0,this),q("box",{width:rf+4,flexShrink:0,overflow:"hidden",children:q("text",{fg:o?t.primary:n?t.text:t.muted,wrapMode:"none",children:r.label},void 0,!1,void 0,this)},void 0,!1,void 0,this),q("box",{width:7,flexShrink:0,overflow:"hidden",children:q("text",{fg:r.mutates?t.warning:t.muted,wrapMode:"none",children:r.mutates?"write":"read"},void 0,!1,void 0,this)},void 0,!1,void 0,this),q("box",{flexGrow:1,overflow:"hidden",children:q("text",{fg:o?t.text:t.muted,wrapMode:"none",children:o?`space or enter ${n?"disable":"enable"} ${r.name}`:r.summary},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function CI(e){let{palette:t,snippet:r,selected:n,brand:o}=e;if(!r)return null;let i=r.hint?` \xB7 ${r.hint}`:"";return q("box",{marginTop:1,flexDirection:"column",overflow:"hidden",children:[q("text",{fg:o,wrapMode:"none",children:n?`${r.title} \xB7 paste into ${r.path}${i}`:`Preview ${r.title} \xB7 j/k to a copy row, then enter`},void 0,!1,void 0,this),q("text",{fg:t.text,children:r.text},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}var Qp=0,Zp=1,hl=2,aw=4,xI,rf=14,bI=14;var Ms=D(()=>{Mt();ke();tt();us();xI={inspect:"Inspect",logs:"Logs",diagnostics:"Diagnostics",control:"Control",setup:"Setup"}});import{jsxDEV as Fe,Fragment as uw}from"@opentui/react/jsx-dev-runtime";function DI(e){return[{key:"\u2190\u2192",label:"filter services"},{key:"e",label:"ERROR+ only"},{key:"g",label:"jump to latest"},{key:"p",label:"pause stream"},{key:"z",label:"full-screen logs"},{key:"\\",label:"split panes"},{key:"|",label:"focus other pane"},{key:"/trace",label:"jump to request id"},{key:"f",label:"search"},{key:"t",label:"timestamps"},{key:"m",label:"metadata"},{key:"w",label:"clip / wrap selected / wrap all"},{key:"j/k",label:"move and unfold"},{key:Ri(e),label:"copy selection"},{key:"/export",label:"write ~/.devctl/exports"},{key:"/exports",label:"open export folder"}]}function $n(e){return e+_I}function pw(e){let t=$n(of.length),r=$n(sf.length),n=$n(e.logCount),o=$n(af.length),i=$n(cf.length),s=lf+e.pad*2;if(e.stacked)return s+t+r+n+o+i+nf+(AI-1)*e.gap;let a=Math.max(t,r),c=Math.max(o,i);return s+a+n+c+nf+(LI-1)*e.gap}function II(e){return pw(e)-lf-e.pad*2}function MI(e){return e-lf*2<RI}function fw(e){let{palette:t,termW:r,termH:n,copyKey:o=Zr(),scrollRef:i}=e,s=go(ye()),a=MI(r),c=s?0:1,l=s?0:1,d=DI(o),u={stacked:a,logCount:d.length,pad:c,gap:l},f=pw(u),p=II(u);return Fe(pt,{palette:t,title:"help",bottomTitle:`${er()} \xB7 j/k scroll \xB7 esc close`,termW:r,termH:n,preferW:PI,preferH:f,gap:0,children:Fe("scrollbox",{ref:i,focused:!1,stickyScroll:!1,scrollX:!1,style:Ue(t),children:Fe("box",{height:p,flexDirection:"column",flexShrink:0,overflow:"hidden",gap:l,children:[a?Fe(uw,{children:[Fe(On,{palette:t,title:"navigation",bindings:of},void 0,!1,void 0,this),Fe(On,{palette:t,title:"services",bindings:sf},void 0,!1,void 0,this),Fe(On,{palette:t,title:"logs",bindings:d},void 0,!1,void 0,this),Fe(On,{palette:t,title:"commands",bindings:af},void 0,!1,void 0,this),Fe(On,{palette:t,title:"display",bindings:cf},void 0,!1,void 0,this)]},void 0,!0,void 0,this):Fe(uw,{children:[Fe(dw,{palette:t,left:{title:"navigation",bindings:of},right:{title:"services",bindings:sf}},void 0,!1,void 0,this),Fe(On,{palette:t,title:"logs",bindings:d},void 0,!1,void 0,this),Fe(dw,{palette:t,left:{title:"commands",bindings:af},right:{title:"display",bindings:cf}},void 0,!1,void 0,this)]},void 0,!0,void 0,this),Fe("box",{height:nf,flexShrink:0,overflow:"hidden",paddingLeft:1,children:Fe("text",{fg:t.muted,wrapMode:"none",children:"~/.devctl/tui.json \xB7 DEVCTL_TUI_CONFIG"},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this)}function dw(e){let{palette:t,left:r,right:n}=e,o=Math.max($n(r.bindings.length),$n(n.bindings.length));return Fe("box",{height:o,flexDirection:"row",flexShrink:0,overflow:"hidden",gap:TI,children:[Fe(On,{palette:t,title:r.title,bindings:r.bindings,height:o,stretch:!0},void 0,!1,void 0,this),Fe(On,{palette:t,title:n.title,bindings:n.bindings,height:o,stretch:!0},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function On(e){let{palette:t,title:r,bindings:n,stretch:o=!1}=e,i=e.height??$n(n.length),s=OI(n);return Fe("box",{height:i,flexGrow:o?1:0,flexBasis:o?0:void 0,flexShrink:0,flexDirection:"column",overflow:"hidden",border:!0,borderStyle:"rounded",borderColor:t.border,title:r,titleColor:t.accent,children:n.map((a)=>Fe("box",{height:1,flexShrink:0,overflow:"hidden",paddingLeft:1,paddingRight:1,children:Fe($I,{palette:t,binding:a,keyWidth:s},void 0,!1,void 0,this)},`${a.key}-${a.label}`,!1,void 0,this))},void 0,!1,void 0,this)}function OI(e){return e.reduce((r,n)=>Math.max(r,n.key.length),0)+EI}function $I(e){let{palette:t,binding:r,keyWidth:n}=e;return Fe("box",{height:1,flexDirection:"row",overflow:"hidden",children:[Fe("box",{width:n,flexShrink:0,overflow:"hidden",children:Fe("text",{fg:t.primary,wrapMode:"none",children:r.key},void 0,!1,void 0,this)},void 0,!1,void 0,this),Fe("box",{flexGrow:1,overflow:"hidden",children:Fe("text",{fg:t.text,wrapMode:"none",children:r.label},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}var PI=78,RI=56,_I=2,lf=2,nf=1,df=8,EI=2,TI=2,AI=6,LI=4,of,sf,af,cf;var uf=D(()=>{Mt();ke();Er();_r();of=[{key:"tab",label:"next tab"},{key:"1-4",label:"dashboard services logs proxy"},{key:"s/l/a/p/d/c/u",label:"letter jump"},{key:"/",label:"command overlay \xB7 other screens"},{key:"j/k",label:"move selection"},{key:"esc",label:"back / close overlay"},{key:"esc \xD72",label:"quit"},{key:"q \xD72",label:"quit"}],sf=[{key:"enter",label:"start profile or open"},{key:"space",label:"multi-select"},{key:"n",label:"start"},{key:"x",label:"stop"},{key:"r",label:"refresh"},{key:"R",label:"restart (c cascade if dependents)"}];af=[{key:It("c"),label:"copy highlighted selection"},{key:"/",label:"command overlay"},{key:It("p"),label:"same overlay"},{key:It("x"),label:"leader chord"},{key:"/settings",label:"preferences \xB7 MCP page"},{key:"/themes",label:"preview themes"},{key:"/diff",label:"config sources"},{key:"/mcp",label:"agent MCP server"}],cf=[{key:It("="),label:"larger"},{key:It("-"),label:"smaller"},{key:"?",label:"this overlay"}]});function mi(e){e.setQuery(""),e.setSlashIndex(0),e.setSlashPicker("commands"),e.setOverlay("slash")}function mw(e,t){let{overlay:r,tui:n,height:o,closeOverlay:i,confirmKind:s,onQuit:a,confirmAction:c,planBusy:l,logDetailsScrollRef:d,scrollTextScrollRef:u,routeDetailsScrollRef:f,planScrollRef:p,helpScrollRef:m,revertThemePreview:h,setPaletteIndex:v,setThemeName:x,paletteIndex:g,applyTheme:R,leaderTimer:_,setOverlay:S,runCommand:y,setConfigEditError:b,saveConfigBuffer:w,setSlashIndex:k,filtered:L,setQuery:O,slashIndex:F,submitSlash:Z,advanceWizard:W}=e,C=(t.name??"").toLowerCase();if(r==="none")return!1;if(r==="setup-wizard"){if(C==="escape")return i(),!0;if(C==="return")return W?.(),!0;return Kp(r)}if(r==="confirm"){if(C==="escape")return i(),!0;if(s==="quit"&&C==="d")return a(!0),!0;if(s==="quit"&&C==="k")return e.onDown(!0),!0;if(s==="restart-cascade"&&C==="c")return c("cascade"),!0;if(C==="return")c();return!0}if(r==="log-details"||r==="scroll-text"){if(an(t,n))return mi(e),!0;if(C==="escape")return i(),!0;if(r==="log-details"&&C==="return"){let B=e.logDetail?.request_id?.trim()??"";if(B!=="")e.setLogSearch(B),e.setScreen("logs"),e.closeOverlay(),e.setStatus(`tracing ${B}`);return!0}let j=r==="log-details"?d.current:u.current;if(C==="down"||C==="j")return rt(j,n.scroll_speed),!0;if(C==="up"||C==="k")return rt(j,-n.scroll_speed),!0;if(As(t))return rt(j,li(o)),!0;if(Ls(t))return rt(j,-li(o)),!0;return!0}if(r==="route-details"){if(an(t,n))return mi(e),!0;if(C==="escape"||C==="return")return i(),!0;if(C==="down"||C==="j")return rt(f.current,n.scroll_speed),!0;if(C==="up"||C==="k")return rt(f.current,-n.scroll_speed),!0;return!0}if(r==="plan"){if(an(t,n))return mi(e),!0;if(C==="escape"||C==="return"&&!l)return i(),!0;if(C==="down"||C==="j")return rt(p.current,n.scroll_speed),!0;if(C==="up"||C==="k")return rt(p.current,-n.scroll_speed),!0;return!0}if(r==="help"){if(an(t,n))return mi(e),!0;if(C==="escape")return i(),!0;if(C==="down"||C==="j")return rt(m.current,1),!0;if(C==="up"||C==="k")return rt(m.current,-1),!0;if(C==="pagedown"||As(t))return rt(m.current,df),!0;if(C==="pageup"||Ls(t))return rt(m.current,-df),!0;return!0}if(r==="themes"){if(an(t,n))return h(),mi(e),!0;if(C==="escape")return h(),i(),!0;if(C==="down"||C==="j")return v((j)=>{let B=Math.min(j+1,St.length-1),z=St[B];if(z)x(z);return B}),!0;if(C==="up"||C==="k")return v((j)=>{let B=Math.max(0,j-1),z=St[B];if(z)x(z);return B}),!0;if(C==="return"){let j=St[g%St.length];if(j)R(j)}return!0}if(r==="leader"){if(an(t,n)){if(_.current)clearTimeout(_.current);return mi(e),!0}if(_.current)clearTimeout(_.current);S("none");let j=db(C),B=j?Wr(j):void 0;if(B)y(B,[]);return!0}if(r==="config-edit"){if(C==="escape")return b(""),i(),!0;if(Or(t)&&C==="s")return w(),!0;return!0}if(Kp(r)){if(C==="escape")return i(),!0;if(r==="slash"&&C==="down")return k((j)=>Math.min(Math.max(L.length-1,0),j+1)),!0;if(r==="slash"&&C==="up")return k((j)=>Math.max(0,j-1)),!0;if(r==="slash"&&C==="tab"){if(t.shift)return k((B)=>Math.max(0,B-1)),!0;let j=di(L,F);if(j)O(wb(j));return!0}if(r==="slash"&&C==="return")return Z(),!0;return!0}return!0}var hw=D(()=>{vr();So();ml();_r();ke();uf();tt()});var vl=D(()=>{Xu()});function gw(e,t){if(t.length===0)return[];let r=new Set(t);return lo(e,t).filter((n)=>!r.has(n))}function vw(e,t,r){let n=r!==""&&e.profiles[r]?r:"";if(t.length>0)return{services:t,profile:n};if(n!=="")return{services:[...e.profiles[r]?.services??[]],profile:n};return{services:Object.keys(e.services),profile:""}}function yl(e){return e.waves.flat().join(" \u2192 ")}function yw(e){let t=yl(e);if(t==="")return"Started selected services";return`Started ${t}`}function xw(e){let t=yl(e);if(t==="")return"Stopped running services";return`Stopped ${t}`}function bw(e,t,r,n="start"){let o=r?.services[e],i=tr(o);if(o?.last_error&&n!=="stop")return o.last_error;if(n==="stop"){if(i==="STOPPED")return"stopped";if(i==="STOPPING")return"stopping now";if(i==="FAILED")return"already failed";return"queued"}if(i==="HEALTHY")return"ready";if(i==="STARTING")return"starting now";if(i==="RUNNING")return"up, waiting for health";if(i==="UNHEALTHY")return"unhealthy \u2014 retrying health check";if(i==="STOPPING")return"stopping first";if(i==="FAILED")return"failed \u2014 later waves will not start";let a=(t.steps.find((c)=>c.name===e)?.dependencies??[]).filter((c)=>{let l=tr(r?.services[zt(c)]);return gn(c)==="service_healthy"?l!=="HEALTHY":l==="STOPPED"||l==="FAILED"}).map(zt);if(a.length>0)return`waits for ${a.join(", ")}`;return"queued"}function Os(e,t,r="start"){if(e.length===0)return"completed";let n=e.map((i)=>t?.services[i]);if(n.some((i)=>i?.state==="FAILED"))return"failed";if(r==="stop"){if(n.every((i)=>!i||i.state==="STOPPED"||i.state==="UNKNOWN"))return"completed";if(n.some((i)=>i?.state==="STOPPING"))return"active";return"queued"}let o=(i)=>Boolean(i&&(i.state==="HEALTHY"||i.health==="HEALTHY"||i.state==="RUNNING"&&i.health!=="UNHEALTHY"));if(n.every(o))return"completed";if(n.some((i)=>i?.state==="UNHEALTHY"||i?.health==="UNHEALTHY"))return"unhealthy";if(n.some((i)=>i?.state==="STARTING"||i?.state==="RUNNING"||i?.state==="RESTARTING"))return"active";return"queued"}function ww(e,t,r="start"){let o=e.waves.flat().length,i=0,s=0,a=0,c=0;for(let m=0;m<e.waves.length;m++){let h=e.waves[m]??[],v=Os(h,t,r);if(v==="active"||v==="unhealthy"||v==="failed")c=m;else if(v==="completed"&&c===m&&m<e.waves.length-1)c=m+1;for(let x of h){let g=t?.services[x];if(r==="stop"?!g||g.state==="STOPPED"||g.state==="UNKNOWN":Boolean(g&&(g.state==="HEALTHY"||g.health==="HEALTHY"||g.state==="RUNNING"&&g.health!=="UNHEALTHY")))i++;else if(g?.state==="FAILED")s++;else if(g?.state==="STARTING"||g?.state==="RUNNING"||g?.state==="UNHEALTHY"||g?.health==="UNHEALTHY"||g?.state==="STOPPING"||g?.state==="RESTARTING")a++}}let l=o>0?Math.round(i/o*100):100,d=16,u=Math.round(l/100*d),f="\u2588".repeat(u)+"\u2591".repeat(Math.max(0,d-u)),p=i===o&&o>0;return{total:o,ready:i,active:a,failed:s,percent:l,progressBar:f,currentWaveIndex:c,totalWaves:e.waves.length,isComplete:p}}function Sw(e,t,r){let n=r==="completed"?"\u2713 Completed":r==="failed"?"\u2717 Failed":r==="unhealthy"?"\u26A0 Unhealthy":r==="active"?"\u23F3 In Progress":"\u25CB Queued";return`${e==="stop"?t===0?"Wave 1 (Stop First)":`Wave ${t+1}`:t===0?"Wave 1 (Start First)":`Wave ${t+1}`} \xB7 ${n}`}function kw(e,t,r,n){let o=n?` \xB7 Profile ${n}`:"";if(r)return`${e==="stop"?"Shutdown":"Startup"} Failed (${r})`;if(t)return`${e==="stop"?"Stopping Services":e==="restart"?"Restarting Pipeline":"Starting Pipeline"}${o}`;return`${e==="stop"?"Shutdown Complete":"Startup Complete"}${o}`}function Cw(e,t){if(e)return{primary:"Working\u2026 esc hide this panel (keeps running in background)",secondary:"Services are transitioning. You can dismiss anytime without stopping them."};if(t)return{primary:"enter or esc back to dashboard",secondary:"Execution stopped. Check logs or open Doctor to resolve errors."};return{primary:"enter or esc back to dashboard",secondary:"All waves finished. Click here or press enter to continue."}}var xl=D(()=>{ht();Tt();At()});function Pw(e,t){let r=(t.name??"").toLowerCase();if(e.screen==="mcp"&&e.listCursor===1&&r.length===1&&r>="0"&&r<="9")return e.setMcpPortDraft((n)=>Py(n,r)),!0;return!1}function Rw(e,t){let r=(t.name??"").toLowerCase(),{screen:n,overlay:o,tui:i,cfg:s,controller:a,profile:c,listCursor:l,names:d,listCount:u,height:f,checked:p,detailName:m,bootErrorMissing:h,bootError:v,logSearchFocused:x,logSources:g,logService:R,logShowTimestamps:_,logShowMeta:S,logWrap:y,showSystemLogs:b,logPinned:w,dashboardLogCursor:k,applyLogCursor:L,applyDashboardLogCursor:O,jumpToLatestLogs:F,toggleSystemLogs:Z,clearLogs:W,setLogService:C,setLogShowTimestamps:j,setLogShowMeta:B,setPaused:z,setErrorOnly:de,setLogWrap:Ee,beginStart:Ne,beginStop:ne,beginRestart:H,applyMcpPortDraft:ae,toggleMcp:X,toggleMcpTool:nt,copyFocusedMcpSnippet:ft,persistMcpPort:ie,restartMcpOnPort:Le,setMcpPortDraft:Me,settingRows:Ke,persistPrefs:gt,cycleSetting:Xe,toggleMouse:Ct,setDoctorTick:ge,refreshAuth:vt,configScrollRef:at,detailScrollRef:yt,handleEnter:rr,setScreen:Qe,setChecked:Xt,setStatus:qe,setSelected:mt,setProfile:xr,refresh:Kt,toggleChecked:br,createStarterConfig:nr,startWizard:qt,setConfirmKind:or,setConfirmDetail:ir,setOverlay:sr,openConfigBuffer:Vt,runCommand:ce}=e,K=(n==="logs"||n==="dashboard")&&!x;if(K&&(r==="left"||r==="["||n==="logs"&&r==="h")){C(Ap(g,R,-1));return}if(K&&(r==="right"||r==="]"||n==="logs"&&r==="l")){C(Ap(g,R,1));return}if((n==="dashboard"||n==="setup")&&r==="o"){Qe("profiles");return}if((n==="dashboard"||n==="services")&&(r==="*"||t.shift&&r==="8")){Xt([...d]),qe(`Selected ${d.length} services`);return}if((n==="dashboard"||n==="services")&&(r==="-"||r==="_"||r==="minus")){Xt([]),qe("Selection cleared");return}if((n==="dashboard"||n==="services")&&r==="n"){Ne(jc(p,d[l]??""),c);return}if((n==="dashboard"||n==="services")&&r==="x"){ne(jc(p,d[l]??""));return}if((n==="dashboard"||n==="services"||n==="detail")&&tw(t)){let T=jc(p,d[l]??m);if(s&&gw(s,T).length>0){or("restart-cascade"),ir({services:T}),sr("confirm");return}H(T,c);return}if((n==="dashboard"||n==="services")&&r==="r"){Kt(),qe("Refreshed");return}if(n==="auth"&&r==="r"){vt();return}if(n==="logs"&&r==="t"){let T=!_;j(T),gt({log_timestamps:T},T?"timestamps on":"timestamps off");return}if(n==="logs"&&r==="m"){let T=!S;B(T),gt({log_metadata:T},T?"metadata on":"metadata off");return}if(r==="down"||r==="j"){if(n==="mcp"&&l===1)ae();if(n==="config"){if(u>0)mt((T)=>Math.min(Math.max(u-1,0),T+1));rt(at.current,i.scroll_speed);return}if(n==="detail"){rt(yt.current,i.scroll_speed);return}if(n==="logs"){L(l+1);return}if(n==="dashboard"&&k>=0){O(k+1);return}mt((T)=>Math.min(Math.max(u-1,0),T+1));return}if(r==="up"||r==="k"){if(n==="mcp"&&l===1)ae();if(n==="config"){if(u>0)mt((T)=>Math.max(0,Math.min(T,Math.max(u-1,0))-1));rt(at.current,-i.scroll_speed);return}if(n==="detail"){rt(yt.current,-i.scroll_speed);return}if(n==="logs"){L(l-1);return}if(n==="dashboard"&&k>=0){O(k-1);return}mt((T)=>Math.max(0,Math.min(T,Math.max(u-1,0))-1));return}if(As(t)){let T=li(f);if(n==="config"){rt(at.current,T);return}if(n==="detail"){rt(yt.current,T);return}if(n==="logs"){L(l+T);return}if(n==="dashboard"&&(w||k>=0)){O(Math.max(0,k)+T);return}mt((ve)=>Math.min(Math.max(u-1,0),ve+T));return}if(Ls(t)){let T=li(f);if(n==="config"){rt(at.current,-T);return}if(n==="detail"){rt(yt.current,-T);return}if(n==="logs"){L(l-T);return}if(n==="dashboard"&&(w||k>=0)){O(Math.max(0,k)-T);return}mt((ve)=>Math.max(0,ve-T));return}if(n==="proxy"&&r==="n"){a?.proxyStart().then(async()=>{qe("Proxy started"),await Kt()});return}if(n==="proxy"&&r==="x"){a?.proxyStop().then(async()=>{qe("Proxy stopped"),await Kt()});return}if(n==="detail"&&m!==""){if(r==="n"){Ne([m],c);return}if(r==="x"){ne([m]);return}if(r==="o"){Qe("config");return}if(r==="l"){C(m),Qe("logs");return}}if(n==="mcp"&&l===1&&(r==="left"||r==="h")){let T=_n(ae()-1);ie(T),Le(T);return}if(n==="mcp"&&l===1&&(r==="right"||r==="l")){let T=_n(ae()+1);ie(T),Le(T);return}if(n==="mcp"&&l===1&&(r==="backspace"||r==="delete")){Me((T)=>Ry(T));return}if(n==="settings"&&(r==="left"||r==="h")){Xe(-1,l);return}if(n==="settings"&&(r==="right"||r==="l")){Xe(1,l);return}if(r==="space"){if(n==="profiles"){let Y=Object.keys(s?.profiles??{}).sort()[l];if(Y)xr(Y),qe(`Profile ${Y}`);return}if(n==="mcp"){if(l===0){X();return}let ve=Is(l);if(ve){nt(ve);return}ft(l);return}if(n==="logs")return;if(n==="settings"){if(Ln(Ke,l)?.id==="mouse")Ct();return}let T=d[l];if(T)br(T);return}if(r==="return"){if(n==="setup"&&!a){if(!h){qe(v||"Existing configuration is invalid \u2014 fix it and restart devctl.");return}qt();return}if(n==="setup"&&a){Ne([],c);return}rr();return}if(Mn(t,i,"services","s")&&o==="none"&&n!=="logs"){Qe("services");return}if(Mn(t,i,"logs","l")&&o==="none"&&n!=="detail"&&n!=="logs"&&n!=="settings"){Qe("logs");return}if(Mn(t,i,"auth","a")&&o==="none"){Qe("auth");return}if(Mn(t,i,"proxy","p")&&o==="none"&&n!=="logs"){Qe("proxy");return}if(Mn(t,i,"doctor","d")&&o==="none"&&n!=="doctor"){Qe("doctor");return}if(Mn(t,i,"config","c")&&o==="none"){Qe("config");return}if(Mn(t,i,"setup","u")&&o==="none"){Qe("setup");return}if(n==="config"&&r==="e"){ce(Wr("/edit")??{name:"edit",aliases:[],desc:"",leader:"",group:"ui"},[]);return}if(n==="config"&&r==="v"){Vt();return}if(n==="doctor"&&r==="r"){ge((T)=>T+1),qe("Re-running doctor");return}if(n==="logs"&&r==="p"){z((T)=>!T);return}if((n==="logs"||n==="dashboard")&&r==="g"){F();return}if((n==="logs"||n==="dashboard")&&r==="w"){let T=Xc(y);Ee(T),qe(`Log ${Zc(T)}`);return}if((n==="logs"||n==="dashboard")&&r==="e"){de((T)=>!T);return}if((n==="logs"||n==="dashboard")&&r==="i"){Z(),qe(b?"Hiding internal auth/mcp/devctl/proxy logs":"Showing internal logs");return}if((n==="logs"||n==="dashboard")&&rw(t))W()}var _w=D(()=>{vl();vr();gr();xl();At();ml();ke();Ms();Er()});import{useKeyboard as FI}from"@opentui/react";import{useCallback as pf}from"react";function Ew({tui:e,controller:t,cfg:r,snap:n,ui:o,logView:i,lifecycleActions:s,mcp:a,preferences:c,diagnostics:l,refs:d}){let{screen:u,onQuit:f,closeOverlay:p,confirmKind:m,confirmDetail:h,portTarget:v,profile:x,listCursor:g,names:R,runCommand:_,copySelection:S,setOverlay:y,setConfirmKind:b,setConfirmDetail:w,setPortTarget:k,setLogDetail:L,setProfile:O,setStatus:F,setSlashIndex:Z,setQuery:W,setSlashPicker:C,setScreen:j,freePort:B,openDetail:z}=o,{logSlice:de,logSliceB:Ee,logSearchFocused:Ne,logsFullscreen:ne,setLogSearchFocused:H,setLogsFullscreen:ae,splitLogs:X,splitFocus:nt,toggleSplitLogs:ft,cycleSplitFocus:ie,setActiveLogService:Le,logService:Me,logServiceB:Ke}=i,{beginStart:gt,beginRestart:Xe,planBusy:Ct}=s,{applyMcpPortDraft:ge,toggleMcp:vt,toggleMcpTool:at,copyFocusedMcpSnippet:yt}=a,{settingRows:rr,activateSetting:Qe,applyFont:Xt,applyReset:qe,fontSize:mt,revertThemePreview:xr,setThemeName:Kt,leaderMs:br}=c,{doctor:nr}=l,{interruptArmedAt:qt,leaderTimer:or}=d,ir=pf(()=>{if(r?.shutdown.stop_services_on_exit===void 0){b("quit"),y("confirm");return}f(r.shutdown.stop_services_on_exit===!1)},[r,f,b,y]),sr=pf((ce)=>{if(m==="quit"){f(!1);return}if(m==="reload"){p();let K=n?.restart_required??[];if(K.length>0)Xe(K,x);return}if(m==="restart-cascade"){let K=h?.services??[];p(),Xe(K,x,ce==="cascade");return}if(m==="reset-prefs"){p(),qe();return}if(m==="free-port"){let K=v;if(p(),!K)return;B(K).then(()=>{F(`Stopped ${K.command} (pid ${K.pid}) on port ${K.port}`),l.setDoctorTick((T)=>T+1)}).catch((T)=>{F(A(T))});return}p(),gt([],x)},[qe,Xe,gt,p,h,m,l,B,f,v,x,F,n]),Vt=pf(()=>{if(u==="logs"){let K=(X&&nt===1?Ee:de)[g];if(K)L(K),y("log-details");return}if(u==="profiles"){let T=Object.keys(r?.profiles??{}).sort()[g];if(T)O(T),b("start-profile"),y("confirm");return}if(u==="doctor"){let K=nr?.checks[g];if(K?.action?.kind==="free-port"){let T=K.action.holder;k(T),w({port:T.port,pid:T.pid,process:T.command}),b("free-port"),y("confirm")}return}if(u==="mcp"){if(g===0){vt();return}if(g===1){ge();return}let K=Is(g);if(K){at(K);return}yt(g);return}if(u==="settings"){let K=Ln(rr,g);if(K)Qe(K);return}if(u==="config"){let K=Object.keys(r?.tasks??{}).sort()[g],T=Wr("run");if(K&&T)_(T,[K]);return}if(u==="dashboard"&&Hc(n)){gt([],x);return}let ce=R[g];if(ce&&(u==="dashboard"||u==="services"))z(ce)},[Qe,ge,gt,r,yt,nr,g,de,Ee,R,z,x,_,u,w,b,L,y,k,O,rr,n,nt,X,vt,at]);FI((ce)=>{let K=(ce.name??"").toLowerCase();if(nw(ce,e)){S();return}if(Or(ce)&&(K==="="||K==="+"||K==="plus")){Xt(ys(mt,1));return}if(Or(ce)&&(K==="-"||K==="_"||K==="minus")){Xt(ys(mt,-1));return}if(Or(ce)&&K==="0"){Xt(Ic().font_size);return}if(u==="logs"&&!Ne&&(ce.sequence==="\\"||K==="\\")){ft(),F(X?"Single log pane":"Split log panes");return}if(u==="logs"&&!Ne&&(ce.sequence==="|"||K==="|")){ie();return}if(mw({...o,tui:e,confirmAction:sr,planBusy:Ct,leaderTimer:or,logDetailsScrollRef:d.logDetailsScrollRef,scrollTextScrollRef:d.scrollTextScrollRef,routeDetailsScrollRef:d.routeDetailsScrollRef,planScrollRef:d.planScrollRef,helpScrollRef:d.helpScrollRef,revertThemePreview:xr,setThemeName:Kt},ce))return;if(Ne){if(K==="escape")H(!1);return}if(Jb(ce,e)){y("leader"),or.current=setTimeout(()=>y("none"),br);return}if(Xb(ce,e)||an(ce,e)){W(""),Z(0),C("commands"),y("slash");return}if(Qb(ce,e)){y("help");return}if(Zb(ce,e)||u==="logs"&&K==="f"&&!ce.ctrl&&!ce.meta){j("logs"),H(!0);return}if(K==="escape"||ew(ce)){if(K==="escape"){if(ne){ae(!1);return}if(u==="detail"){j("services");return}if(u==="setup"&&t){j("dashboard");return}}let Y=Date.now();if(ow(Y,qt.current)){qt.current=0,ir();return}qt.current=Y,F(K==="q"?"q again to quit":"Esc again to quit");return}if(K==="z"&&(u==="logs"||u==="dashboard")&&!ce.ctrl&&!ce.meta){j("logs"),ae((Y)=>u==="logs"?!Y:!0);return}if(K==="tab"){j(ce.shift?yb(u):vb(u));return}let T={...o,tui:e,controller:t,cfg:r,snap:n,...i,...s,...a,...c,logService:X&&nt===1?Ke:Me,setLogService:Le,setDoctorTick:l.setDoctorTick,refreshAuth:l.refreshAuth,configScrollRef:d.configScrollRef,detailScrollRef:d.detailScrollRef,handleEnter:Vt};if(Pw(T,ce))return;let ve=Yx(K);if(ve){j(ve);return}Rw(T,ce)})}var Tw=D(()=>{te();So();In();At();ml();Ms();Er();_r();hw();_w()});import{parse as NI,stringify as HI}from"yaml";function bl(e,t="imported"){let r=NI(e);if(!r||typeof r!=="object"||Array.isArray(r))throw Error("compose file is not a mapping");let n=r,o=hi(n.services)?n.services:{},i=[];if(n.networks!==void 0)i.push({service:"(root)",field:"networks",reason:Aw.networks??"not modeled"});let s={},a=[];for(let[l,d]of Object.entries(o)){if(!hi(d))continue;let u=jI(l,d,i);if(s[l]=u.body,!u.container)a.push(l)}return{yaml:HI({version:1,project:{name:t},services:s,profiles:{default:{services:a}}}),dropped:i,services:Object.keys(s),profileServices:a}}function wl(e){let t=[e.yaml.trimEnd(),"","Dropped fields:"];if(e.dropped.length===0)t.push("(none)");else{t.push("service\tfield\treason");for(let r of e.dropped)t.push(`${r.service} ${r.field} ${r.reason}`)}if(e.profileServices.length<e.services.length)t.push(""),t.push("Container services were omitted from profile default so first start does not require Docker.");return`${t.join(`
2216
+ `}function CI(e){return`codex mcp add --url ${e}`}function Ms(e,t){return[{kind:"claude",title:"Claude",path:".mcp.json",language:"json",text:Yp(e,t),hint:"claude mcp add-json"},{kind:"cursor",title:"Cursor",path:".cursor/mcp.json",language:"json",text:Jp(e,t)},{kind:"kilo",title:"Kilo Code",path:"kilo.jsonc",language:"json",text:Xp(e,t)},{kind:"codex",title:"Codex",path:"~/.codex/config.toml",language:"toml",text:Qp(e,t),hint:CI(e)}]}function uw(e,t){let r=[`URL ${e}`,""];for(let n of Ms(e,t))r.push(`# ${n.title} ${n.path}${n.hint?` (${n.hint})`:""}`),r.push(n.text.trimEnd()),r.push("");return r.join(`
2217
+ `)}import{jsxDEV as q}from"@opentui/react/jsx-dev-runtime";function rf(){return cp.flatMap((e)=>_c.filter((t)=>t.category===e))}function vl(){return gl+rf().length}function mw(){return vl()+fw}function Os(e){if(e<gl||e>=vl())return;return rf()[e-gl]}function nf(e){let t=e-vl();return t>=0&&t<fw?t:void 0}function hw(e){let{palette:t,snap:r,port:n,portDraft:o="",selected:i,onPick:s,onToggle:a,onCopy:c,onToggleTool:l}=e,d=ye(),u=r?.mcp?.running===!0,f=r?.mcp?.port??n,p=r?.mcp?.address??mi(f),m=r?.mcp?.token??"",h=Ms(p,m),v=r?.mcp?.disabled_tools??[],b=rf(),g=b.filter((S)=>!An(S.name,v)).length,_=h[nf(i)??-1],R=_??h[0];return q(kt,{palette:t,title:"mcp settings",children:[q(ut,{palette:t,items:[{text:u?"RUNNING":"STOPPED",tone:u?"success":"idle"},{text:`127.0.0.1:${f}`,tone:"info"}]},void 0,!1,void 0,this),q("box",{flexGrow:1,overflow:"hidden",children:q("scrollbox",{focused:!1,stickyScroll:!1,scrollX:!1,style:Ue(t),children:q("box",{flexDirection:"column",overflow:"hidden",children:[q(Zp,{palette:t,text:"Server"},void 0,!1,void 0,this),q(pw,{palette:t,active:i===ef,rowH:d.rowH,label:"Listen",value:u?"[ ON ]":"[ OFF ]",valueTone:u?"success":"muted",hint:u?"space or enter stop":"space or enter start",onPick:()=>s(ef),onActivate:a},void 0,!1,void 0,this),q(pw,{palette:t,active:i===tf,rowH:d.rowH,label:"Port",value:`\u2039 ${o===""?n:o} \u203A`,valueTone:"info",hint:o===""?"\u2190 \u2192 change type digits enter apply":"typing\u2026 enter apply \u232B erase",onPick:()=>s(tf)},void 0,!1,void 0,this),q("box",{height:1,overflow:"hidden",children:q("text",{fg:t.muted,wrapMode:"none",children:u?"Listening on loopback. Agents use the URL and token in the JSON below.":"Server is stopped. Highlight Listen and press space to start it."},void 0,!1,void 0,this)},void 0,!1,void 0,this),q(Zp,{palette:t,text:"Tools",extra:g===0?"all enabled":`${g} disabled`},void 0,!1,void 0,this),q("box",{height:1,overflow:"hidden",children:q("text",{fg:t.muted,wrapMode:"none",children:"Disabled tools are hidden from agents and refused if called anyway."},void 0,!1,void 0,this)},void 0,!1,void 0,this),cp.map((S)=>{let y=b.filter((x)=>x.category===S);if(y.length===0)return null;return q("box",{flexDirection:"column",overflow:"hidden",children:[q("box",{height:1,marginTop:1,overflow:"hidden",children:q("text",{fg:t.muted,wrapMode:"none",children:PI[S]??S},void 0,!1,void 0,this)},void 0,!1,void 0,this),y.map((x)=>{let w=gl+b.indexOf(x);return q(TI,{palette:t,tool:x,enabled:An(x.name,v),active:i===w,rowH:d.rowH,onPick:()=>s(w),onToggle:()=>l(x)},x.name,!1,void 0,this)})]},S,!0,void 0,this)}),q(Zp,{palette:t,text:"Copy agent config",extra:"highlight a row, then enter or click Copy"},void 0,!1,void 0,this),h.map((S,y)=>{let x=vl()+y;return q(EI,{palette:t,snippet:S,active:i===x,rowH:d.rowH,onPick:()=>s(x),onCopy:()=>c(S)},S.kind,!1,void 0,this)}),q(AI,{palette:t,snippet:R,selected:Boolean(_),brand:R?Dc(R.kind,t):t.muted},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this),q(Ut,{palette:t,backgroundColor:t.element,edge:"top",children:q(it,{palette:t,hints:RI(i,u)},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function RI(e,t){if(e===ef)return[{key:"space",label:t?"stop server":"start server"},{key:"j/k",label:"move"}];if(e===tf)return[{key:"\u2190\u2192",label:"change port"},{key:"0-9",label:"type port"},{key:"enter",label:"apply port"},{key:"j/k",label:"move"}];if(Os(e))return[{key:"space",label:"enable / disable"},{key:"enter",label:"enable / disable"},{key:"j/k",label:"move"}];return[{key:"enter",label:"copy JSON"},{key:"space",label:"copy JSON"},{key:"j/k",label:"move"}]}function Zp(e){let t=e.extra?` \xB7 ${e.extra}`:"";return q("box",{height:1,marginTop:1,overflow:"hidden",children:q("text",{wrapMode:"none",children:[q("span",{fg:e.palette.primary,children:e.text},void 0,!1,void 0,this),q("span",{fg:e.palette.muted,children:t},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this)}function pw(e){let{palette:t,active:r,rowH:n,label:o,value:i,valueTone:s,hint:a,onPick:c,onActivate:l}=e,d=s==="success"?t.success:s==="info"?t.info:t.muted;return q("box",{height:n,flexDirection:"row",overflow:"hidden",backgroundColor:r?t.highlight:t.panel,onMouseDown:()=>{if(r&&l){l();return}c()},children:[q("box",{width:2,flexShrink:0,overflow:"hidden",children:q("text",{fg:t.primary,children:r?"\u203A":" "},void 0,!1,void 0,this)},void 0,!1,void 0,this),q("box",{width:of,flexShrink:0,overflow:"hidden",children:q("text",{fg:r?t.primary:t.text,wrapMode:"none",children:o},void 0,!1,void 0,this)},void 0,!1,void 0,this),q("box",{width:_I,flexShrink:0,overflow:"hidden",children:q("text",{fg:d,wrapMode:"none",children:i},void 0,!1,void 0,this)},void 0,!1,void 0,this),q("box",{flexGrow:1,overflow:"hidden",children:q("text",{fg:r?t.text:t.muted,wrapMode:"none",children:a},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function EI(e){let{palette:t,snippet:r,active:n,rowH:o,onPick:i,onCopy:s}=e,a=Dc(r.kind,t),c=fb(r.kind,t),l=r.language==="toml"?"Copy TOML":"Copy JSON";return q("box",{height:o,flexDirection:"row",overflow:"hidden",backgroundColor:n?t.highlight:t.panel,onMouseDown:i,children:[q("box",{width:2,flexShrink:0,overflow:"hidden",children:q("text",{fg:a,children:n?"\u203A":" "},void 0,!1,void 0,this)},void 0,!1,void 0,this),q("box",{width:of,flexShrink:0,overflow:"hidden",children:q("text",{fg:a,wrapMode:"none",children:r.title},void 0,!1,void 0,this)},void 0,!1,void 0,this),q("box",{flexGrow:1,overflow:"hidden",children:q("text",{fg:t.muted,wrapMode:"none",children:r.path},void 0,!1,void 0,this)},void 0,!1,void 0,this),q(Be,{palette:t,label:l,bg:a,fg:c,onMouseDown:()=>{i(),s()}},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function TI(e){let{palette:t,tool:r,enabled:n,active:o,rowH:i,onPick:s,onToggle:a}=e;return q("box",{height:i,flexDirection:"row",overflow:"hidden",backgroundColor:o?t.highlight:t.panel,onMouseDown:()=>{if(o){a();return}s()},children:[q("box",{width:2,flexShrink:0,overflow:"hidden",children:q("text",{fg:t.primary,children:o?"\u203A":" "},void 0,!1,void 0,this)},void 0,!1,void 0,this),q("box",{width:5,flexShrink:0,overflow:"hidden",children:q("text",{fg:n?t.success:t.muted,wrapMode:"none",children:n?" ON ":" OFF "},void 0,!1,void 0,this)},void 0,!1,void 0,this),q("box",{width:of+4,flexShrink:0,overflow:"hidden",children:q("text",{fg:o?t.primary:n?t.text:t.muted,wrapMode:"none",children:r.label},void 0,!1,void 0,this)},void 0,!1,void 0,this),q("box",{width:7,flexShrink:0,overflow:"hidden",children:q("text",{fg:r.mutates?t.warning:t.muted,wrapMode:"none",children:r.mutates?"write":"read"},void 0,!1,void 0,this)},void 0,!1,void 0,this),q("box",{flexGrow:1,overflow:"hidden",children:q("text",{fg:o?t.text:t.muted,wrapMode:"none",children:o?`space or enter ${n?"disable":"enable"} ${r.name}`:r.summary},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function AI(e){let{palette:t,snippet:r,selected:n,brand:o}=e;if(!r)return null;let i=r.hint?` \xB7 ${r.hint}`:"";return q("box",{marginTop:1,flexDirection:"column",overflow:"hidden",children:[q("text",{fg:o,wrapMode:"none",children:n?`${r.title} \xB7 paste into ${r.path}${i}`:`Preview ${r.title} \xB7 j/k to a copy row, then enter`},void 0,!1,void 0,this),q("text",{fg:t.text,children:r.text},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}var ef=0,tf=1,gl=2,fw=4,PI,of=14,_I=14;var $s=I(()=>{Mt();ke();tt();fs();PI={inspect:"Inspect",logs:"Logs",diagnostics:"Diagnostics",control:"Control",setup:"Setup"}});import{jsxDEV as Fe,Fragment as vw}from"@opentui/react/jsx-dev-runtime";function NI(e){return[{key:"\u2190\u2192",label:"filter services"},{key:"e",label:"ERROR+ only"},{key:"g",label:"jump to latest"},{key:"p",label:"pause stream"},{key:"z",label:"full-screen logs"},{key:"\\",label:"split panes"},{key:"|",label:"focus other pane"},{key:"/trace",label:"jump to request id"},{key:"f",label:"search"},{key:"t",label:"timestamps"},{key:"m",label:"metadata"},{key:"w",label:"clip / wrap selected / wrap all"},{key:"j/k",label:"move and unfold"},{key:Ei(e),label:"copy selection"},{key:"/export",label:"write ~/.devctl/exports"},{key:"/exports",label:"open export folder"}]}function $n(e){return e+II}function yw(e){let t=$n(af.length),r=$n(cf.length),n=$n(e.logCount),o=$n(lf.length),i=$n(df.length),s=uf+e.pad*2;if(e.stacked)return s+t+r+n+o+i+sf+($I-1)*e.gap;let a=Math.max(t,r),c=Math.max(o,i);return s+a+n+c+sf+(FI-1)*e.gap}function HI(e){return yw(e)-uf-e.pad*2}function jI(e){return e-uf*2<DI}function bw(e){let{palette:t,termW:r,termH:n,copyKey:o=Zr(),scrollRef:i}=e,s=ho(ye()),a=jI(r),c=s?0:1,l=s?0:1,d=NI(o),u={stacked:a,logCount:d.length,pad:c,gap:l},f=yw(u),p=HI(u);return Fe(pt,{palette:t,title:"help",bottomTitle:`${er()} \xB7 j/k scroll \xB7 esc close`,termW:r,termH:n,preferW:LI,preferH:f,gap:0,children:Fe("scrollbox",{ref:i,focused:!1,stickyScroll:!1,scrollX:!1,style:Ue(t),children:Fe("box",{height:p,flexDirection:"column",flexShrink:0,overflow:"hidden",gap:l,children:[a?Fe(vw,{children:[Fe(On,{palette:t,title:"navigation",bindings:af},void 0,!1,void 0,this),Fe(On,{palette:t,title:"services",bindings:cf},void 0,!1,void 0,this),Fe(On,{palette:t,title:"logs",bindings:d},void 0,!1,void 0,this),Fe(On,{palette:t,title:"commands",bindings:lf},void 0,!1,void 0,this),Fe(On,{palette:t,title:"display",bindings:df},void 0,!1,void 0,this)]},void 0,!0,void 0,this):Fe(vw,{children:[Fe(gw,{palette:t,left:{title:"navigation",bindings:af},right:{title:"services",bindings:cf}},void 0,!1,void 0,this),Fe(On,{palette:t,title:"logs",bindings:d},void 0,!1,void 0,this),Fe(gw,{palette:t,left:{title:"commands",bindings:lf},right:{title:"display",bindings:df}},void 0,!1,void 0,this)]},void 0,!0,void 0,this),Fe("box",{height:sf,flexShrink:0,overflow:"hidden",paddingLeft:1,children:Fe("text",{fg:t.muted,wrapMode:"none",children:"~/.devctl/tui.json \xB7 DEVCTL_TUI_CONFIG"},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this)}function gw(e){let{palette:t,left:r,right:n}=e,o=Math.max($n(r.bindings.length),$n(n.bindings.length));return Fe("box",{height:o,flexDirection:"row",flexShrink:0,overflow:"hidden",gap:OI,children:[Fe(On,{palette:t,title:r.title,bindings:r.bindings,height:o,stretch:!0},void 0,!1,void 0,this),Fe(On,{palette:t,title:n.title,bindings:n.bindings,height:o,stretch:!0},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function On(e){let{palette:t,title:r,bindings:n,stretch:o=!1}=e,i=e.height??$n(n.length),s=BI(n);return Fe("box",{height:i,flexGrow:o?1:0,flexBasis:o?0:void 0,flexShrink:0,flexDirection:"column",overflow:"hidden",border:!0,borderStyle:"rounded",borderColor:t.border,title:r,titleColor:t.accent,children:n.map((a)=>Fe("box",{height:1,flexShrink:0,overflow:"hidden",paddingLeft:1,paddingRight:1,children:Fe(UI,{palette:t,binding:a,keyWidth:s},void 0,!1,void 0,this)},`${a.key}-${a.label}`,!1,void 0,this))},void 0,!1,void 0,this)}function BI(e){return e.reduce((r,n)=>Math.max(r,n.key.length),0)+MI}function UI(e){let{palette:t,binding:r,keyWidth:n}=e;return Fe("box",{height:1,flexDirection:"row",overflow:"hidden",children:[Fe("box",{width:n,flexShrink:0,overflow:"hidden",children:Fe("text",{fg:t.primary,wrapMode:"none",children:r.key},void 0,!1,void 0,this)},void 0,!1,void 0,this),Fe("box",{flexGrow:1,overflow:"hidden",children:Fe("text",{fg:t.text,wrapMode:"none",children:r.label},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}var LI=78,DI=56,II=2,uf=2,sf=1,pf=8,MI=2,OI=2,$I=6,FI=4,af,cf,lf,df;var ff=I(()=>{Mt();ke();Er();Rr();af=[{key:"tab",label:"next tab"},{key:"1-4",label:"dashboard services logs proxy"},{key:"s/l/a/p/d/c/u",label:"letter jump"},{key:"/",label:"command overlay \xB7 other screens"},{key:"j/k",label:"move selection"},{key:"esc",label:"back / close overlay"},{key:"esc \xD72",label:"quit"},{key:"q \xD72",label:"quit"}],cf=[{key:"enter",label:"start profile or open"},{key:"space",label:"multi-select"},{key:"n",label:"start"},{key:"x",label:"stop"},{key:"r",label:"refresh"},{key:"R",label:"restart (c cascade if dependents)"}];lf=[{key:It("c"),label:"copy highlighted selection"},{key:"/",label:"command overlay"},{key:It("p"),label:"same overlay"},{key:It("x"),label:"leader chord"},{key:"/settings",label:"preferences \xB7 MCP page"},{key:"/themes",label:"preview themes"},{key:"/diff",label:"config sources"},{key:"/mcp",label:"agent MCP server"}],df=[{key:It("="),label:"larger"},{key:It("-"),label:"smaller"},{key:"?",label:"this overlay"}]});function hi(e){e.setQuery(""),e.setSlashIndex(0),e.setSlashPicker("commands"),e.setOverlay("slash")}function xw(e,t){let{overlay:r,tui:n,height:o,closeOverlay:i,confirmKind:s,onQuit:a,confirmAction:c,planBusy:l,logDetailsScrollRef:d,scrollTextScrollRef:u,routeDetailsScrollRef:f,planScrollRef:p,helpScrollRef:m,revertThemePreview:h,setPaletteIndex:v,setThemeName:b,paletteIndex:g,applyTheme:_,leaderTimer:R,setOverlay:S,runCommand:y,setConfigEditError:x,saveConfigBuffer:w,setSlashIndex:k,filtered:D,setQuery:O,slashIndex:F,submitSlash:Z,advanceWizard:W}=e,C=(t.name??"").toLowerCase();if(r==="none")return!1;if(r==="setup-wizard"){if(C==="escape")return i(),!0;if(C==="return")return W?.(),!0;return Vp(r)}if(r==="confirm"){if(C==="escape")return i(),!0;if(s==="quit"&&C==="d")return a(!0),!0;if(s==="quit"&&C==="k")return e.onDown(!0),!0;if(s==="restart-cascade"&&C==="c")return c("cascade"),!0;if(C==="return")c();return!0}if(r==="log-details"||r==="scroll-text"){if(an(t,n))return hi(e),!0;if(C==="escape")return i(),!0;if(r==="log-details"&&C==="return"){let B=e.logDetail?.request_id?.trim()??"";if(B!=="")e.setLogSearch(B),e.setScreen("logs"),e.closeOverlay(),e.setStatus(`tracing ${B}`);return!0}let j=r==="log-details"?d.current:u.current;if(C==="down"||C==="j")return rt(j,n.scroll_speed),!0;if(C==="up"||C==="k")return rt(j,-n.scroll_speed),!0;if(Ds(t))return rt(j,di(o)),!0;if(Is(t))return rt(j,-di(o)),!0;return!0}if(r==="route-details"){if(an(t,n))return hi(e),!0;if(C==="escape"||C==="return")return i(),!0;if(C==="down"||C==="j")return rt(f.current,n.scroll_speed),!0;if(C==="up"||C==="k")return rt(f.current,-n.scroll_speed),!0;return!0}if(r==="plan"){if(an(t,n))return hi(e),!0;if(C==="escape"||C==="return"&&!l)return i(),!0;if(C==="down"||C==="j")return rt(p.current,n.scroll_speed),!0;if(C==="up"||C==="k")return rt(p.current,-n.scroll_speed),!0;return!0}if(r==="help"){if(an(t,n))return hi(e),!0;if(C==="escape")return i(),!0;if(C==="down"||C==="j")return rt(m.current,1),!0;if(C==="up"||C==="k")return rt(m.current,-1),!0;if(C==="pagedown"||Ds(t))return rt(m.current,pf),!0;if(C==="pageup"||Is(t))return rt(m.current,-pf),!0;return!0}if(r==="themes"){if(an(t,n))return h(),hi(e),!0;if(C==="escape")return h(),i(),!0;if(C==="down"||C==="j")return v((j)=>{let B=Math.min(j+1,St.length-1),z=St[B];if(z)b(z);return B}),!0;if(C==="up"||C==="k")return v((j)=>{let B=Math.max(0,j-1),z=St[B];if(z)b(z);return B}),!0;if(C==="return"){let j=St[g%St.length];if(j)_(j)}return!0}if(r==="leader"){if(an(t,n)){if(R.current)clearTimeout(R.current);return hi(e),!0}if(R.current)clearTimeout(R.current);S("none");let j=gx(C),B=j?Wr(j):void 0;if(B)y(B,[]);return!0}if(r==="config-edit"){if(C==="escape")return x(""),i(),!0;if(Or(t)&&C==="s")return w(),!0;return!0}if(Vp(r)){if(C==="escape")return i(),!0;if(r==="slash"&&C==="down")return k((j)=>Math.min(Math.max(D.length-1,0),j+1)),!0;if(r==="slash"&&C==="up")return k((j)=>Math.max(0,j-1)),!0;if(r==="slash"&&C==="tab"){if(t.shift)return k((B)=>Math.max(0,B-1)),!0;let j=ui(D,F);if(j)O(Rx(j));return!0}if(r==="slash"&&C==="return")return Z(),!0;return!0}return!0}var ww=I(()=>{vr();wo();hl();Rr();ke();ff();tt()});var yl=I(()=>{Zu()});function Sw(e,t){if(t.length===0)return[];let r=new Set(t);return co(e,t).filter((n)=>!r.has(n))}function kw(e,t,r){let n=r!==""&&e.profiles[r]?r:"";if(t.length>0)return{services:t,profile:n};if(n!=="")return{services:[...e.profiles[r]?.services??[]],profile:n};return{services:Object.keys(e.services),profile:""}}function bl(e){return e.waves.flat().join(" \u2192 ")}function Cw(e){let t=bl(e);if(t==="")return"Started selected services";return`Started ${t}`}function Pw(e){let t=bl(e);if(t==="")return"Stopped running services";return`Stopped ${t}`}function _w(e,t,r,n="start"){let o=r?.services[e],i=tr(o);if(o?.last_error&&n!=="stop")return o.last_error;if(n==="stop"){if(i==="STOPPED")return"stopped";if(i==="STOPPING")return"stopping now";if(i==="FAILED")return"already failed";return"queued"}if(i==="HEALTHY")return"ready";if(i==="STARTING")return"starting now";if(i==="RUNNING")return"up, waiting for health";if(i==="UNHEALTHY")return"unhealthy \u2014 retrying health check";if(i==="STOPPING")return"stopping first";if(i==="FAILED")return"failed \u2014 later waves will not start";let a=(t.steps.find((c)=>c.name===e)?.dependencies??[]).filter((c)=>{let l=tr(r?.services[zt(c)]);return gn(c)==="service_healthy"?l!=="HEALTHY":l==="STOPPED"||l==="FAILED"}).map(zt);if(a.length>0)return`waits for ${a.join(", ")}`;return"queued"}function Fs(e,t,r="start"){if(e.length===0)return"completed";let n=e.map((i)=>t?.services[i]);if(n.some((i)=>i?.state==="FAILED"))return"failed";if(r==="stop"){if(n.every((i)=>!i||i.state==="STOPPED"||i.state==="UNKNOWN"))return"completed";if(n.some((i)=>i?.state==="STOPPING"))return"active";return"queued"}let o=(i)=>Boolean(i&&(i.state==="HEALTHY"||i.health==="HEALTHY"||i.state==="RUNNING"&&i.health!=="UNHEALTHY"));if(n.every(o))return"completed";if(n.some((i)=>i?.state==="UNHEALTHY"||i?.health==="UNHEALTHY"))return"unhealthy";if(n.some((i)=>i?.state==="STARTING"||i?.state==="RUNNING"||i?.state==="RESTARTING"))return"active";return"queued"}function Rw(e,t,r="start"){let o=e.waves.flat().length,i=0,s=0,a=0,c=0;for(let m=0;m<e.waves.length;m++){let h=e.waves[m]??[],v=Fs(h,t,r);if(v==="active"||v==="unhealthy"||v==="failed")c=m;else if(v==="completed"&&c===m&&m<e.waves.length-1)c=m+1;for(let b of h){let g=t?.services[b];if(r==="stop"?!g||g.state==="STOPPED"||g.state==="UNKNOWN":Boolean(g&&(g.state==="HEALTHY"||g.health==="HEALTHY"||g.state==="RUNNING"&&g.health!=="UNHEALTHY")))i++;else if(g?.state==="FAILED")s++;else if(g?.state==="STARTING"||g?.state==="RUNNING"||g?.state==="UNHEALTHY"||g?.health==="UNHEALTHY"||g?.state==="STOPPING"||g?.state==="RESTARTING")a++}}let l=o>0?Math.round(i/o*100):100,d=16,u=Math.round(l/100*d),f="\u2588".repeat(u)+"\u2591".repeat(Math.max(0,d-u)),p=i===o&&o>0;return{total:o,ready:i,active:a,failed:s,percent:l,progressBar:f,currentWaveIndex:c,totalWaves:e.waves.length,isComplete:p}}function Ew(e,t,r){let n=r==="completed"?"\u2713 Completed":r==="failed"?"\u2717 Failed":r==="unhealthy"?"\u26A0 Unhealthy":r==="active"?"\u23F3 In Progress":"\u25CB Queued";return`${e==="stop"?t===0?"Wave 1 (Stop First)":`Wave ${t+1}`:t===0?"Wave 1 (Start First)":`Wave ${t+1}`} \xB7 ${n}`}function Tw(e,t,r,n){let o=n?` \xB7 Profile ${n}`:"";if(r)return`${e==="stop"?"Shutdown":"Startup"} Failed (${r})`;if(t)return`${e==="stop"?"Stopping Services":e==="restart"?"Restarting Pipeline":"Starting Pipeline"}${o}`;return`${e==="stop"?"Shutdown Complete":"Startup Complete"}${o}`}function Aw(e,t){if(e)return{primary:"Working\u2026 esc hide this panel (keeps running in background)",secondary:"Services are transitioning. You can dismiss anytime without stopping them."};if(t)return{primary:"enter or esc back to dashboard",secondary:"Execution stopped. Check logs or open Doctor to resolve errors."};return{primary:"enter or esc back to dashboard",secondary:"All waves finished. Click here or press enter to continue."}}var xl=I(()=>{ht();At();Lt()});function Lw(e,t){let r=(t.name??"").toLowerCase();if(e.screen==="mcp"&&e.listCursor===1&&r.length===1&&r>="0"&&r<="9")return e.setMcpPortDraft((n)=>Ty(n,r)),!0;return!1}function Dw(e,t){let r=(t.name??"").toLowerCase(),{screen:n,overlay:o,tui:i,cfg:s,controller:a,profile:c,listCursor:l,names:d,listCount:u,height:f,checked:p,detailName:m,bootErrorMissing:h,bootError:v,logSearchFocused:b,logSources:g,logService:_,logShowTimestamps:R,logShowMeta:S,logWrap:y,showSystemLogs:x,logPinned:w,dashboardLogCursor:k,applyLogCursor:D,applyDashboardLogCursor:O,jumpToLatestLogs:F,toggleSystemLogs:Z,clearLogs:W,setLogService:C,setLogShowTimestamps:j,setLogShowMeta:B,setPaused:z,setErrorOnly:de,setLogWrap:Ee,beginStart:Ne,beginStop:ne,beginRestart:H,applyMcpPortDraft:ae,toggleMcp:Q,toggleMcpTool:nt,copyFocusedMcpSnippet:ft,persistMcpPort:ie,restartMcpOnPort:Le,setMcpPortDraft:Me,settingRows:Ke,persistPrefs:gt,cycleSetting:Xe,toggleMouse:Ct,setDoctorTick:ge,refreshAuth:vt,configScrollRef:at,detailScrollRef:yt,handleEnter:rr,setScreen:Qe,setChecked:Xt,setStatus:qe,setSelected:mt,setProfile:br,refresh:Kt,toggleChecked:xr,createStarterConfig:nr,startWizard:qt,setConfirmKind:or,setConfirmDetail:ir,setOverlay:sr,openConfigBuffer:Vt,runCommand:ce}=e,K=(n==="logs"||n==="dashboard")&&!b;if(K&&(r==="left"||r==="["||n==="logs"&&r==="h")){C(Dp(g,_,-1));return}if(K&&(r==="right"||r==="]"||n==="logs"&&r==="l")){C(Dp(g,_,1));return}if((n==="dashboard"||n==="setup")&&r==="o"){Qe("profiles");return}if((n==="dashboard"||n==="services")&&(r==="*"||t.shift&&r==="8")){Xt([...d]),qe(`Selected ${d.length} services`);return}if((n==="dashboard"||n==="services")&&(r==="-"||r==="_"||r==="minus")){Xt([]),qe("Selection cleared");return}if((n==="dashboard"||n==="services")&&r==="n"){Ne(Bc(p,d[l]??""),c);return}if((n==="dashboard"||n==="services")&&r==="x"){ne(Bc(p,d[l]??""));return}if((n==="dashboard"||n==="services"||n==="detail")&&aw(t)){let A=Bc(p,d[l]??m);if(s&&Sw(s,A).length>0){or("restart-cascade"),ir({services:A}),sr("confirm");return}H(A,c);return}if((n==="dashboard"||n==="services")&&r==="r"){Kt(),qe("Refreshed");return}if(n==="auth"&&r==="r"){vt();return}if(n==="logs"&&r==="t"){let A=!R;j(A),gt({log_timestamps:A},A?"timestamps on":"timestamps off");return}if(n==="logs"&&r==="m"){let A=!S;B(A),gt({log_metadata:A},A?"metadata on":"metadata off");return}if(r==="down"||r==="j"){if(n==="mcp"&&l===1)ae();if(n==="config"){if(u>0)mt((A)=>Math.min(Math.max(u-1,0),A+1));rt(at.current,i.scroll_speed);return}if(n==="detail"){rt(yt.current,i.scroll_speed);return}if(n==="logs"){D(l+1);return}if(n==="dashboard"&&k>=0){O(k+1);return}mt((A)=>Math.min(Math.max(u-1,0),A+1));return}if(r==="up"||r==="k"){if(n==="mcp"&&l===1)ae();if(n==="config"){if(u>0)mt((A)=>Math.max(0,Math.min(A,Math.max(u-1,0))-1));rt(at.current,-i.scroll_speed);return}if(n==="detail"){rt(yt.current,-i.scroll_speed);return}if(n==="logs"){D(l-1);return}if(n==="dashboard"&&k>=0){O(k-1);return}mt((A)=>Math.max(0,Math.min(A,Math.max(u-1,0))-1));return}if(Ds(t)){let A=di(f);if(n==="config"){rt(at.current,A);return}if(n==="detail"){rt(yt.current,A);return}if(n==="logs"){D(l+A);return}if(n==="dashboard"&&(w||k>=0)){O(Math.max(0,k)+A);return}mt((ve)=>Math.min(Math.max(u-1,0),ve+A));return}if(Is(t)){let A=di(f);if(n==="config"){rt(at.current,-A);return}if(n==="detail"){rt(yt.current,-A);return}if(n==="logs"){D(l-A);return}if(n==="dashboard"&&(w||k>=0)){O(Math.max(0,k)-A);return}mt((ve)=>Math.max(0,ve-A));return}if(n==="proxy"&&r==="n"){a?.proxyStart().then(async()=>{qe("Proxy started"),await Kt()});return}if(n==="proxy"&&r==="x"){a?.proxyStop().then(async()=>{qe("Proxy stopped"),await Kt()});return}if(n==="detail"&&m!==""){if(r==="n"){Ne([m],c);return}if(r==="x"){ne([m]);return}if(r==="o"){Qe("config");return}if(r==="l"){C(m),Qe("logs");return}}if(n==="mcp"&&l===1&&(r==="left"||r==="h")){let A=Rn(ae()-1);ie(A),Le(A);return}if(n==="mcp"&&l===1&&(r==="right"||r==="l")){let A=Rn(ae()+1);ie(A),Le(A);return}if(n==="mcp"&&l===1&&(r==="backspace"||r==="delete")){Me((A)=>Ay(A));return}if(n==="settings"&&(r==="left"||r==="h")){Xe(-1,l);return}if(n==="settings"&&(r==="right"||r==="l")){Xe(1,l);return}if(r==="space"){if(n==="profiles"){let J=Object.keys(s?.profiles??{}).sort()[l];if(J)br(J),qe(`Profile ${J}`);return}if(n==="mcp"){if(l===0){Q();return}let ve=Os(l);if(ve){nt(ve);return}ft(l);return}if(n==="logs")return;if(n==="settings"){if(Ln(Ke,l)?.id==="mouse")Ct();return}let A=d[l];if(A)xr(A);return}if(r==="return"){if(n==="setup"&&!a){if(!h){qe(v||"Existing configuration is invalid \u2014 fix it and restart devctl.");return}qt();return}if(n==="setup"&&a){Ne([],c);return}rr();return}if(Mn(t,i,"services","s")&&o==="none"&&n!=="logs"){Qe("services");return}if(Mn(t,i,"logs","l")&&o==="none"&&n!=="detail"&&n!=="logs"&&n!=="settings"){Qe("logs");return}if(Mn(t,i,"auth","a")&&o==="none"){Qe("auth");return}if(Mn(t,i,"proxy","p")&&o==="none"&&n!=="logs"){Qe("proxy");return}if(Mn(t,i,"doctor","d")&&o==="none"&&n!=="doctor"){Qe("doctor");return}if(Mn(t,i,"config","c")&&o==="none"){Qe("config");return}if(Mn(t,i,"setup","u")&&o==="none"){Qe("setup");return}if(n==="config"&&r==="e"){ce(Wr("/edit")??{name:"edit",aliases:[],desc:"",leader:"",group:"ui"},[]);return}if(n==="config"&&r==="v"){Vt();return}if(n==="doctor"&&r==="r"){ge((A)=>A+1),qe("Re-running doctor");return}if(n==="logs"&&r==="p"){z((A)=>!A);return}if((n==="logs"||n==="dashboard")&&r==="g"){F();return}if((n==="logs"||n==="dashboard")&&r==="w"){let A=Qc(y);Ee(A),qe(`Log ${el(A)}`);return}if((n==="logs"||n==="dashboard")&&r==="e"){de((A)=>!A);return}if((n==="logs"||n==="dashboard")&&r==="i"){Z(),qe(x?"Hiding internal auth/mcp/devctl/proxy logs":"Showing internal logs");return}if((n==="logs"||n==="dashboard")&&cw(t))W()}var Iw=I(()=>{yl();vr();gr();xl();Lt();hl();ke();$s();Er()});import{useKeyboard as WI}from"@opentui/react";import{useCallback as mf}from"react";function Mw({tui:e,controller:t,cfg:r,snap:n,ui:o,logView:i,lifecycleActions:s,mcp:a,preferences:c,diagnostics:l,refs:d}){let{screen:u,onQuit:f,closeOverlay:p,confirmKind:m,confirmDetail:h,portTarget:v,profile:b,listCursor:g,names:_,runCommand:R,copySelection:S,setOverlay:y,setConfirmKind:x,setConfirmDetail:w,setPortTarget:k,setLogDetail:D,setProfile:O,setStatus:F,setSlashIndex:Z,setQuery:W,setSlashPicker:C,setScreen:j,freePort:B,openDetail:z}=o,{logSlice:de,logSliceB:Ee,logSearchFocused:Ne,logsFullscreen:ne,setLogSearchFocused:H,setLogsFullscreen:ae,splitLogs:Q,splitFocus:nt,toggleSplitLogs:ft,cycleSplitFocus:ie,setActiveLogService:Le,logService:Me,logServiceB:Ke}=i,{beginStart:gt,beginRestart:Xe,planBusy:Ct}=s,{applyMcpPortDraft:ge,toggleMcp:vt,toggleMcpTool:at,copyFocusedMcpSnippet:yt}=a,{settingRows:rr,activateSetting:Qe,applyFont:Xt,applyReset:qe,fontSize:mt,revertThemePreview:br,setThemeName:Kt,leaderMs:xr}=c,{doctor:nr}=l,{interruptArmedAt:qt,leaderTimer:or}=d,ir=mf(()=>{if(r?.shutdown.stop_services_on_exit===void 0){x("quit"),y("confirm");return}f(r.shutdown.stop_services_on_exit===!1)},[r,f,x,y]),sr=mf((ce)=>{if(m==="quit"){f(!1);return}if(m==="reload"){p();let K=n?.restart_required??[];if(K.length>0)Xe(K,b);return}if(m==="restart-cascade"){let K=h?.services??[];p(),Xe(K,b,ce==="cascade");return}if(m==="reset-prefs"){p(),qe();return}if(m==="free-port"){let K=v;if(p(),!K)return;B(K).then(()=>{F(`Stopped ${K.command} (pid ${K.pid}) on port ${K.port}`),l.setDoctorTick((A)=>A+1)}).catch((A)=>{F(L(A))});return}p(),gt([],b)},[qe,Xe,gt,p,h,m,l,B,f,v,b,F,n]),Vt=mf(()=>{if(u==="logs"){let K=(Q&&nt===1?Ee:de)[g];if(K)D(K),y("log-details");return}if(u==="profiles"){let A=Object.keys(r?.profiles??{}).sort()[g];if(A)O(A),x("start-profile"),y("confirm");return}if(u==="doctor"){let K=nr?.checks[g];if(K?.action?.kind==="free-port"){let A=K.action.holder;k(A),w({port:A.port,pid:A.pid,process:A.command}),x("free-port"),y("confirm")}return}if(u==="mcp"){if(g===0){vt();return}if(g===1){ge();return}let K=Os(g);if(K){at(K);return}yt(g);return}if(u==="settings"){let K=Ln(rr,g);if(K)Qe(K);return}if(u==="config"){let K=Object.keys(r?.tasks??{}).sort()[g],A=Wr("run");if(K&&A)R(A,[K]);return}if(u==="dashboard"&&jc(n)){gt([],b);return}let ce=_[g];if(ce&&(u==="dashboard"||u==="services"))z(ce)},[Qe,ge,gt,r,yt,nr,g,de,Ee,_,z,b,R,u,w,x,D,y,k,O,rr,n,nt,Q,vt,at]);WI((ce)=>{let K=(ce.name??"").toLowerCase();if(lw(ce,e)){S();return}if(Or(ce)&&(K==="="||K==="+"||K==="plus")){Xt(xs(mt,1));return}if(Or(ce)&&(K==="-"||K==="_"||K==="minus")){Xt(xs(mt,-1));return}if(Or(ce)&&K==="0"){Xt(Mc().font_size);return}if(u==="logs"&&!Ne&&(ce.sequence==="\\"||K==="\\")){ft(),F(Q?"Single log pane":"Split log panes");return}if(u==="logs"&&!Ne&&(ce.sequence==="|"||K==="|")){ie();return}if(xw({...o,tui:e,confirmAction:sr,planBusy:Ct,leaderTimer:or,logDetailsScrollRef:d.logDetailsScrollRef,scrollTextScrollRef:d.scrollTextScrollRef,routeDetailsScrollRef:d.routeDetailsScrollRef,planScrollRef:d.planScrollRef,helpScrollRef:d.helpScrollRef,revertThemePreview:br,setThemeName:Kt},ce))return;if(Ne){if(K==="escape")H(!1);return}if(rw(ce,e)){y("leader"),or.current=setTimeout(()=>y("none"),xr);return}if(nw(ce,e)||an(ce,e)){W(""),Z(0),C("commands"),y("slash");return}if(ow(ce,e)){y("help");return}if(iw(ce,e)||u==="logs"&&K==="f"&&!ce.ctrl&&!ce.meta){j("logs"),H(!0);return}if(K==="escape"||sw(ce)){if(K==="escape"){if(ne){ae(!1);return}if(u==="detail"){j("services");return}if(u==="setup"&&t){j("dashboard");return}}let J=Date.now();if(dw(J,qt.current)){qt.current=0,ir();return}qt.current=J,F(K==="q"?"q again to quit":"Esc again to quit");return}if(K==="z"&&(u==="logs"||u==="dashboard")&&!ce.ctrl&&!ce.meta){j("logs"),ae((J)=>u==="logs"?!J:!0);return}if(K==="tab"){j(ce.shift?Cx(u):kx(u));return}let A={...o,tui:e,controller:t,cfg:r,snap:n,...i,...s,...a,...c,logService:Q&&nt===1?Ke:Me,setLogService:Le,setDoctorTick:l.setDoctorTick,refreshAuth:l.refreshAuth,configScrollRef:d.configScrollRef,detailScrollRef:d.detailScrollRef,handleEnter:Vt};if(Lw(A,ce))return;let ve=tx(K);if(ve){j(ve);return}Dw(A,ce)})}var Ow=I(()=>{te();wo();In();Lt();hl();$s();Er();Rr();ww();Iw()});import{parse as GI,stringify as KI}from"yaml";function wl(e,t="imported"){let r=GI(e);if(!r||typeof r!=="object"||Array.isArray(r))throw Error("compose file is not a mapping");let n=r,o=gi(n.services)?n.services:{},i=[];if(n.networks!==void 0)i.push({service:"(root)",field:"networks",reason:$w.networks??"not modeled"});let s={},a=[];for(let[l,d]of Object.entries(o)){if(!gi(d))continue;let u=qI(l,d,i);if(s[l]=u.body,!u.container)a.push(l)}return{yaml:KI({version:1,project:{name:t},services:s,profiles:{default:{services:a}}}),dropped:i,services:Object.keys(s),profileServices:a}}function Sl(e){let t=[e.yaml.trimEnd(),"","Dropped fields:"];if(e.dropped.length===0)t.push("(none)");else{t.push("service\tfield\treason");for(let r of e.dropped)t.push(`${r.service} ${r.field} ${r.reason}`)}if(e.profileServices.length<e.services.length)t.push(""),t.push("Container services were omitted from profile default so first start does not require Docker.");return`${t.join(`
2146
2218
  `)}
2147
- `}function jI(e,t,r){for(let f of Object.keys(t)){let p=Aw[f];if(p)r.push({service:e,field:f,reason:p})}let n={},o=Lw(t.command);if(o)n.command=o;let i=typeof t.working_dir==="string"?t.working_dir:typeof t.workingDir==="string"?t.workingDir:"";if(i!=="")n.working_dir=i;let s=BI(t.depends_on);if(s.length>0)n.dependencies=s;let a=UI(t.environment);if(Object.keys(a).length>0)n.environment={defaults:a};let c=WI(t.ports);if(c.host.length>0)n.ports=c.host;let l=KI(t.healthcheck);if(l)n.health=l;let d=typeof t.image==="string"?t.image:"",u=d!=="";if(u)n.container={image:d,...c.container&&Object.keys(c.container).length>0?{ports:c.container}:{},...Object.keys(a).length>0?{env:a}:{}};return{body:n,container:u}}function Lw(e){if(typeof e==="string"&&e.trim()!=="")return["sh","-c",e];if(Array.isArray(e)&&e.every((t)=>typeof t==="string"))return e;return}function BI(e){if(Array.isArray(e))return e.filter((t)=>typeof t==="string");if(hi(e))return Object.keys(e);return[]}function UI(e){let t={};if(Array.isArray(e)){for(let r of e){if(typeof r!=="string")continue;let n=r.indexOf("=");if(n===-1)t[r]="";else t[r.slice(0,n)]=r.slice(n+1)}return t}if(hi(e))for(let[r,n]of Object.entries(e))t[r]=n===void 0||n===null?"":String(n);return t}function WI(e){let t=[],r={};if(!Array.isArray(e))return{host:t,container:r};let n=0;for(let o of e){let i=GI(o);if(!i)continue;n+=1;let s=n===1?"http":`port${n}`;t.push({name:s,value:i.host}),r[s]=i.container}return{host:t,container:r}}function GI(e){if(typeof e==="number"&&Number.isFinite(e))return{host:e,container:e};if(typeof e==="string"){let r=e.replace(/\/(tcp|udp)$/i,"").split(":"),n=Number(r[r.length-1]),o=r.length>1?Number(r[r.length-2]):n;if(!Number.isFinite(n)||!Number.isFinite(o))return;return{host:o,container:n}}if(hi(e)){let t=Number(e.published??e.target),r=Number(e.target??e.published);if(!Number.isFinite(t)||!Number.isFinite(r))return;return{host:t,container:r}}return}function KI(e){if(!hi(e)||e.disable===!0)return;let t=Lw(e.test);if(!t||t[0]==="CMD"&&t.length===1||t[0]==="NONE")return;let r=t[0]==="CMD"||t[0]==="CMD-SHELL"?t.slice(1):t;if(r.length===0)return;return{type:"command",command:r}}function hi(e){return Boolean(e)&&typeof e==="object"&&!Array.isArray(e)}var Aw;var ff=D(()=>{Aw={build:"not modeled; use a local command or pre-built image",networks:"not modeled",deploy:"not modeled",replicas:"not modeled",env_file:"per-service env_file is dropped; set environment.sources at the project level",volumes:"not modeled on import",labels:"not modeled",restart:"use service.restart in authoring",expose:"use ports",privileged:"not modeled",user:"not modeled",entrypoint:"not modeled; command is mapped when present",hostname:"not modeled",extra_hosts:"not modeled",cap_add:"not modeled",cap_drop:"not modeled",mem_limit:"not modeled",cpus:"not modeled",scale:"not modeled"}});function Dw(e){let t=Object.keys(e.services).length,r=Object.keys(e.profiles).length,n=e.proxy.routes.length,o=Object.keys(e.tasks).length,i=[{text:e.project.name||"unnamed",tone:"primary"},{text:`schema ${e.version}`,tone:"muted"},{text:`${t} service${t===1?"":"s"}`,tone:t>0?"info":"idle"},{text:`${r} profile${r===1?"":"s"}`,tone:r>0?"info":"idle"},{text:e.proxy.enabled?"proxy on":"proxy off",tone:e.proxy.enabled?"success":"idle"}];if(n>0)i.push({text:`${n} route${n===1?"":"s"}`,tone:"info"});if(o>0)i.push({text:`${o} task${o===1?"":"s"}`,tone:"info"});return i}function Iw(e,t){return[{label:"path",value:e.configPath||"\u2014"},{label:"repo",value:e.repoRoot||"\u2014"},{label:"devctl",value:t},{label:"schema",value:String(e.version)}]}function Mw(e){return[{label:"project",value:e.google.project_id||"(unset)",tone:e.google.project_id?"text":"muted"},{label:"region",value:e.google.region||"(unset)",tone:e.google.region?"text":"muted"}]}function Ow(e){let t=qI(e);return[{label:"exit",value:t,tone:t==="ask on quit"?"warning":"text"},{label:"grace",value:`${e.shutdown.grace_seconds}s`},{label:"refresh",value:`${gh(e.auth)}s`},{label:"env",value:e.environment.sources.join(", ")||"\u2014",tone:e.environment.sources.length>0?"text":"muted"},{label:"theme",value:e.ui.theme||"system"}]}function $w(e){let t=e.logs.persistence;return[{label:"memory",value:`${e.logs.max_memory_events} events`},{label:"persist",value:t.enabled?t.directory:"off",tone:t.enabled?"text":"muted"},{label:"keep",value:t.enabled?`${t.retention_days}d \xB7 ${t.max_session_logs||"\u221E"} sessions`:"\u2014"}]}function Fw(e){return[{label:"listen",value:xn(e.proxy.listen),tone:e.proxy.enabled?"text":"muted"},{label:"token",value:YI(e),tone:e.proxy.token_endpoint.enabled?"text":"muted"}]}function Nw(e){return Object.keys(e.services).sort().map((t)=>{let r=e.services[t]??vn();return{name:t,extends:r.extends||"\u2014",command:Bc(r),ports:Rs(r),identity:Wc(r),health:Uc(r),depends:r.dependencies.map(Pa).join(", ")||"\u2014",restart:Gc(r)}})}function Hw(e){return e.proxy.routes.map((t)=>({name:t.name||t.match.host||"(unnamed)",match:JI(t),upstream:t.upstream.url||"\u2014",auth:t.auth.type||"none",identity:XI(t)}))}function jw(e){return Object.keys(e.profiles).sort().map((t)=>{let r=e.profiles[t],n=Object.keys(r?.environment??{}).sort();return{name:t,services:r?.services.join(", ")||"\u2014",env:n.length>0?n.join(", "):"\u2014"}})}function Bw(e){return Object.keys(e.templates).sort().map((t)=>({name:t,summary:QI(e.templates[t])}))}function Uw(e){return Object.keys(e.tasks).sort().map((t)=>{let r=e.tasks[t],n=r?.command.args.join(" ")||"\u2014",o=r?.dependencies.join(", ");return{name:t,summary:o?`${n} depends ${o}`:n}})}function Ww(e,t){let r=Pn(e);if(r.length===0)return"No provenance recorded for this configuration.";let n=new mr(e.secrets.extra_markers,e.secrets.extra_patterns);return r.map((o)=>{let i=typeof o.value==="string"?o.value:JSON.stringify(o.value),s=t?i:n.redactMap({[o.path]:i})[o.path],a=o.shadowed.length===0?"":`
2219
+ `}function qI(e,t,r){for(let f of Object.keys(t)){let p=$w[f];if(p)r.push({service:e,field:f,reason:p})}let n={},o=Fw(t.command);if(o)n.command=o;let i=typeof t.working_dir==="string"?t.working_dir:typeof t.workingDir==="string"?t.workingDir:"";if(i!=="")n.working_dir=i;let s=VI(t.depends_on);if(s.length>0)n.dependencies=s;let a=zI(t.environment);if(Object.keys(a).length>0)n.environment={defaults:a};let c=YI(t.ports);if(c.host.length>0)n.ports=c.host;let l=XI(t.healthcheck);if(l)n.health=l;let d=typeof t.image==="string"?t.image:"",u=d!=="";if(u)n.container={image:d,...c.container&&Object.keys(c.container).length>0?{ports:c.container}:{},...Object.keys(a).length>0?{env:a}:{}};return{body:n,container:u}}function Fw(e){if(typeof e==="string"&&e.trim()!=="")return["sh","-c",e];if(Array.isArray(e)&&e.every((t)=>typeof t==="string"))return e;return}function VI(e){if(Array.isArray(e))return e.filter((t)=>typeof t==="string");if(gi(e))return Object.keys(e);return[]}function zI(e){let t={};if(Array.isArray(e)){for(let r of e){if(typeof r!=="string")continue;let n=r.indexOf("=");if(n===-1)t[r]="";else t[r.slice(0,n)]=r.slice(n+1)}return t}if(gi(e))for(let[r,n]of Object.entries(e))t[r]=n===void 0||n===null?"":String(n);return t}function YI(e){let t=[],r={};if(!Array.isArray(e))return{host:t,container:r};let n=0;for(let o of e){let i=JI(o);if(!i)continue;n+=1;let s=n===1?"http":`port${n}`;t.push({name:s,value:i.host}),r[s]=i.container}return{host:t,container:r}}function JI(e){if(typeof e==="number"&&Number.isFinite(e))return{host:e,container:e};if(typeof e==="string"){let r=e.replace(/\/(tcp|udp)$/i,"").split(":"),n=Number(r[r.length-1]),o=r.length>1?Number(r[r.length-2]):n;if(!Number.isFinite(n)||!Number.isFinite(o))return;return{host:o,container:n}}if(gi(e)){let t=Number(e.published??e.target),r=Number(e.target??e.published);if(!Number.isFinite(t)||!Number.isFinite(r))return;return{host:t,container:r}}return}function XI(e){if(!gi(e)||e.disable===!0)return;let t=Fw(e.test);if(!t||t[0]==="CMD"&&t.length===1||t[0]==="NONE")return;let r=t[0]==="CMD"||t[0]==="CMD-SHELL"?t.slice(1):t;if(r.length===0)return;return{type:"command",command:r}}function gi(e){return Boolean(e)&&typeof e==="object"&&!Array.isArray(e)}var $w;var hf=I(()=>{$w={build:"not modeled; use a local command or pre-built image",networks:"not modeled",deploy:"not modeled",replicas:"not modeled",env_file:"per-service env_file is dropped; set environment.sources at the project level",volumes:"not modeled on import",labels:"not modeled",restart:"use service.restart in authoring",expose:"use ports",privileged:"not modeled",user:"not modeled",entrypoint:"not modeled; command is mapped when present",hostname:"not modeled",extra_hosts:"not modeled",cap_add:"not modeled",cap_drop:"not modeled",mem_limit:"not modeled",cpus:"not modeled",scale:"not modeled"}});function Nw(e){let t=Object.keys(e.services).length,r=Object.keys(e.profiles).length,n=e.proxy.routes.length,o=Object.keys(e.tasks).length,i=[{text:e.project.name||"unnamed",tone:"primary"},{text:`schema ${e.version}`,tone:"muted"},{text:`${t} service${t===1?"":"s"}`,tone:t>0?"info":"idle"},{text:`${r} profile${r===1?"":"s"}`,tone:r>0?"info":"idle"},{text:e.proxy.enabled?"proxy on":"proxy off",tone:e.proxy.enabled?"success":"idle"}];if(n>0)i.push({text:`${n} route${n===1?"":"s"}`,tone:"info"});if(o>0)i.push({text:`${o} task${o===1?"":"s"}`,tone:"info"});return i}function Hw(e,t){return[{label:"path",value:e.configPath||"\u2014"},{label:"repo",value:e.repoRoot||"\u2014"},{label:"devctl",value:t},{label:"schema",value:String(e.version)}]}function jw(e){return[{label:"project",value:e.google.project_id||"(unset)",tone:e.google.project_id?"text":"muted"},{label:"region",value:e.google.region||"(unset)",tone:e.google.region?"text":"muted"}]}function Bw(e){let t=QI(e);return[{label:"exit",value:t,tone:t==="ask on quit"?"warning":"text"},{label:"grace",value:`${e.shutdown.grace_seconds}s`},{label:"refresh",value:`${bh(e.auth)}s`},{label:"env",value:e.environment.sources.join(", ")||"\u2014",tone:e.environment.sources.length>0?"text":"muted"},{label:"theme",value:e.ui.theme||"system"}]}function Uw(e){let t=e.logs.persistence;return[{label:"memory",value:`${e.logs.max_memory_events} events`},{label:"persist",value:t.enabled?t.directory:"off",tone:t.enabled?"text":"muted"},{label:"keep",value:t.enabled?`${t.retention_days}d \xB7 ${t.max_session_logs||"\u221E"} sessions`:"\u2014"}]}function Ww(e){return[{label:"listen",value:bn(e.proxy.listen),tone:e.proxy.enabled?"text":"muted"},{label:"token",value:tM(e),tone:e.proxy.token_endpoint.enabled?"text":"muted"}]}function Gw(e){return Object.keys(e.services).sort().map((t)=>{let r=e.services[t]??vn();return{name:t,extends:r.extends||"\u2014",command:Uc(r),ports:Es(r),identity:Gc(r),health:Wc(r),depends:r.dependencies.map(Ra).join(", ")||"\u2014",restart:Kc(r)}})}function Kw(e){return e.proxy.routes.map((t)=>({name:t.name||t.match.host||"(unnamed)",match:rM(t),upstream:t.upstream.url||"\u2014",auth:t.auth.type||"none",identity:nM(t)}))}function qw(e){return Object.keys(e.profiles).sort().map((t)=>{let r=e.profiles[t],n=Object.keys(r?.environment??{}).sort();return{name:t,services:r?.services.join(", ")||"\u2014",env:n.length>0?n.join(", "):"\u2014"}})}function Vw(e){return Object.keys(e.templates).sort().map((t)=>({name:t,summary:oM(e.templates[t])}))}function zw(e){return Object.keys(e.tasks).sort().map((t)=>{let r=e.tasks[t],n=r?.command.args.join(" ")||"\u2014",o=r?.dependencies.join(", ");return{name:t,summary:o?`${n} depends ${o}`:n}})}function Yw(e,t){let r=Pn(e);if(r.length===0)return"No provenance recorded for this configuration.";let n=new mr(e.secrets.extra_markers,e.secrets.extra_patterns);return r.map((o)=>{let i=typeof o.value==="string"?o.value:JSON.stringify(o.value),s=t?i:n.redactMap({[o.path]:i})[o.path],a=o.shadowed.length===0?"":`
2148
2220
  shadowed ${o.shadowed.map((c)=>`${c.layer} \xB7 ${c.source}`).join("; ")}`;return`${o.path}
2149
2221
  ${o.layer} \xB7 ${o.source}
2150
2222
  ${s??""}${a}`}).join(`
2151
2223
 
2152
- `)}function Gw(e){let t=e.doctor.tools.map((i)=>i.name||i.command).filter((i)=>i!==""),r=e.plugins.map((i)=>i.path).filter((i)=>i!==""),n=Object.keys(e.ui.keymap),o=[...e.secrets.extra_markers,...e.secrets.extra_patterns];return[{label:"doctor",value:t.join(", ")||"\u2014",tone:t.length>0?"text":"muted"},{label:"plugins",value:r.join(", ")||"\u2014",tone:r.length>0?"text":"muted"},{label:"keymap",value:n.length>0?`${n.length} override${n.length===1?"":"s"}`:"defaults",tone:n.length>0?"text":"muted"},{label:"secrets",value:o.length>0?o.join(", "):"defaults",tone:o.length>0?"text":"muted"}]}function qI(e){if(e.shutdown.stop_services_on_exit===void 0)return"ask on quit";return e.shutdown.stop_services_on_exit?"stop services":"detach"}function Kw(e){let t=e.reduce((r,n)=>Math.max(r,n.name.length),0);return Math.min(zI,Math.max(VI,t))}function YI(e){let t=e.proxy.token_endpoint;if(!t.enabled)return"off";let r=t.host===""?"127.0.0.1":t.host;if(t.port===0)return r;return`${r}:${t.port}`}function JI(e){let t=e.match.host||"*",r=e.match.path;if(r==="")return t;return`${t}${r.startsWith("/")?r:`/${r}`}`}function XI(e){if(e.auth.identity.service_account!=="")return e.auth.identity.service_account;if(e.auth.service_account!=="")return e.auth.service_account;return e.auth.identity.type||"user"}function QI(e){if(!e)return"empty";let t=[];if(e.health.type!=="")t.push(`health ${e.health.type}`);if(e.restart.policy!=="")t.push(`restart ${e.restart.policy}`);if(e.logs.stdout||e.logs.stderr)t.push("logs");return t.join(" \xB7 ")||"empty"}var VI=8,zI=20,qw=80,gi=10;var mf=D(()=>{ht();ni();At()});async function Sl(e,t){e.suspend();try{return await t()}finally{e.resume(),e.intermediateRender()}}import{useCallback as ZI,useRef as eM}from"react";function Vw({setOverlay:e,setQuery:t,setSlashPicker:r,checked:n,setConfirmKind:o,setStatus:i,persistTheme:s,setPaletteIndex:a,themeName:c,setScreen:l,setSelected:d,screen:u,renderer:f,cfg:p,setScrollText:m,reveal:h,controller:v,refresh:x,profile:g,setReveal:R,resolveEnvironment:_,openDetail:S,copySelection:y,lastExportPath:b,openConfigBuffer:w,onDown:k,workspace:L,logView:O,diagnostics:F,lifecycleActions:Z}){let{loginGoogle:W,detectGoogle:C,logoutGoogle:j,bootstrapLogPath:B,fileExists:z,readTextFile:de,writeTextFile:Ee,validateConfigText:Ne,resolveExportPath:ne,writeLogExport:H,exportsDir:ae,openInFileManager:X,listSessions:nt,loadSessionEvents:ft,checkUpdate:ie,applyUpdate:Le,formatUpdateStatus:Me}=L,{setLogService:Ke,setLogsFullscreen:gt,setPaused:Xe,setErrorOnly:Ct,toggleSystemLogs:ge,clearLogs:vt,logWrap:at,setLogWrap:yt,logServices:rr,errorOnly:Qe,logLevel:Xt,logSearch:qe,logRegex:mt,logSource:xr,filteredLogs:Kt,setLogRegex:br,setLogSince:nr,setLogUntil:qt,setLogs:or,setLogSearch:ir,toggleSplitLogs:sr}=O,{refreshAuth:Vt,setGoogle:ce}=F,{beginStart:K,beginStop:T,beginRestart:ve}=Z,Y=eM(!1);return{runCommand:ZI(async(ct,Ce)=>{if(Y.current)return;if(Y.current=!0,ct.name!=="start"&&ct.name!=="stop"&&ct.name!=="restart")e("none");t("");let Ro=Pp(Ce,n);try{switch(ct.name){case"exit":o("quit"),e("confirm");return;case"down":{let P=Ce.includes("--keep-services")||Ce.includes("-k");k(P);return}case"help":e("help");return;case"version":i(er()),ie().then((P)=>i(Me(P))).catch((P)=>i(A(P)));return;case"update":i("checking for update\u2026"),ie().then(async(P)=>{if(!P.newer||!P.command){i(Me(P));return}i(`installing ${P.current} \u2192 ${P.latest} via ${P.kind}\u2026`);let ue=await Le(P.command);if(ue.code!==0){i(`update failed (${ue.code}): ${(ue.stderr||ue.stdout).trim()||P.hint}`);return}i(`updated to ${P.latest}; ${pc}`)}).catch((P)=>i(A(P)));return;case"themes":if(Ce[0]){s(Ce[0]);return}a(Math.max(0,St.indexOf(c))),t(""),e("themes");return;case"settings":l("settings"),d(0);return;case"dashboard":case"services":l(ct.name);return;case"logs":if(Ce[0])Ke(Ce[0]);l("logs");return;case"fullscreen":l("logs"),gt((P)=>u==="logs"?!P:!0);return;case"split":l("logs"),sr(),i("Split log panes");return;case"trace":{let P=Ce.join(" ").trim();if(P===""){i("usage: /trace <request-or-trace-id>");return}ir(P),l("logs"),i(`tracing ${P}`);return}case"auth":{let P=(Ce[0]??"").toLowerCase();if(P==="refresh"){l("auth"),await Vt();return}if(P==="login"){l("auth"),i("starting gcloud ADC login\u2026");try{await Sl(f,()=>W()),ce(await C(p?.google.project_id??"")),await Vt(),i("ADC login complete")}catch(ue){i(A(ue))}return}if(P==="logout"){l("auth"),i("revoking ADC\u2026");try{await j(),ce(await C(p?.google.project_id??"")),await Vt(),i("ADC revoked")}catch(ue){i(A(ue))}return}l("auth");return}case"credentials":case"proxy":case"mcp":case"config":case"profiles":case"setup":l(ct.name);return;case"import":{let P=Ce[0]??"",ue=Ce.includes("--write"),Yr=Ce.find((se)=>se!=="compose"&&se!=="--write")??"compose.yaml";if(P!=="compose"){i("usage: /import compose [path] [--write]");return}if(!z(Yr)){i(`compose file not found: ${Yr}`);return}let Jr=p?.repoRoot||process.cwd(),wr=p?.configPath||`${Jr}/.devctl/config.yaml`;try{let se=bl(de(Yr),Jr.split("/").pop()??"imported"),Bn=Ne(Jr,wr,se.yaml),Un=`${wl(se)}${Bn.length>0?`
2224
+ `)}function Jw(e){let t=e.doctor.tools.map((i)=>i.name||i.command).filter((i)=>i!==""),r=e.plugins.map((i)=>i.path).filter((i)=>i!==""),n=Object.keys(e.ui.keymap),o=[...e.secrets.extra_markers,...e.secrets.extra_patterns];return[{label:"doctor",value:t.join(", ")||"\u2014",tone:t.length>0?"text":"muted"},{label:"plugins",value:r.join(", ")||"\u2014",tone:r.length>0?"text":"muted"},{label:"keymap",value:n.length>0?`${n.length} override${n.length===1?"":"s"}`:"defaults",tone:n.length>0?"text":"muted"},{label:"secrets",value:o.length>0?o.join(", "):"defaults",tone:o.length>0?"text":"muted"}]}function QI(e){if(e.shutdown.stop_services_on_exit===void 0)return"ask on quit";return e.shutdown.stop_services_on_exit?"stop services":"detach"}function Xw(e){let t=e.reduce((r,n)=>Math.max(r,n.name.length),0);return Math.min(eM,Math.max(ZI,t))}function tM(e){let t=e.proxy.token_endpoint;if(!t.enabled)return"off";let r=t.host===""?"127.0.0.1":t.host;if(t.port===0)return r;return`${r}:${t.port}`}function rM(e){let t=e.match.host||"*",r=e.match.path;if(r==="")return t;return`${t}${r.startsWith("/")?r:`/${r}`}`}function nM(e){if(e.auth.identity.service_account!=="")return e.auth.identity.service_account;if(e.auth.service_account!=="")return e.auth.service_account;return e.auth.identity.type||"user"}function oM(e){if(!e)return"empty";let t=[];if(e.health.type!=="")t.push(`health ${e.health.type}`);if(e.restart.policy!=="")t.push(`restart ${e.restart.policy}`);if(e.logs.stdout||e.logs.stderr)t.push("logs");return t.join(" \xB7 ")||"empty"}var ZI=8,eM=20,Qw=80,vi=10;var gf=I(()=>{ht();ni();Lt()});async function kl(e,t){e.suspend();try{return await t()}finally{e.resume(),e.intermediateRender()}}import{useCallback as iM,useRef as sM}from"react";function Zw({setOverlay:e,setQuery:t,setSlashPicker:r,checked:n,setConfirmKind:o,setStatus:i,persistTheme:s,setPaletteIndex:a,themeName:c,setScreen:l,setSelected:d,screen:u,renderer:f,cfg:p,setScrollText:m,reveal:h,controller:v,refresh:b,profile:g,setReveal:_,resolveEnvironment:R,openDetail:S,copySelection:y,lastExportPath:x,openConfigBuffer:w,onDown:k,workspace:D,logView:O,diagnostics:F,lifecycleActions:Z}){let{loginGoogle:W,detectGoogle:C,logoutGoogle:j,bootstrapLogPath:B,fileExists:z,readTextFile:de,writeTextFile:Ee,validateConfigText:Ne,resolveExportPath:ne,writeLogExport:H,exportsDir:ae,openInFileManager:Q,listSessions:nt,loadSessionEvents:ft,checkUpdate:ie,applyUpdate:Le,formatUpdateStatus:Me}=D,{setLogService:Ke,setLogsFullscreen:gt,setPaused:Xe,setErrorOnly:Ct,toggleSystemLogs:ge,clearLogs:vt,logWrap:at,setLogWrap:yt,logServices:rr,errorOnly:Qe,logLevel:Xt,logSearch:qe,logRegex:mt,logSource:br,filteredLogs:Kt,setLogRegex:xr,setLogSince:nr,setLogUntil:qt,setLogs:or,setLogSearch:ir,toggleSplitLogs:sr}=O,{refreshAuth:Vt,setGoogle:ce}=F,{beginStart:K,beginStop:A,beginRestart:ve}=Z,J=sM(!1);return{runCommand:iM(async(ct,Ce)=>{if(J.current)return;if(J.current=!0,ct.name!=="start"&&ct.name!=="stop"&&ct.name!=="restart")e("none");t("");let Po=Rp(Ce,n);try{switch(ct.name){case"exit":o("quit"),e("confirm");return;case"down":{let P=Ce.includes("--keep-services")||Ce.includes("-k");k(P);return}case"help":e("help");return;case"version":i(er()),ie().then((P)=>i(Me(P))).catch((P)=>i(L(P)));return;case"update":i("checking for update\u2026"),ie().then(async(P)=>{if(!P.newer||!P.command){i(Me(P));return}i(`installing ${P.current} \u2192 ${P.latest} via ${P.kind}\u2026`);let ue=await Le(P.command);if(ue.code!==0){i(`update failed (${ue.code}): ${(ue.stderr||ue.stdout).trim()||P.hint}`);return}i(`updated to ${P.latest}; ${mc}`)}).catch((P)=>i(L(P)));return;case"themes":if(Ce[0]){s(Ce[0]);return}a(Math.max(0,St.indexOf(c))),t(""),e("themes");return;case"settings":l("settings"),d(0);return;case"dashboard":case"services":l(ct.name);return;case"logs":if(Ce[0])Ke(Ce[0]);l("logs");return;case"fullscreen":l("logs"),gt((P)=>u==="logs"?!P:!0);return;case"split":l("logs"),sr(),i("Split log panes");return;case"trace":{let P=Ce.join(" ").trim();if(P===""){i("usage: /trace <request-or-trace-id>");return}ir(P),l("logs"),i(`tracing ${P}`);return}case"auth":{let P=(Ce[0]??"").toLowerCase();if(P==="refresh"){l("auth"),await Vt();return}if(P==="login"){l("auth"),i("starting gcloud ADC login\u2026");try{await kl(f,()=>W()),ce(await C(p?.google.project_id??"")),await Vt(),i("ADC login complete")}catch(ue){i(L(ue))}return}if(P==="logout"){l("auth"),i("revoking ADC\u2026");try{await j(),ce(await C(p?.google.project_id??"")),await Vt(),i("ADC revoked")}catch(ue){i(L(ue))}return}l("auth");return}case"credentials":case"proxy":case"mcp":case"config":case"profiles":case"setup":l(ct.name);return;case"import":{let P=Ce[0]??"",ue=Ce.includes("--write"),Yr=Ce.find((se)=>se!=="compose"&&se!=="--write")??"compose.yaml";if(P!=="compose"){i("usage: /import compose [path] [--write]");return}if(!z(Yr)){i(`compose file not found: ${Yr}`);return}let Jr=p?.repoRoot||process.cwd(),wr=p?.configPath||`${Jr}/.devctl/config.yaml`;try{let se=wl(de(Yr),Jr.split("/").pop()??"imported"),Bn=Ne(Jr,wr,se.yaml),Un=`${Sl(se)}${Bn.length>0?`
2153
2225
  validation:
2154
2226
  ${Bn.map((Dr)=>` ${Dr}`).join(`
2155
2227
  `)}
2156
2228
  `:""}`;if(ue){if(Bn.length>0){m({title:"compose import",body:Un}),e("scroll-text"),i("import is invalid; not written");return}if(z(wr)){i(`configuration already exists at ${wr}; not overwriting`);return}Ee(wr,se.yaml),i(`wrote ${wr}; /reload to apply`);return}m({title:"compose import (dry-run)",body:`${Un}
2157
- Enter is preview only. /import compose ${Yr} --write to save.`}),e("scroll-text")}catch(se){i(A(se))}return}case"diff":if(!p){i("no configuration loaded");return}m({title:"config sources",body:Ww(p,h)}),e("scroll-text");return;case"daemon":{let P=p?.repoRoot;if(!P){i("no repository");return}let ue=B(P);if(!z(ue)){i("no daemon bootstrap log yet for this repository");return}m({title:"daemon bootstrap",body:de(ue)}),e("scroll-text");return}case"reload":if(!v)return;v.reload().then((P)=>{if(i(P.restart_required.length===0?"Configuration reloaded":`Reload requires restart: ${P.restart_required.join(", ")}`),P.restart_required.length>0)o("reload"),e("confirm");x()});return;case"doctor":l("doctor");return;case"stats":l("stats");return;case"start":await K(Ro,g);return;case"stop":await T(Ro);return;case"restart":{let P=ub(Ce),ue=Pp(P.services,n);await ve(ue,g,P.cascade);return}case"run":{let P=Ce[0]??"";if(!P){r("tasks"),t(""),e("slash"),i(Object.keys(p?.tasks??{}).length===0?"no tasks configured":"pick a task");return}if(!v){i("no daemon attached");return}try{let ue=await v.runTask(P);Ke(`task:${P}`),l("logs"),i(`task ${P} exited ${ue.code}`)}catch(ue){throw Ke(`task:${P}`),l("logs"),ue}return}case"exec":{let P=pb(Ce);if(!P.service){r("services"),t(""),e("slash"),i(Object.keys(p?.services??{}).length===0?"no services configured":"pick a service");return}if(!v){i("no daemon attached");return}if(P.printEnv){if(!p?.services[P.service]){i(`unknown service ${P.service}`);return}if(P.reveal)R(!0);i(`Resolving environment for ${P.service}\u2026`);try{await _(P.service),S(P.service),i(`Resolved environment for ${P.service}`)}catch(ue){S(P.service),i(A(ue))}return}if(P.command.length===0){i("usage: /exec <service> -- <command\u2026>");return}try{let ue=await v.execService(P.service,P.command);Ke(`${P.service}:exec`),l("logs"),i(`${P.service}:exec exited ${ue.code}`)}catch(ue){throw Ke(`${P.service}:exec`),l("logs"),ue}return}case"refresh":if(u==="auth"){await Vt();return}await x(),i("Refreshed status and logs");return;case"pause":Xe((P)=>!P),l("logs");return;case"filter":Ct((P)=>!P),l("logs");return;case"system":ge(),l("logs");return;case"clear":vt();return;case"reveal":{let P=!h;R(P),i(P?"Secrets visible this session":"Secrets hidden");return}case"wrap":{let P=Xc(at);yt(P),i(`Log ${Zc(P)}`);return}case"copy":await y();return;case"export":{let P=ne(Ce[0]);if(v)await v.logs({export:P,services:rr,level:Qe?"ERROR":Xt,search:qe,regex:mt,source:xr});else H(P,Kt);b.current=P,i(`Exported ${P}`);return}case"exports":{let P=b.current||ae();X(P),i(`Opened ${ae()}`);return}case"regex":br((P)=>!P),i(mt?"Substring search":"Regex search");return;case"since":nr(Ce[0]||new Date(Date.now()-3600000).toISOString()),i(`Logs since ${Ce[0]||"1h"}`);return;case"until":qt(Ce[0]||new Date().toISOString()),i(`Logs until ${Ce[0]||"now"}`);return;case"history":{let P=nt(),ue=Ce[0]||P[0];if(!ue){i("No persisted log sessions");return}or(ft(ue)),i(`Loaded session ${ue}`);return}case"buffer":w();return;case"edit":{if(!p)return;let P=process.env.DEVCTL_EDITOR||process.env.EDITOR,ue=P?[P,p.configPath]:process.platform==="darwin"?["open",p.configPath]:["xdg-open",p.configPath];Bun.spawn({cmd:ue,stdout:"ignore",stderr:"ignore",stdin:"ignore"}),i(`Opened ${p.configPath} in ${ue[0]}. Run /reload after saving.`);return}default:i(`/${ct.name}`)}}catch(P){i(A(P))}finally{setTimeout(()=>{Y.current=!1},tM)}},[ve,K,T,n,p,vt,v,y,Qe,Kt,Xt,mt,qe,rr,xr,at,k,w,S,s,g,x,Vt,f,h,u,ir,r,c,sr,ge])}}var tM=50;var zw=D(()=>{te();ff();mf();gr();At();tt()});import{useCallback as Yw,useRef as rM,useState as Jw}from"react";function Xw({cfg:e,readTextFile:t,setOverlay:r,setStatus:n,validateConfigText:o,writeTextFile:i,controller:s,setConfirmKind:a,refresh:c}){let l=rM(null),[d,u]=Jw(""),[f,p]=Jw(""),m=Yw(()=>{if(!e)return;try{u(t(e.configPath)),p(""),r("config-edit")}catch(v){n(A(v))}},[e]),h=Yw(()=>{if(!e)return;let v=l.current?.plainText??d,x=o(e.repoRoot,e.configPath,v);if(x.length>0){p(x.join(`
2158
- `)),n("Config buffer not saved");return}if(i(e.configPath,v),p(""),r("none"),!s){n(`Wrote ${e.configPath}`);return}s.reload().then((g)=>{if(n(g.restart_required.length===0?"Configuration saved and reloaded":`Saved; restart required: ${g.restart_required.join(", ")}`),g.restart_required.length>0)a("reload"),r("confirm");c()})},[e,d,s,c]);return{configEditRef:l,configEditText:d,configEditError:f,setConfigEditError:p,openConfigBuffer:m,saveConfigBuffer:h}}var Qw=D(()=>{te()});import{useEffect as nM}from"react";function Zw({controller:e,cfg:t,paused:r,logSince:n,refresh:o,setLogs:i,setCfg:s,setConfigReloadError:a,setStatus:c}){return nM(()=>{if(!e)return;let l,d=!1,u=[],f=t&&t.logs.max_memory_events>0?t.logs.max_memory_events:50000,p=()=>{if(l=void 0,u.length>0){let h=u.splice(0,u.length);i((v)=>Nx(v,h,n,f))}if(d)d=!1,o()},m=e.onEvent((h)=>{if(h.type===Mo&&h.payload&&typeof h.payload==="object"&&"event"in h.payload){let v=h.payload.event;if(!r)u.push(v);if(!l)l=setTimeout(p,30);return}if(h.type===_i){a(ob(h));return}if(h.type===ca)a(void 0),e.configSnapshot().then(s).catch((v)=>c(A(v)));if(d=!0,!l)l=setTimeout(p,30)});return()=>{if(m(),l)clearTimeout(l)}},[e,n,r,o]),{}}var eS=D(()=>{te();vr();gr()});import{useCallback as oM,useEffect as tS,useRef as rS,useState as vi}from"react";function nS({controller:e,workspace:t,cfg:r,snap:n,screen:o,configReloadError:i,setSnap:s,setStatus:a}){let{detectGoogle:c,runDoctor:l,loadPath:d}=t,[u,f]=vi(),[p,m]=vi(),[h,v]=vi(!1),[x,g]=vi(""),[R,_]=vi(0),[S,y]=vi({active:"Preparing diagnostics",checks:[]}),b=rS(void 0),w=rS(0),k=oM(async()=>{if(!e){a("Supervisor is not running");return}a("Probing configured identities\u2026");try{let L=await e.refreshAuth();s((W)=>W?{...W,identity:L}:W);let O=Object.values(L.service_account_status),F=O.filter((W)=>W==="available").length,Z=O.filter((W)=>W==="unavailable").length;a(O.length===0?"Identity refreshed \u2014 no service accounts configured":`Identity refreshed \u2014 ${F} available${Z>0?`, ${Z} unavailable`:""}`)}catch(L){a(A(L))}},[e]);return tS(()=>{c(r?.google.project_id??"").then(f).catch((L)=>{a(A(L))})},[r?.google.project_id,n?.identity.project]),tS(()=>{if(o!=="doctor"||!r)return;let L=b.current,O=n?.session_id??"";if(L?.cfg===r&&L.tick===R&&L.sessionID===O&&L.reloadError===i)return;b.current={cfg:r,tick:R,sessionID:O,reloadError:i};let F=++w.current;v(!0),g(""),y({active:"Preparing diagnostics",checks:[]});let Z;if(r.configPath!==""){Z="";try{d(r.repoRoot,r.configPath)}catch(W){Z=A(W)}}l(r,(W)=>{if(F===w.current)y(W)},{services:n?.services,proxyRunning:n?.proxy.running,repositoryConfigError:Z}).then((W)=>{if(F!==w.current)return;m(W)}).catch((W)=>{if(F!==w.current)return;g(A(W))}).finally(()=>{if(F===w.current)v(!1)})},[o,r,R,n?.session_id,i]),{google:u,setGoogle:f,doctor:p,doctorLoading:h,doctorError:x,doctorProgress:S,setDoctorTick:_,refreshAuth:k}}var oS=D(()=>{te()});import{useCallback as hf,useState as kl}from"react";function iS({controller:e,workspace:t,cfg:r,snap:n,refresh:o,setStatus:i,setOverlay:s}){let{resolveStartRequest:a,startupPlan:c,shutdownPlan:l,shutdownPlanExact:d}=t,[u,f]=kl(),[p,m]=kl([]),[h,v]=kl(!1),[x,g]=kl("start"),R=hf(async(y,b)=>{if(!e||!r){i("no configuration loaded");return}try{let w=y.length>0,k=a(r,{services:y,profile:w?void 0:b}),L=c(r,k.services,k.profile);g("start"),m(L.waves.flat().filter((Z)=>yo(n?.services[Z]))),f(L),s("plan"),v(!0);let O=k.services.filter((Z)=>!yo(n?.services[Z])),F=await e.start({services:O.length>0?O:k.services,profile:w?void 0:k.profile});await o(),v(!1),i(yw(F))}catch(w){v(!1),await o(),i(A(w))}},[r,e,o,n]),_=hf(async(y)=>{if(!e||!r){i("no configuration loaded");return}let b=y.length>0?y:Object.entries(n?.services??{}).filter(([,w])=>w.state!=="STOPPED"&&w.state!=="UNKNOWN").map(([w])=>w);if(b.length===0){i("nothing to stop");return}try{let w=l(r,b);g("stop"),m([]),f(w),s("plan"),v(!0),await e.stop(y),await o(),v(!1),i(xw(w))}catch(w){v(!1),await o(),i(A(w))}},[r,e,o,n]),S=hf(async(y,b,w=!1)=>{if(!e||!r){i("no configuration loaded");return}try{let k=vw(r,y,b),L=w?l(r,k.services):d(r,k.services);g("restart"),m([]),f(L),s("plan"),v(!0),await e.restart(k.services,w),await o(),v(!1);let O=yl(L);i(O===""?"Restarted selected services":`Restarted ${O}`)}catch(k){v(!1),await o(),i(A(k))}},[r,e,o]);return{plan:u,planInitiallyRunning:p,planBusy:h,lifecycle:x,beginStart:R,beginStop:_,beginRestart:S}}var sS=D(()=>{te();xl();At()});import{useCallback as Lr,useEffect as Kr,useMemo as Fn,useRef as iM,useState as xe}from"react";function cS(e,t,r,n,o){let i=Ep(e,t.sliceLen,t.windowStart,t.windowNewer);if(i.startDelta!==0){r(Math.max(0,t.windowStart+i.startDelta)),n(!0),o(i.selected);return}let s=Math.max(t.sliceLen-1,0),a=t.sliceLen>0&&(i.selected<s||t.windowNewer>0);if(a&&!t.pinned)r(Qc(t.filteredLen));n(a),o(i.selected)}function lS({controller:e,tui:t,names:r,screen:n,refresh:o,setStatus:i}){let[s,a]=xe([]),[c,l]=xe(!1),[d,u]=xe(!1),[f,p]=xe(!0),[m,h]=xe(""),[v,x]=xe(!1),[g,R]=xe(0),[_,S]=xe(""),[y,b]=xe(""),[w,k]=xe(""),[L,O]=xe(""),[F,Z]=xe(!1),[W,C]=xe(0),[j,B]=xe(Ar-1),[z,de]=xe(!1),[Ee,Ne]=xe(0),ne=aS,[H,ae]=xe(t.log_timestamps!==!1),[X,nt]=xe(t.log_metadata!==!1),[ft,ie]=xe(()=>[...Fx]),[Le]=xe(""),[Me]=xe(""),[Ke,gt]=xe(!1),[Xe,Ct]=xe("focus"),[ge,vt]=xe(!1),[at,yt]=xe(Ar-1),[rr,Qe]=xe(!1),[Xt,qe]=xe(-1),[mt,xr]=xe(),[Kt,br]=xe(""),[nr,qt]=xe(!1),[or,ir]=xe(!1),sr=iM([]),[Vt,ce]=xe(0),K=Fn(()=>el(r,s,ft),[r,s,ft]);Kr(()=>{ie((le)=>{let xt=new Set(le),Pt=!1;for(let Xr of s)if(Xr.service!==""&&!r.includes(Xr.service)&&!xt.has(Xr.service))xt.add(Xr.service),Pt=!0;return Pt?[...xt].sort():le})},[s,r]);let T=Fn(()=>xo(s,{service:w,services:ne,errorOnly:d,search:m,regex:Ke,source:Me,since:_,until:y,systemLogs:f}),[d,Ke,m,w,ne,_,Me,y,s,f]),ve=Fn(()=>_p(T,ge,Vt),[T,ge,Vt]),Y=ve.items,Lt=Fn(()=>xo(s,{service:L,services:ne,errorOnly:d,search:m,regex:Ke,source:Me,since:_,until:y,systemLogs:f}),[d,Ke,m,L,ne,_,Me,y,s,f]),ct=Fn(()=>_p(Lt,z,Ee),[Lt,z,Ee]),Ce=ct.items;Kr(()=>{qe(-1),vt(!1),yt(Math.max(0,Math.min(Ar,T.length)-1))},[d,m,w,f]),Kr(()=>{de(!1),B(Math.max(0,Math.min(Ar,Lt.length)-1))},[d,m,L,f]),Kr(()=>{if(n!=="logs")Qe(!1)},[n]),Kr(()=>{if(n!=="logs"||ge)return;yt(Math.max(0,Math.min(Ar,T.length)-1))},[T.length,ge,n]),Kr(()=>{if(n!=="logs"||z)return;B(Math.max(0,Math.min(Ar,Lt.length)-1))},[Lt.length,z,n]);let Ro=Lr(()=>{if(!ge)ce(Qc(T.length)),vt(!0)},[T.length,ge]),P=Lr(()=>{if(!z)Ne(Qc(Lt.length)),de(!0)},[Lt.length,z]),ue=Fn(()=>({sliceLen:Y.length,windowStart:ve.start,windowNewer:ve.newer,filteredLen:T.length,pinned:ge}),[T.length,ge,Y.length,ve.newer,ve.start]),Yr=Fn(()=>({sliceLen:Ce.length,windowStart:ct.start,windowNewer:ct.newer,filteredLen:Lt.length,pinned:z}),[Lt.length,z,Ce.length,ct.newer,ct.start]),Jr=Lr((le)=>cS(le,ue,ce,vt,yt),[ue]),wr=Lr((le)=>cS(le,Yr,Ne,de,B),[Yr]),se=Lr((le)=>{if(F&&W===1){wr(le);return}Jr(le)},[Jr,wr,W,F]),Bn=Lr((le)=>{let xt=Y.length,Pt=Ep(le,xt,ve.start,ve.newer);if(Pt.startDelta!==0)ce(Math.max(0,ve.start+Pt.startDelta)),vt(!0);qe(Pt.selected)},[Y.length,ve.newer,ve.start]),Un=Lr(()=>{if(vt(!1),de(!1),qe(-1),n==="logs")yt(Math.max(0,Math.min(Ar,T.length)-1)),B(Math.max(0,Math.min(Ar,Lt.length)-1));R((xt)=>xt+1);let le=Math.max(ve.newer,ct.newer);i(le>0?`Jumped to latest (+${le} new)`:"Jumped to latest logs")},[T.length,Lt.length,ve.newer,ct.newer,n]),Dr=Fn(()=>({services:F?aS:w!==""?[w]:ne,level:d?"ERROR":Le,search:m,regex:Ke,source:Me,since:_,until:y}),[d,Le,Ke,m,w,ne,_,Me,y,F]),dn=Lr(async()=>{if(!e)return;try{xr(await e.logsStats(Dr))}catch(le){i(A(le))}},[e,Dr]),Bs=Lr(async()=>{if(!e||c)return;try{let le=await e.logsPage(Dr);a((xt)=>Hx(xt,le.events)),br(le.prevCursor),qt(le.hasPrev),await dn()}catch(le){i(A(le))}},[e,Dr,c,dn]),ki=Lr(()=>{a([]),S(new Date().toISOString()),vt(!1),de(!1),i("Cleared on-screen log buffer")},[]),Ci=Lr(()=>{p((le)=>!le)},[]);return Kr(()=>{o().then(()=>Bs())},[o,Bs]),Kr(()=>{sr.current=s},[s]),Kr(()=>{let le=Tp(ge,ve.start,nr),xt=F&&Tp(z,ct.start,nr);if(!e||or||!le&&!xt)return;ir(!0),e.logsPage({...Dr,cursor:Kt,direction:"backward"}).then((Pt)=>{if(Pt.sessionChanged){i("Daemon session changed \u2014 older log history is no longer available"),qt(!1);return}let Xr=sr.current,Us=jx(Xr,Pt.events);a(Us);let _o=Us.length-Xr.length;if(_o>0){if(ge)ce((Sr)=>Sr+_o);if(z)Ne((Sr)=>Sr+_o)}br(Pt.prevCursor),qt(Pt.hasPrev)}).catch((Pt)=>i(A(Pt))).finally(()=>ir(!1))},[e,Dr,or,nr,ge,z,Kt,ve.start,ct.start,F]),Kr(()=>{if(!e||c||n!=="logs")return;let le=setInterval(()=>{dn()},sM);return()=>clearInterval(le)},[e,c,dn,n]),{logs:s,setLogs:a,paused:c,setPaused:l,errorOnly:d,setErrorOnly:u,showSystemLogs:f,logSearch:m,setLogSearch:h,logSearchFocused:v,setLogSearchFocused:x,logFollow:g,logSince:_,setLogSince:S,setLogUntil:b,logService:w,setLogService:k,logServiceB:L,setLogServiceB:O,splitLogs:F,splitFocus:W,setSplitFocus:C,toggleSplitLogs:()=>{if(F){C(0),Z(!1);return}if(L===""){let le=r.find((xt)=>xt!==w);if(le!==void 0)O(le)}Z(!0)},cycleSplitFocus:()=>C((le)=>le===0?1:0),setActiveLogService:(le)=>{let xt=(Pt)=>typeof le==="function"?le(Pt):le;if(F&&W===1){O(xt);return}k(xt)},logSliceB:Ce,logWindowB:ct,logSelectedB:j,logPinnedB:z,logServices:ne,logShowTimestamps:H,setLogShowTimestamps:ae,logShowMeta:X,setLogShowMeta:nt,logLevel:Le,logSource:Me,logRegex:Ke,setLogRegex:gt,logWrap:Xe,setLogWrap:Ct,logPinned:ge,logSelected:at,logsFullscreen:rr,setLogsFullscreen:Qe,dashboardLogCursor:Xt,setDashboardLogCursor:qe,logFacets:mt,logSources:K,filteredLogs:T,filteredLogsB:Lt,logWindow:ve,logSlice:Y,pinLogView:Ro,pinLogViewB:P,applyLogCursor:se,applyLogCursorA:Jr,applyLogCursorB:wr,applyDashboardLogCursor:Bn,jumpToLatestLogs:Un,clearLogs:ki,toggleSystemLogs:Ci}}var aS,sM=2000;var dS=D(()=>{te();gr();aS=[]});import{useCallback as Co,useState as uS}from"react";function pS({tui:e,controller:t,cfg:r,persistPrefs:n,snap:o,refresh:i,setStatus:s}){let[a,c]=uS(""),[l,d]=uS(()=>e.mcp_port??mo(t?.cfg.repoRoot??process.cwd())),u=Co((g)=>{let R=_n(g);d(R);let _=r?.repoRoot??process.cwd();if(Ey(_,R)){n({mcp_port:null},`MCP port ${R} (default)`);return}n({mcp_port:R},`MCP port ${R}`)},[r,n]),f=Co(()=>{if(a==="")return l;let g=_y(a,l);return c(""),u(g),g},[l,a,u]),p=Co(async(g)=>{if(!t||o?.mcp?.running!==!0)return;await t.mcpStop(),await t.mcpStart({port:g}),await i()},[t,i,o?.mcp?.running]),m=Co(async(g)=>{if(!t){s("Supervisor is not running");return}let R=o?.mcp?.disabled_tools??[],_=An(g.name,R),S=_?[...R,g.name]:R.filter((y)=>y!==g.name);try{let y=await t.mcpSetTools(S);n({mcp_disabled_tools:y},`${g.label} ${_?"disabled":"enabled"}`),await i()}catch(y){s(A(y))}},[t,n,i,o]),h=Co(async()=>{if(!t){s("Supervisor is not running");return}try{if(o?.mcp?.running)await t.mcpStop(),n({mcp_enabled:!1},"MCP off"),s("MCP stopped");else await t.mcpStart({port:l}),n({mcp_enabled:!0},"MCP on"),s("MCP started");await i()}catch(g){s(A(g))}},[t,l,n,i,o?.mcp?.running]),v=Co((g)=>{fl(g.text).then(()=>{s(`Copied ${g.title} snippet`)}).catch((R)=>{s(A(R))})},[]),x=Co((g)=>{let R=tf(g);if(R===void 0)return!1;let _=o?.mcp?.address??fi(o?.mcp?.port??l),S=Ds(_,o?.mcp?.token??"")[R];if(!S)return!1;return v(S),!0},[v,l,o?.mcp?.address,o?.mcp?.port,o?.mcp?.token]);return{mcpPortDraft:a,setMcpPortDraft:c,mcpPort:l,persistMcpPort:u,applyMcpPortDraft:f,restartMcpOnPort:p,toggleMcpTool:m,toggleMcp:h,copyMcpSnippet:v,copyFocusedMcpSnippet:x}}var fS=D(()=>{te();vl();us();Gp();Ms()});import{RGBA as aM}from"@opentui/core";import{useCallback as cn,useEffect as cM,useMemo as mS,useRef as Cl,useState as Pl}from"react";function hS({tui:e,controller:t,resolveTuiOverridePath:r,terminalBackground:n,userTuiConfigPath:o,snap:i,setStatus:s,saveTuiPreferences:a,setPaletteIndex:c,setOverlay:l,setConfirmKind:d,setScreen:u,setSelected:f,screen:p}){let[m,h]=Pl(e.theme||t?.cfg.ui.theme||"devctl"),v=Cl(m),[x,g]=Pl(e.mouse),R=Cl(e.mouse),[_,S]=Pl(e.leader_timeout),y=Cl(e.leader_timeout),[b,w]=Pl(()=>vs(e.font_size)),k=Cl(b),L=ux(r()),O=mS(()=>{let H=sx(m);if(Ec(m)!=="terminal"||!up(n))return H;let ae=n??H.background;return{...H,background:ae,panel:ae,element:ae,inverse:ae}},[n,m]),F=Ec(m)==="terminal"&&up(n)?aM.defaultBackground(n??void 0):O.background,Z=mS(()=>hx({themeName:m,fontSize:b,mouse:x,leaderMs:_,locked:L,configPath:L?e.path||gp(r(),o()):gp(r(),o()),mcpRunning:i?.mcp?.running===!0}),[b,_,x,L,i?.mcp?.running,m,e.path]),W=cn((H,ae)=>{if(L){s(`${ae} session only`);return}let X=a(H);s(`${ae} saved ${X}`)},[L]),C=cn((H)=>{h(H),v.current=H,W({theme:H},`theme ${H}`)},[W]),j=cn(()=>{let H=!x;g(H),R.current=H,W({mouse:H},`mouse ${H?"on":"off"} restart TUI to apply clicks`)},[x,W]),B=cn((H)=>{S(H),y.current=H,W({leader_timeout:H},`leader ${H}ms`)},[W]),z=cn((H)=>{let ae=vs(H);w(ae),k.current=ae,W({font_size:ae},`display ${Dc(ae)}`)},[W]),de=cn(()=>{let H=Ic();h(H.theme),v.current=H.theme,g(H.mouse),R.current=H.mouse,S(H.leader_timeout),y.current=H.leader_timeout,w(H.font_size),k.current=H.font_size,W(H,"restored default preferences")},[W]),Ee=cn((H)=>{if(H.id==="theme"){c(Math.max(0,St.indexOf(m))),l("themes");return}if(H.id==="mouse"){j();return}if(H.id==="leader"){B(_);return}if(H.id==="font"){z(b);return}if(H.id==="reset"){d("reset-prefs"),l("confirm");return}if(H.id==="mcp"){u("mcp"),f(0);return}s(H.detail)},[z,B,b,_,m,j]),Ne=cn((H,ae)=>{let X=Ln(Z,ae);if(!X)return;if(X.id==="theme"){C(px(m,H));return}if(X.id==="leader"){B(fx(_,H));return}if(X.id==="font"){z(ys(b,H));return}if(X.id==="mouse")j()},[z,B,b,_,C,Z,m,j]),ne=cn(()=>{h(v.current)},[]);return cM(()=>{if(p==="settings")return;h(v.current),w(k.current),S(y.current),g(R.current)},[p]),{themeName:m,setThemeName:h,leaderMs:_,fontSize:b,prefsLocked:L,palette:O,rootBackground:F,settingRows:Z,persistPrefs:W,persistTheme:C,toggleMouse:j,applyFont:z,applyReset:de,activateSetting:Ee,cycleSetting:Ne,revertThemePreview:ne}}var gS=D(()=>{Er();tt()});import{jsxDEV as $s}from"@opentui/react/jsx-dev-runtime";function vS(e){let{palette:t,termW:r,termH:n,step:o,answers:i,repo:s,draft:a,authStatus:c,onDraft:l,onSubmit:d}=e,u=wt[o]??wt[0],f=wt.length,p=lM(u.id,i,s);return $s(pt,{palette:t,title:`setup ${o+1}/${f} ${u.title}`,bottomTitle:"enter next \xB7 esc cancel",termW:r,termH:n,preferW:64,preferH:12,borderColor:t.primary,gap:1,children:[$s("text",{fg:t.muted,wrapMode:"word",children:p},void 0,!1,void 0,this),u.id==="auth"||u.id==="write"||u.id==="repo"?$s("text",{fg:t.text,wrapMode:"word",children:u.id==="auth"?c:u.id==="repo"?s:"Write .devctl/config.yaml and attach the daemon."},void 0,!1,void 0,this):$s("input",{focused:!0,value:a,placeholder:u.prompt,onInput:l,onSubmit:d,backgroundColor:t.element,focusedBackgroundColor:t.element,textColor:t.text,cursorColor:t.primary},void 0,!1,void 0,this),$s(it,{palette:t,hints:[{key:"enter",label:u.id==="write"?"write and attach":"next"},{key:"esc",label:"cancel"}]},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function gf(e,t,r){if(e==="name")return t.name;if(e==="project")return t.project;if(e==="sa")return t.sa;if(e==="audience")return t.audience;if(e==="port")return String(t.proxyPort);if(e==="profile")return t.profile;if(e==="repo")return r;return""}function yS(e,t,r,n){let o={...t};if(e==="name")o.name=r.trim()||wn(n).name;else if(e==="project")o.project=r.trim();else if(e==="sa")o.sa=r.trim();else if(e==="audience")o.audience=r.trim();else if(e==="port")o.proxyPort=Ea(r,t.proxyPort);else if(e==="profile")o.profile=r.trim();return o}function lM(e,t,r){if(e==="repo")return`Using ${r}. The TUI always writes under this checkout.`;if(e==="auth")return"ADC login needs a real TTY. Enter continues; if ADC is missing, login runs gcloud on the terminal.";if(e==="write")return`Project ${t.name||"(unnamed)"}. Enter writes the starter file and attaches the daemon.`;return wt.find((n)=>n.id===e)?.prompt??""}var vf=D(()=>{Ta();ke()});import{useCallback as yf,useState as Rl}from"react";function xS({workspace:e,renderer:t,setOverlay:r,setStatus:n,setScreen:o,onAttached:i,setController:s,setCfg:a,setBootError:c}){let l=process.cwd(),[d,u]=Rl(0),[f,p]=Rl(()=>wn(l)),[m,h]=Rl(""),[v,x]=Rl("Checking Application Default Credentials\u2026"),g=yf(()=>{let S=wn(l);u(0),p(S),h(gf("repo",S,l)),r("setup-wizard"),e.detectGoogle("").then((y)=>{p((b)=>({...b,project:y.projectID||b.project})),x(y.adcAvailable?`ADC available${y.userEmail?` (${y.userEmail})`:""}. Enter continues.`:"ADC is not available. Enter runs gcloud ADC login on this TTY, then continues.")})},[l,r,e]),R=yf(async(S)=>{try{La(l,S);let y=await e.openTui(l,"");i?.(y),s(y),a(y.cfg),c(void 0),r("none"),o("dashboard"),n("Wrote .devctl/config.yaml and attached the daemon")}catch(y){n(A(y))}},[i,l,c,a,s,r,o,n,e]),_=yf(async()=>{let S=wt[d];if(!S)return;let y=yS(S.id,f,m,l);if(S.id==="auth"){if(!(await e.detectGoogle(y.project)).adcAvailable)try{await Sl(t,()=>e.loginGoogle())}catch(L){n(A(L))}}if(p(y),S.id==="write"){await R(y);return}let b=Math.min(d+1,wt.length-1),w=wt[b];u(b),h(w?gf(w.id,y,l):"")},[f,m,R,t,l,d,e,n]);return{step:d,answers:f,draft:m,authStatus:v,repo:l,setDraft:h,startWizard:g,advanceWizard:_}}var bS=D(()=>{Ta();Da();te();vf()});import{useCallback as dM,useEffect as wS,useState as xf}from"react";function SS({controller:e,cfg:t,envService:r}){let[n,o]=xf(),[i,s]=xf(!1),[a,c]=xf(""),l=n?.name===r,d=l?n?.env:void 0,u=r===""?"config":i&&!l?"loading":l?"resolved":a!==""?"error":"config",f=l||r===""?"":a;wS(()=>{o(void 0),c("")},[t]),wS(()=>{if(!e||r===""||!t?.services[r])return;if(n?.name===r)return;let m=!1;return s(!0),c(""),e.execService(r,[],!0).then((h)=>{if(m)return;o({name:r,env:h.environment??{}}),c("")}).catch((h)=>{if(m)return;c(A(h))}).finally(()=>{s(!1)}),()=>{m=!0}},[t,e,r,n?.name]);let p=dM(async(m)=>{if(!e)return;try{let h=await e.execService(m,[],!0);o({name:m,env:h.environment??{}}),c("")}catch(h){throw c(A(h)),h}},[e]);return{inspectorEnv:d,inspectorEnvStatus:u,inspectorEnvError:f,resolveEnvironment:p}}var kS=D(()=>{te()});import{jsxDEV as Nn}from"@opentui/react/jsx-dev-runtime";function CS(e){let{palette:t,path:r,initialValue:n,error:o,termW:i,termH:s,textareaRef:a}=e;return Nn(pt,{palette:t,title:"config buffer",bottomTitle:`${It("s")} save \xB7 esc discard \xB7 e still opens $EDITOR`,termW:i,termH:s,preferW:88,preferH:28,gap:1,children:[Nn("box",{height:1,overflow:"hidden",children:Nn("text",{fg:t.muted,wrapMode:"none",children:r},void 0,!1,void 0,this)},void 0,!1,void 0,this),Nn("textarea",{ref:a,focused:!0,initialValue:n,backgroundColor:t.panel,focusedBackgroundColor:t.panel,textColor:t.text,focusedTextColor:t.text,cursorColor:t.primary},void 0,!1,void 0,this),o!==""?Nn("box",{height:2,overflow:"hidden",children:Nn("text",{fg:t.error,wrapMode:"word",children:o},void 0,!1,void 0,this)},void 0,!1,void 0,this):Nn("box",{height:1,overflow:"hidden",children:Nn("text",{fg:t.muted,children:"validate, then write. invalid YAML is not saved."},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}var PS=D(()=>{ke();_r()});import{jsxDEV as bf}from"@opentui/react/jsx-dev-runtime";function RS(e){let{palette:t,title:r,body:n,kind:o,termW:i,termH:s}=e,a=nb(o),c=a.map((l)=>`${l.key} ${l.label}`).join(" \xB7 ");return bf(pt,{palette:t,title:r,bottomTitle:c,termW:i,termH:s,preferW:52,preferH:8,borderColor:t.warning,gap:1,children:[bf("text",{fg:t.text,wrapMode:"word",children:n},void 0,!1,void 0,this),bf(it,{palette:t,hints:a},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}var _S=D(()=>{vr();ke()});import{jsxDEV as ES}from"@opentui/react/jsx-dev-runtime";function TS(e){let{palette:t,termW:r,termH:n}=e,o=Fp();return ES(pt,{palette:t,title:"leader",termW:r,termH:n,preferW:68,preferH:5,anchor:"bottom",children:ES(it,{palette:t,hints:o},void 0,!1,void 0,this)},void 0,!1,void 0,this)}var AS=D(()=>{So();ke()});import{jsxDEV as Jt,Fragment as uM}from"@opentui/react/jsx-dev-runtime";function LS(e){let{palette:t,event:r,termW:n,termH:o,scrollRef:i}=e;if(!r)return null;let s=r.raw?Lx(r.raw):void 0,a=r.message.length>120||r.message.includes(`
2159
- `)||s!==void 0;return Jt(pt,{palette:t,title:"log details",bottomTitle:"j/k scroll \xB7 esc close",termW:n,termH:o,preferW:a?84:72,preferH:a?22:14,gap:1,children:Jt("scrollbox",{ref:i,focused:!1,stickyScroll:!1,scrollX:!1,style:Ue(t),children:Jt("box",{flexDirection:"column",overflow:"hidden",children:[Jt("text",{fg:t.text,wrapMode:"word",children:Yc(r.message)},void 0,!1,void 0,this),Jt("text",{fg:t.muted,children:`time ${r.timestamp}`},void 0,!1,void 0,this),Jt("text",{fg:Ze(r.service,t),children:`service ${r.service}`},void 0,!1,void 0,this),Jt("text",{fg:t.muted,children:`source ${r.source}${r.stream?` / ${r.stream}`:""}`},void 0,!1,void 0,this),Jt("text",{fg:t.muted,children:`level ${zc(r.level)}`},void 0,!1,void 0,this),Jt("text",{fg:t.muted,children:`pid ${r.pid||"\u2014"}`},void 0,!1,void 0,this),Jt("text",{fg:t.muted,children:`request ${r.request_id||"\u2014"}`},void 0,!1,void 0,this),Jt("text",{fg:t.muted,children:`identity ${r.identity||"\u2014"}`},void 0,!1,void 0,this),s===void 0?null:Jt(uM,{children:[Jt("text",{fg:t.muted,children:"raw json"},void 0,!1,void 0,this),Jt("text",{fg:t.text,wrapMode:"word",children:s},void 0,!1,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this)}var DS=D(()=>{gr();ke();tt()});import{jsxDEV as U}from"@opentui/react/jsx-dev-runtime";function wf(e,t){switch(e){case"completed":return{icon:"\u2713",label:"Completed",tone:"success",color:t.success};case"failed":return{icon:"\u2717",label:"Failed",tone:"error",color:t.error};case"unhealthy":return{icon:"\u26A0",label:"Unhealthy",tone:"warning",color:t.warning};case"active":return{icon:"\u25CF",label:"In progress",tone:"primary",color:t.primary};default:return{icon:"\u25CB",label:"Queued",tone:"muted",color:t.muted}}}function pM(e,t,r,n){if(t!=="")return{icon:"\u2717",label:"Failed",tone:"error",color:n.error};if(!e)return{icon:"\u2713",label:"Finished",tone:"success",color:n.success};if(r==="unhealthy")return{icon:"\u26A0",label:"Waiting on health",tone:"warning",color:n.warning};return{icon:"\u25CF",label:"Running",tone:"primary",color:n.primary}}function IS(e){return U("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,children:U("text",{wrapMode:"none",children:[U("span",{fg:e.palette.primary,children:e.label.toUpperCase()},void 0,!1,void 0,this),e.detail?U("span",{fg:e.palette.muted,children:` ${e.detail}`},void 0,!1,void 0,this):null]},void 0,!0,void 0,this)},void 0,!1,void 0,this)}function MS(e){let{palette:t,plan:r,snap:n,busy:o,failed:i,kind:s,termW:a=80,termH:c=24,scrollRef:l,initiallyRunning:d=[],onDismiss:u}=e,f=new Set(d),p=r.waves.map((C,j)=>({wave:C.filter((B)=>!f.has(B)),waveIdx:j})).filter(({wave:C})=>C.length>0),m=ww(r,n,s),h=r.waves[m.currentWaveIndex]??[],v=Os(h,n,s),x=wf(v,t),g=pM(o,i,v,t),R=kw(s,o,i,r.profile),_=Cw(o,i),S=i?t.inverse:t.text,y=i?t.error:t.element,b=Math.min(92,Math.max(40,a-8)),w=b>=82?22:b>=66?17:13,k=12,L=9,O=b>=56,F=b>=72,Z=b<72||r.waves.length>3,W=r.profile?`PROFILE ${r.profile}`:s.toUpperCase();return U(pt,{palette:t,title:R,bottomTitle:"enter close \xB7 esc hide (operation continues)",termW:a,termH:c,preferW:98,preferH:28,borderColor:g.color,gap:0,children:[U("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,backgroundColor:t.element,children:[U(Be,{palette:t,label:`${g.icon} ${g.label.toUpperCase()}`,tone:g.tone},void 0,!1,void 0,this),U("box",{paddingLeft:1,flexGrow:1,overflow:"hidden",children:U("text",{fg:t.text,wrapMode:"none",children:W},void 0,!1,void 0,this)},void 0,!1,void 0,this),U(Be,{palette:t,label:`${m.ready}/${m.total} READY`,tone:m.isComplete?"success":"info"},void 0,!1,void 0,this),U(Be,{palette:t,label:`WAVE ${Math.min(m.currentWaveIndex+1,Math.max(1,m.totalWaves))}/${Math.max(1,m.totalWaves)}`,tone:x.tone},void 0,!1,void 0,this)]},void 0,!0,void 0,this),U("box",{height:2,flexDirection:"column",overflow:"hidden",flexShrink:0,paddingLeft:1,paddingRight:1,children:[U("box",{height:1,flexDirection:"row",overflow:"hidden",children:[U("text",{wrapMode:"none",children:[U("span",{fg:t.muted,children:"PROGRESS "},void 0,!1,void 0,this),U("span",{fg:g.color,children:`[${m.progressBar}]`},void 0,!1,void 0,this),U("span",{fg:t.text,children:` ${m.percent}%`},void 0,!1,void 0,this)]},void 0,!0,void 0,this),U("box",{flexGrow:1},void 0,!1,void 0,this),U("text",{wrapMode:"none",children:U("span",{fg:x.color,children:`${x.icon} ${x.label}`},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this),U("box",{height:1,overflow:"hidden",children:U("text",{fg:t.muted,wrapMode:"none",children:o?s==="stop"?"Dependents stop first; each wave waits for the previous wave.":"Each wave waits until its services are healthy before the next wave starts.":_.primary},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this),U(IS,{palette:t,label:"Pipeline",detail:`${r.waves.length} execution wave${r.waves.length===1?"":"s"}`},void 0,!1,void 0,this),U("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,backgroundColor:t.element,children:r.waves.map((C,j)=>{let B=Os(C,n,s),z=wf(B,t),de=Z?`W${j+1} ${z.icon} ${C.length}`:`W${j+1} ${z.icon} ${z.label} \xB7 ${C.length}`;return U("box",{flexDirection:"row",overflow:"hidden",flexShrink:0,children:[U(Be,{palette:t,label:de,tone:z.tone},void 0,!1,void 0,this),j<r.waves.length-1?U("box",{width:3,flexShrink:0,overflow:"hidden",children:U("text",{fg:t.muted,children:" \u2500\u25BA"},void 0,!1,void 0,this)},void 0,!1,void 0,this):null]},`step-${j}`,!0,void 0,this)})},void 0,!1,void 0,this),(r.blockers??[]).length>0?U("box",{minHeight:1,flexDirection:"row",overflow:"hidden",flexShrink:0,backgroundColor:t.element,paddingLeft:1,children:U("text",{fg:t.error,wrapMode:"word",children:`BLOCKED ${r.blockers?.map((C)=>`${C.name}: ${C.message}`).join(" \xB7 ")}`},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,d.length>0?U("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,paddingLeft:1,children:U("text",{wrapMode:"none",children:[U("span",{fg:t.success,children:"\u2713 ALREADY READY"},void 0,!1,void 0,this),U("span",{fg:t.muted,children:` ${d.join(", ")}`},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this):null,U(IS,{palette:t,label:"Wave details",detail:"live process and health state"},void 0,!1,void 0,this),U("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,backgroundColor:t.element,paddingLeft:1,children:[U("box",{width:2,flexShrink:0},void 0,!1,void 0,this),U("box",{width:w,flexShrink:0,overflow:"hidden",children:U("text",{fg:t.muted,children:oe("SERVICE",w)},void 0,!1,void 0,this)},void 0,!1,void 0,this),U("box",{width:12,flexShrink:0,overflow:"hidden",children:U("text",{fg:t.muted,children:oe("STATE",12)},void 0,!1,void 0,this)},void 0,!1,void 0,this),O?U("box",{width:9,flexShrink:0,overflow:"hidden",children:U("text",{fg:t.muted,children:oe("PORT",9)},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,F?U("box",{flexGrow:1,overflow:"hidden",children:U("text",{fg:t.muted,children:"DETAIL"},void 0,!1,void 0,this)},void 0,!1,void 0,this):null]},void 0,!0,void 0,this),U("scrollbox",{ref:l,focused:!1,stickyScroll:!1,scrollX:!1,style:Ue(t),children:U("box",{flexDirection:"column",overflow:"hidden",gap:1,children:p.map(({wave:C,waveIdx:j})=>{let B=Os(C,n,s),z=wf(B,t),de=`${Sw(s,j,B)} \xB7 ${C.length} service${C.length===1?"":"s"}`;return U("box",{border:!0,borderStyle:"rounded",borderColor:z.color,title:de,titleColor:z.color,flexDirection:"column",paddingLeft:1,paddingRight:1,flexShrink:0,overflow:"hidden",children:C.map((Ee)=>{let Ne=n?.services[Ee],ne=tr(Ne),H=bw(Ee,r,n,s),ae=Ee===i||ne==="FAILED",X=ne==="UNHEALTHY",nt=ne==="HEALTHY"||s==="stop"&&ne==="STOPPED",ft=vo(Ne),ie=ae?"\u2717":nt?"\u2713":X?"\u26A0":ne==="STARTING"||ne==="RUNNING"||ne==="STOPPING"||ne==="RESTARTING"?"\u25CF":"\u25CB",Le=ae?t.error:et(t,ne),Me=ae?t.error:X?t.warning:nt?t.success:t.muted;return U("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,children:[U("box",{width:2,flexShrink:0,overflow:"hidden",children:U("text",{fg:Le,children:ie},void 0,!1,void 0,this)},void 0,!1,void 0,this),U("box",{width:w,flexShrink:0,overflow:"hidden",children:U("text",{fg:Ze(Ee,t),wrapMode:"none",children:oe(Ee,w)},void 0,!1,void 0,this)},void 0,!1,void 0,this),U("box",{width:12,flexShrink:0,overflow:"hidden",children:U("text",{fg:et(t,ne),wrapMode:"none",children:oe(ne,12)},void 0,!1,void 0,this)},void 0,!1,void 0,this),O?U("box",{width:9,flexShrink:0,overflow:"hidden",children:U("text",{fg:ft?t.info:t.muted,wrapMode:"none",children:oe(ft?`:${ft}`:"\u2014",9)},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,F?U("box",{flexGrow:1,overflow:"hidden",children:U("text",{fg:Me,wrapMode:"none",children:H},void 0,!1,void 0,this)},void 0,!1,void 0,this):null]},Ee,!0,void 0,this)})},`wave-card-${j}`,!1,void 0,this)})},void 0,!1,void 0,this)},void 0,!1,void 0,this),U("box",{height:3,flexShrink:0,flexDirection:"column",backgroundColor:y,border:["top"],borderStyle:"single",borderColor:g.color,paddingLeft:1,paddingRight:1,overflow:"hidden",onMouseDown:u,children:[U("text",{fg:S,wrapMode:"none",children:_.primary},void 0,!1,void 0,this),U("text",{fg:i?t.inverse:t.muted,wrapMode:"none",children:_.secondary},void 0,!1,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)}var OS=D(()=>{Ot();xl();At();ke();tt()});import{jsxDEV as yr}from"@opentui/react/jsx-dev-runtime";function $S(e){let{palette:t,route:r,termW:n,termH:o,scrollRef:i}=e;if(!r)return null;return yr(pt,{palette:t,title:r.name,bottomTitle:"j/k scroll \xB7 esc close",termW:n,termH:o,preferW:78,preferH:18,gap:1,children:yr("scrollbox",{ref:i,focused:!1,stickyScroll:!1,scrollX:!1,style:Ue(t),children:yr("box",{flexDirection:"column",overflow:"hidden",children:[yr("text",{fg:t.muted,children:`auth ${r.authType||"none"}`},void 0,!1,void 0,this),yr("text",{fg:t.muted,children:`identity ${r.identityType||"\u2014"}`},void 0,!1,void 0,this),yr("text",{fg:t.text,wrapMode:"word",children:`account ${r.serviceAccount||"\u2014"}`},void 0,!1,void 0,this),yr("text",{fg:t.text,wrapMode:"word",children:`audience ${r.audience||"\u2014"}`},void 0,!1,void 0,this),yr("text",{fg:t.muted,wrapMode:"word",children:`client id ${r.clientId||"ADC default"}`},void 0,!1,void 0,this),yr("text",{fg:t.muted,wrapMode:"word",children:`client secret ${r.clientSecret||"\u2014"}`},void 0,!1,void 0,this),yr("text",{fg:t.muted,children:`match host ${r.matchHost||"*"}`},void 0,!1,void 0,this),yr("text",{fg:t.muted,children:`match path ${r.matchPath||"*"}`},void 0,!1,void 0,this),yr("text",{fg:t.text,wrapMode:"word",children:`upstream ${r.upstream}`},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this)}var FS=D(()=>{ke()});import{jsxDEV as _l}from"@opentui/react/jsx-dev-runtime";function NS(e){let{palette:t,title:r,body:n,termW:o,termH:i,scrollRef:s}=e,a=n.length>400||n.split(`
2160
- `).length>16;return _l(pt,{palette:t,title:r,bottomTitle:"j/k scroll \xB7 esc close",termW:o,termH:i,preferW:a?88:76,preferH:a?24:16,gap:1,children:_l("scrollbox",{ref:s,focused:!1,stickyScroll:!1,scrollX:!1,style:Ue(t),children:_l("box",{flexDirection:"column",overflow:"hidden",children:_l("text",{fg:n===""?t.muted:t.text,wrapMode:"word",children:n===""?"(empty)":n},void 0,!1,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this)}var HS=D(()=>{ke()});import{jsxDEV as Wt}from"@opentui/react/jsx-dev-runtime";function jS(e){let{palette:t,items:r,query:n,selected:o,onQuery:i,onSubmit:s,title:a="commands"}=e,c=wo(n)!=="",l=Pb(o,Sf,r.length),d=c?r.slice(l,l+Sf):Rb(r,o,Sf),u=c?[{group:"",items:d}]:Mp(d),f=Math.max(d.length+(c?0:u.length),1),p=di(r,o),m=bb(r);return Wt("box",{height:f+fM,flexShrink:0,border:!0,borderStyle:"rounded",borderColor:t.borderActive,titleColor:t.primary,backgroundColor:t.panel,title:a,flexDirection:"column",overflow:"hidden",children:[d.length===0?Wt("box",{height:1,paddingLeft:1,overflow:"hidden",children:Wt("text",{fg:t.muted,children:"no matching command"},void 0,!1,void 0,this)},void 0,!1,void 0,this):u.flatMap((h)=>[...h.group===""?[]:[Wt("box",{height:1,paddingLeft:1,overflow:"hidden",backgroundColor:t.panel,children:Wt("text",{fg:t.muted,wrapMode:"none",children:h.group},void 0,!1,void 0,this)},`g-${h.group}`,!1,void 0,this)],...h.items.map((x)=>{let g=il(x)===(p?il(p):"");return Wt("box",{height:1,flexDirection:"row",overflow:"hidden",paddingLeft:1,paddingRight:1,backgroundColor:g?t.highlight:t.panel,children:[Wt("box",{width:m,flexShrink:0,overflow:"hidden",children:Wt("text",{fg:g?t.primary:t.text,wrapMode:"none",children:`${g?"\u203A":" "} /${Op(x).slice(0,Ip)}`},void 0,!1,void 0,this)},void 0,!1,void 0,this),Wt("box",{flexGrow:1,overflow:"hidden",children:Wt("text",{fg:g?t.accent:t.muted,wrapMode:"none",children:xb(x)},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},il(x),!0,void 0,this)})]),Wt("box",{height:1,flexDirection:"row",overflow:"hidden",backgroundColor:t.highlight,paddingLeft:1,children:[Wt("box",{width:2,flexShrink:0,children:Wt("text",{fg:t.primary,children:"/"},void 0,!1,void 0,this)},void 0,!1,void 0,this),Wt("box",{flexGrow:1,overflow:"hidden",children:Wt("input",{focused:!0,value:n,placeholder:"start logs settings",onInput:i,onSubmit:()=>s(),backgroundColor:t.highlight,focusedBackgroundColor:t.highlight,textColor:t.text,cursorColor:t.primary},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)}var Sf=10,fM=3;var BS=D(()=>{So()});import{jsxDEV as GS}from"@opentui/react/jsx-dev-runtime";function KS(e){let{palette:t,themeName:r,selected:n,termW:o,termH:i,onIndex:s,onPreview:a,onPick:c}=e,l=St.map((u)=>({name:u===r?`${u} (active)`:u,description:u===r?`${dp[u]} \xB7 current`:dp[u],value:u})),d=nl(o,i,US,WS,"center",!go(ye()));return GS(pt,{palette:t,title:"/themes",bottomTitle:"enter save \xB7 esc revert",termW:o,termH:i,preferW:US,preferH:WS,children:GS("select",{height:Math.max(4,d.height-3),options:l,selectedIndex:n,showDescription:!0,showSelectionIndicator:!0,wrapSelection:!0,backgroundColor:t.panel,focusedBackgroundColor:t.panel,textColor:t.text,focusedTextColor:t.text,selectedBackgroundColor:t.highlight,selectedTextColor:t.primary,descriptionColor:t.muted,selectedDescriptionColor:t.accent,onChange:(u)=>{s(u);let f=St[u];if(f)a(f)},onSelect:(u,f)=>{let p=f?.value;if(typeof p==="string"&&p!=="")c(p)}},void 0,!1,void 0,this)},void 0,!1,void 0,this)}var US=52,WS=22;var qS=D(()=>{Mt();vr();ke();Er();tt()});function El(e,t,r){let n=e?.google?.project_id??"";if(n!=="")return{project:n,source:"configuration"};let o=t?.project||r?.projectID||"",i=t?.project_source||r?.projectSource||"";return{project:o,source:i}}import{jsxDEV as st}from"@opentui/react/jsx-dev-runtime";function Tl(e){let{palette:t,title:r,tone:n="muted",children:o}=e,i=n==="success"?t.success:n==="warning"?t.warning:n==="error"?t.error:t.border;return st("box",{border:!0,borderStyle:"rounded",borderColor:i,title:r,titleColor:i,flexDirection:"column",flexShrink:0,paddingLeft:1,paddingRight:1,marginBottom:1,overflow:"hidden",children:o},void 0,!1,void 0,this)}function VS(e){let{palette:t,cfg:r,google:n,identity:o}=e;if(!n&&!o)return st(pl,{palette:t,label:"Checking Application Default Credentials\u2026"},void 0,!1,void 0,this);let i=mM(r,o),s=o?.user||n?.userEmail||"",{project:a,source:c}=El(r,o,n),l=o?.adc??n?.adcAvailable===!0,d=o?.iap??(r?.proxy.routes??[]).some((h)=>h.auth.type.toLowerCase()==="iap"),u=i.filter((h)=>h.inactive!==!0),f=u.some((h)=>h.ok===!0),p=u.some((h)=>h.ok===!1),m=f?"AVAILABLE":p?"UNAVAILABLE":u.length>0?"NOT PROBED":i.length>0?"not required":"not configured";return st(kt,{palette:t,title:"identity",scroll:!0,children:[st(Tl,{palette:t,title:"Google identity",tone:l?"success":"warning",children:[st(ko,{palette:t,label:"user",value:s||"(unknown)"},void 0,!1,void 0,this),st(ko,{palette:t,label:"project",value:a||"(unset)"},void 0,!1,void 0,this),st(ko,{palette:t,label:"source",value:c||"\u2014"},void 0,!1,void 0,this),st(ko,{palette:t,label:"ADC",value:l?"\u2713 available":"\u2717 missing",tone:l?"success":"error"},void 0,!1,void 0,this),st(ko,{palette:t,label:"gcloud",value:n?.gcloudInstalled?"\u2713 installed":"\u2717 not installed",tone:n?.gcloudInstalled?"success":"error"},void 0,!1,void 0,this)]},void 0,!0,void 0,this),st(Tl,{palette:t,title:`Service accounts (${i.length})`,tone:u.length===0?"muted":f?"success":"warning",children:[i.length===0?st("text",{fg:t.muted,wrapMode:"word",children:"none configured \u2014 identities come from config, never hard-coded"},void 0,!1,void 0,this):i.map((h)=>st("text",{fg:h.inactive?t.muted:h.ok===!0?t.success:h.ok===!1?t.error:t.text,wrapMode:"none",children:`${h.inactive?"\u2013":h.ok===!0?"\u2713":h.ok===!1?"\u2717":"\u25CB"} ${h.email}${h.inactive?" \u2014 inactive (route auth is none)":""}`},h.email,!1,void 0,this)),st("box",{height:1,flexShrink:0},void 0,!1,void 0,this),st(ko,{palette:t,label:"impersonation",value:m,tone:f?"success":u.length>0?"warning":"muted"},void 0,!1,void 0,this)]},void 0,!0,void 0,this),st(Tl,{palette:t,title:"IAP",tone:d?"success":"muted",children:[st("box",{flexDirection:"row",overflow:"hidden",flexShrink:0,children:st(Be,{palette:t,label:d?"routes present":"no IAP routes",tone:d?"success":"idle"},void 0,!1,void 0,this)},void 0,!1,void 0,this),d?st("text",{fg:t.muted,wrapMode:"word",children:"each route needs its own audience"},void 0,!1,void 0,this):null]},void 0,!0,void 0,this),!l?st(Tl,{palette:t,title:"ADC unavailable",tone:"warning",children:[st("text",{fg:t.text,wrapMode:"word",children:"Local services still start. Cloud identity, impersonation, and IAP need ADC."},void 0,!1,void 0,this),st("text",{fg:t.muted,wrapMode:"word",children:"/auth login suspends this screen and runs gcloud ADC login, then restores the TUI"},void 0,!1,void 0,this)]},void 0,!0,void 0,this):null]},void 0,!0,void 0,this)}function mM(e,t){let r=new Map((e?nu(e):[]).map((i)=>[i.email,i.active])),n=t?.service_account_status??{};return[...new Set([...r.keys(),...Object.keys(n)])].sort((i,s)=>i.localeCompare(s)).map((i)=>{let s=n[i]??"unknown",a=r.get(i)===!1;return{email:i,inactive:a,ok:a||s==="unknown"?void 0:s==="available"}})}var zS=D(()=>{jr();Yt();ke()});import{jsxDEV as M}from"@opentui/react/jsx-dev-runtime";function QS(e){let{palette:t,cfg:r,width:n,selectedTask:o=0}=e,i=ye();if(!r)return M(ze,{palette:t,title:"No configuration",body:"Create .devctl/config.yaml or run setup."},void 0,!1,void 0,this);let s=Math.max(gi+8,n-6-i.pad*2),a=s>=qw,c=a?Math.floor((s-JS)/2):s,l=Nw(r),d=Hw(r),u=jw(r),f=Bw(r),p=Uw(r);return M(kt,{palette:t,title:`config ${r.project.name||"unnamed"}`,children:[M(ut,{palette:t,items:Dw(r)},void 0,!1,void 0,this),M("box",{flexGrow:1,overflow:"hidden",children:M("scrollbox",{ref:e.scrollRef,focused:!1,stickyScroll:!1,scrollX:!1,style:Ue(t),children:M("box",{flexDirection:"column",overflow:"hidden",gap:XS,children:[M(kf,{wide:a,left:M(qr,{palette:t,title:"project",children:M(yi,{palette:t,facts:Iw(r,jt),width:c},void 0,!1,void 0,this)},void 0,!1,void 0,this),right:M(qr,{palette:t,title:"google",children:M(yi,{palette:t,facts:Mw(r),width:c},void 0,!1,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this),M(kf,{wide:a,left:M(qr,{palette:t,title:"runtime",children:M(yi,{palette:t,facts:Ow(r),width:c},void 0,!1,void 0,this)},void 0,!1,void 0,this),right:M(qr,{palette:t,title:"logs",children:M(yi,{palette:t,facts:$w(r),width:c},void 0,!1,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this),M(qr,{palette:t,title:`proxy ${r.proxy.enabled?"enabled":"disabled"}`,children:[M(yi,{palette:t,facts:Fw(r),width:s},void 0,!1,void 0,this),M(gM,{palette:t,routes:d,width:s},void 0,!1,void 0,this)]},void 0,!0,void 0,this),M(qr,{palette:t,title:`services ${l.length}`,children:M(hM,{palette:t,rows:l,width:s},void 0,!1,void 0,this)},void 0,!1,void 0,this),M(qr,{palette:t,title:`tasks ${p.length}`,children:M(YS,{palette:t,rows:p,empty:"no tasks",width:s,selected:o},void 0,!1,void 0,this)},void 0,!1,void 0,this),M(kf,{wide:a,left:M(qr,{palette:t,title:`profiles ${u.length}`,children:M(vM,{palette:t,rows:u,width:c},void 0,!1,void 0,this)},void 0,!1,void 0,this),right:M(qr,{palette:t,title:`templates ${f.length}`,children:M(YS,{palette:t,rows:f,empty:"no templates",width:c},void 0,!1,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this),M(qr,{palette:t,title:"extras",children:M(yi,{palette:t,facts:Gw(r),width:s},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this),M(Ut,{palette:t,backgroundColor:t.element,edge:"top",children:M(it,{palette:t,hints:[{key:"v",label:"validate/save buffer"},{key:"e",label:"open in $EDITOR"},{key:"/diff",label:"config sources"},{key:"/reload",label:"re-read .devctl after editing"},{key:"enter",label:"run task"},{key:"j/k",label:"scroll"}]},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function kf(e){let{wide:t,left:r,right:n}=e;if(!t)return M("box",{flexDirection:"column",overflow:"hidden",gap:XS,flexShrink:0,children:[r,n]},void 0,!0,void 0,this);return M("box",{flexDirection:"row",overflow:"hidden",gap:JS,flexShrink:0,children:[M("box",{flexGrow:1,flexBasis:0,overflow:"hidden",children:r},void 0,!1,void 0,this),M("box",{flexGrow:1,flexBasis:0,overflow:"hidden",children:n},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function qr(e){let{palette:t,title:r,children:n}=e;return M("box",{border:!0,borderStyle:"rounded",borderColor:t.border,title:r,titleColor:t.accent,paddingLeft:1,paddingRight:1,flexDirection:"column",overflow:"hidden",flexShrink:0,children:n},void 0,!1,void 0,this)}function yi(e){let{palette:t,facts:r,width:n}=e,o=Math.max(4,n-gi-4);return M("box",{flexDirection:"column",overflow:"hidden",flexShrink:0,children:r.map((i)=>M("box",{height:1,flexDirection:"row",overflow:"hidden",children:[M("box",{width:gi,flexShrink:0,overflow:"hidden",children:M("text",{fg:t.muted,wrapMode:"none",children:oe(i.label,gi)},void 0,!1,void 0,this)},void 0,!1,void 0,this),M("box",{width:o,flexShrink:0,overflow:"hidden",children:M("text",{fg:bM(t,i.tone),wrapMode:"none",children:oe(i.value,o)},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},i.label,!0,void 0,this))},void 0,!1,void 0,this)}function hM(e){let{palette:t,rows:r,width:n}=e;if(r.length===0)return M(Al,{palette:t,text:"no services"},void 0,!1,void 0,this);let o=Kw(r),i=Math.max(8,n-o-6);return M("box",{flexDirection:"column",overflow:"hidden",flexShrink:0,children:r.map((s)=>M("box",{height:1,flexDirection:"row",overflow:"hidden",children:[M("box",{width:o,flexShrink:0,overflow:"hidden",children:M("text",{fg:Ze(s.name,t),wrapMode:"none",children:oe(s.name,o)},void 0,!1,void 0,this)},void 0,!1,void 0,this),M("box",{width:i,flexShrink:0,overflow:"hidden",children:M("text",{fg:t.text,wrapMode:"none",children:_e(yM(s),i)},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},s.name,!0,void 0,this))},void 0,!1,void 0,this)}function gM(e){let{palette:t,routes:r,width:n}=e;if(r.length===0)return M(Al,{palette:t,text:"no routes"},void 0,!1,void 0,this);let o=Math.min(gi+4,Math.max(8,...r.map((s)=>s.name.length))),i=Math.max(8,n-o-6);return M("box",{flexDirection:"column",overflow:"hidden",flexShrink:0,children:r.map((s)=>M("box",{height:1,flexDirection:"row",overflow:"hidden",children:[M("box",{width:o,flexShrink:0,overflow:"hidden",children:M("text",{fg:t.primary,wrapMode:"none",children:oe(s.name,o)},void 0,!1,void 0,this)},void 0,!1,void 0,this),M("box",{width:i,flexShrink:0,overflow:"hidden",children:M("text",{fg:t.text,wrapMode:"none",children:_e(xM(s),i)},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},`${s.name}-${s.match}`,!0,void 0,this))},void 0,!1,void 0,this)}function vM(e){let{palette:t,rows:r,width:n}=e;if(r.length===0)return M(Al,{palette:t,text:"no profiles"},void 0,!1,void 0,this);let o=Math.min(12,Math.max(8,...r.map((s)=>s.name.length))),i=Math.max(8,n-o-6);return M("box",{flexDirection:"column",overflow:"hidden",flexShrink:0,children:r.map((s)=>M("box",{height:1,flexDirection:"row",overflow:"hidden",children:[M("box",{width:o,flexShrink:0,overflow:"hidden",children:M("text",{fg:t.primary,wrapMode:"none",children:oe(s.name,o)},void 0,!1,void 0,this)},void 0,!1,void 0,this),M("box",{width:i,flexShrink:0,overflow:"hidden",children:M("text",{fg:t.text,wrapMode:"none",children:_e(s.env==="\u2014"?s.services:`${s.services} env ${s.env}`,i)},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},s.name,!0,void 0,this))},void 0,!1,void 0,this)}function YS(e){let{palette:t,rows:r,empty:n,width:o,selected:i}=e;if(r.length===0)return M(Al,{palette:t,text:n},void 0,!1,void 0,this);let s=Math.min(14,Math.max(8,...r.map((c)=>c.name.length))),a=Math.max(8,o-s-6);return M("box",{flexDirection:"column",overflow:"hidden",flexShrink:0,children:r.map((c,l)=>{let d=i===l;return M("box",{height:1,flexDirection:"row",overflow:"hidden",backgroundColor:d?t.highlight:void 0,children:[M("box",{width:s,flexShrink:0,overflow:"hidden",children:M("text",{fg:d?t.primary:t.text,wrapMode:"none",children:oe(c.name,s)},void 0,!1,void 0,this)},void 0,!1,void 0,this),M("box",{width:a,flexShrink:0,overflow:"hidden",children:M("text",{fg:d?t.accent:t.muted,wrapMode:"none",children:_e(c.summary,a)},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},c.name,!0,void 0,this)})},void 0,!1,void 0,this)}function Al(e){return M("box",{height:1,overflow:"hidden",children:M("text",{fg:e.palette.muted,wrapMode:"none",children:e.text},void 0,!1,void 0,this)},void 0,!1,void 0,this)}function yM(e){let t=e.extends==="\u2014"?"":` ${e.extends}`;return` ${e.ports} ${e.identity} ${e.health} ${e.restart} \u2190 ${e.depends}${t}`}function xM(e){return` ${e.auth} ${e.identity} ${e.match} \u2192 ${e.upstream}`}function bM(e,t){if(t==="success")return e.success;if(t==="warning")return e.warning;if(t==="error")return e.error;if(t==="muted")return e.muted;return e.text}var JS=1,XS=1;var ZS=D(()=>{Yt();mf();Mt();Ot();ke();tt()});import{jsxDEV as Ft}from"@opentui/react/jsx-dev-runtime";function ek(e){let{palette:t,credentials:r}=e;if(!r)return Ft(pl,{palette:t,label:"Reading credential store\u2026"},void 0,!1,void 0,this);let n=r.entries.filter((i)=>i.valid).length,o=r.entries.length-n;return Ft(kt,{palette:t,title:"credentials",scroll:!0,children:[Ft("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,children:[Ft(Be,{palette:t,label:`backend: ${r.backend}`,tone:"info"},void 0,!1,void 0,this),r.entries.length>0?Ft(Be,{palette:t,label:`${n} valid`,tone:"success"},void 0,!1,void 0,this):null,o>0?Ft(Be,{palette:t,label:`${o} expired`,tone:"warning"},void 0,!1,void 0,this):null]},void 0,!0,void 0,this),Ft("text",{fg:t.muted,wrapMode:"word",children:"Tokens stay in the OS keychain or ~/.devctl/credentials (0600). They are never shown here."},void 0,!1,void 0,this),Ft("box",{height:1,flexShrink:0},void 0,!1,void 0,this),r.entries.length===0?Ft("text",{fg:t.muted,children:"no cached credentials"},void 0,!1,void 0,this):Ft("box",{flexDirection:"column",overflow:"hidden",gap:1,children:r.entries.map((i)=>Ft("box",{border:!0,borderStyle:"rounded",borderColor:i.valid?t.success:t.warning,title:i.identity,titleColor:i.valid?t.success:t.warning,flexDirection:"column",flexShrink:0,paddingLeft:1,paddingRight:1,overflow:"hidden",children:[Ft("text",{wrapMode:"none",children:[Ft("span",{fg:i.valid?t.success:t.warning,children:i.valid?"\u2713 valid":"\u2717 expired"},void 0,!1,void 0,this),Ft("span",{fg:t.muted,children:` \xB7 expires ${i.expires_at}`},void 0,!1,void 0,this)]},void 0,!0,void 0,this),Ft("text",{fg:t.muted,wrapMode:"none",children:`audience: ${i.audience||"(none)"}`},void 0,!1,void 0,this)]},`${i.identity}|${i.audience}`,!0,void 0,this))},void 0,!1,void 0,this),Ft(ul,{palette:t,title:"Actions",body:"Use /refresh to mint again or auth refresh on the CLI. Invalidate clears the store without printing tokens.",hint:"/refresh /auth"},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}var tk=D(()=>{Yt();ke()});import{jsxDEV as Ll}from"@opentui/react/jsx-dev-runtime";function xi(e){let t=e.newer>0?`g jump to latest \xB7 ${e.newer} new`:"g jump to latest",r=Math.min(e.width,t.length+4);return Ll("box",{position:"absolute",left:Math.max(0,Math.floor((e.width-r)/2)),bottom:e.bottom??2,width:r,height:3,border:!0,borderStyle:"rounded",borderColor:e.palette.warning,backgroundColor:e.palette.panel,alignItems:"center",justifyContent:"center",onMouseDown:e.onJump,children:Ll("text",{wrapMode:"none",children:[Ll("span",{fg:e.palette.primary,children:"g"},void 0,!1,void 0,this),Ll("span",{fg:e.palette.text,children:t.slice(1)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this)}var rk=()=>{};import{jsxDEV as Fs}from"@opentui/react/jsx-dev-runtime";function bi(e){let{palette:t,logs:r,names:n,service:o,errorOnly:i,width:s,onService:a,onToggleErrors:c,facets:l}=e,d=l?Wx(n,l):Ux(n,r),u=s<80,f=d.map((v)=>{let x=v.name===""?"all":v.name;return{id:x,label:u?x:`${x} \xB7 ${v.count}`,color:Ze(v.name,t)}}),p=Math.max(0,d.findIndex((v)=>v.name===o)),m=i?"ERROR+":u?"lvls":"all levels",h=Math.max(ai(f[p]?.label??"all"),s-ai(m));return Fs(Ut,{palette:t,backgroundColor:t.element,children:Fs("box",{height:1,flexDirection:"row",overflow:"hidden",backgroundColor:t.element,children:[Fs("box",{flexGrow:1,overflow:"hidden",children:Fs(dl,{palette:t,items:f,active:p,width:h,emphasis:"fill",onPick:(v)=>{let x=d[v];if(x)a(x.name)}},void 0,!1,void 0,this)},void 0,!1,void 0,this),Fs(Be,{palette:t,label:m,tone:i?"error":"muted",onMouseDown:c},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this)}var nk=D(()=>{gr();In();ke();tt()});import{jsxDEV as wM}from"@opentui/react/jsx-dev-runtime";function Ns(e){let t=Math.min(e.total,e.start+e.count),r=Math.max(0,e.start),n=Math.max(0,e.total-t);return wM(ut,{palette:e.palette,items:[{text:`view ${e.start+1}\u2013${t} of ${e.total}`,tone:"primary"},{text:r>0?`\u2191 ${r} older`:"start of history",tone:r>0?"info":"idle"},{text:n>0?`\u2193 ${n} newer`:"at latest",tone:n>0?"warning":"success"}],hints:[{key:"pgup/pgdn",label:"move history window"},{key:"g",label:"latest"}]},void 0,!1,void 0,this)}var ok=D(()=>{ke()});import{useEffect as Cf,useRef as ik}from"react";import{jsxDEV as Ye}from"@opentui/react/jsx-dev-runtime";function wi(e){let{palette:t,logs:r,limit:n,width:o=80,followTick:i=0,focused:s=!1,wrapMode:a="clip",selected:c=-1,follow:l=!0,showTimestamps:d=!0,showMeta:u=!0,onPick:f,onLeaveLatest:p,viewStart:m=0,search:h="",regex:v=!1}=e,x=ik(null),g=ik(""),R=n===void 0?r:r.slice(-n),_=[...new Set(r.map((k)=>k.service))],S=$x(o,_),y=Ox({width:o,serviceWidth:S,showTimestamps:d,showMeta:u}),b=R[R.length-1],w=b===void 0?"empty":String(b.seq);return Cf(()=>{let k=x.current;if(!k)return;if(k.stickyScroll=l,l)k.stickyStart="bottom",k.scrollTo({x:k.scrollLeft,y:Math.max(0,k.scrollHeight)})},[l,i,w]),Cf(()=>{let k=`${m}:${c}:${a}`;if(g.current===k)return;if(g.current=k,l&&(c<0||c>=R.length-1))return;let L=x.current;if(!L||c<0)return;let O=`log-row-${c}`,F=requestAnimationFrame(()=>{L.stickyScroll=!1,L.scrollChildIntoView(O)});return()=>cancelAnimationFrame(F)},[l,c,R.length,m,a]),Cf(()=>{if(!l||!p)return;let k=!1,L=0,O=setInterval(()=>{let F=x.current;if(!F)return;let Z=F.viewport.height;if(Z<=0)return;let W=F.scrollHeight;if(F.scrollTop+Z>=W-kM){k=!0,L=W;return}if(W>L){F.stickyScroll=!0,F.stickyStart="bottom",F.scrollTo({x:F.scrollLeft,y:Math.max(0,W)}),L=W;return}if(k)p()},SM);return()=>{clearInterval(O)}},[l,i,p]),Ye("box",{flexGrow:1,height:"100%",overflow:"hidden",children:Ye("scrollbox",{ref:x,focused:s,stickyScroll:l,stickyStart:"bottom",scrollX:!1,style:{rootOptions:{flexGrow:1,height:"100%",overflow:"hidden",backgroundColor:t.panel},viewportOptions:{backgroundColor:t.panel},contentOptions:{backgroundColor:t.panel},scrollbarOptions:{trackOptions:{foregroundColor:t.primary,backgroundColor:t.element}}},children:Ye("box",{flexDirection:"column",overflow:"hidden",children:R.map((k,L)=>Ye(CM,{id:`log-row-${L}`,palette:t,event:k,serviceWidth:S,msgWidth:y,expanded:Ix(a,L===c),active:L===c,showTimestamps:d,showMeta:u,onPick:f?()=>f(L):void 0,search:h,regex:v},`${k.timestamp}-${k.service}-${L}`,!1,void 0,this))},void 0,!1,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this)}function CM(e){let{id:t,palette:r,event:n,serviceWidth:o,msgWidth:i,expanded:s,active:a,showTimestamps:c=!0,showMeta:l=!0,onPick:d,search:u="",regex:f=!1}=e,p=Dx(Jc(n.message,i),i,s);return Ye("box",{id:t,flexShrink:0,flexDirection:"column",overflow:"hidden",backgroundColor:a?r.highlight:void 0,onMouseDown:d,children:p.visible.map((m,h)=>Ye("box",{height:1,flexDirection:"row",overflow:"hidden",children:[c?Ye("box",{width:qc,flexShrink:0,overflow:"hidden",children:Ye("text",{fg:r.muted,children:h===0?n.timestamp.slice(11,19):""},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,Ye("box",{width:o,flexShrink:0,overflow:"hidden",children:Ye("text",{fg:h===0?Ze(n.service,r):r.muted,wrapMode:"none",children:h===0?oe(n.service,o):""},void 0,!1,void 0,this)},void 0,!1,void 0,this),Ye("box",{width:Es,flexShrink:0,overflow:"hidden",children:Ye("text",{children:" "},void 0,!1,void 0,this)},void 0,!1,void 0,this),Ye("box",{width:Vc,flexShrink:0,overflow:"hidden",children:Ye("text",{fg:h===0?et(r,n.level):r.muted,children:h===0?zc(String(n.level)):""},void 0,!1,void 0,this)},void 0,!1,void 0,this),l?Ye("box",{width:Kc,flexShrink:0,overflow:"hidden",children:Ye("text",{fg:r.muted,children:h===0&&n.source?oe(n.source,Kc):""},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,Ye("box",{width:Es,flexShrink:0,overflow:"hidden",children:Ye("text",{fg:r.muted,children:h===0?" ":"\u2502"},void 0,!1,void 0,this)},void 0,!1,void 0,this),Ye("box",{flexGrow:1,overflow:"hidden",children:Ye(PM,{palette:r,level:n.level,text:m,mark:h===0?p.mark:"",search:u,regex:f},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},`${n.timestamp}-${h}`,!0,void 0,this))},void 0,!1,void 0,this)}function PM(e){let{palette:t,level:r,text:n,mark:o,search:i="",regex:s=!1}=e;return Ye("text",{wrapMode:"none",children:[Mx(n,i,s).map((a,c)=>Ye("span",{fg:cx(t,r,a.kind),children:a.text},`${a.kind}-${c}`,!1,void 0,this)),o===""?null:Ye("span",{fg:t.muted,children:o},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}var SM=200,kM=2;var sk=D(()=>{Ot();gr();tt()});var Pf=D(()=>{rk();nk();ok();sk()});import{useRef as RM}from"react";import{jsxDEV as Ie}from"@opentui/react/jsx-dev-runtime";function Dl(e){let{palette:t,names:r,snap:n,selected:o,checked:i,width:s,onOpen:a,onSelectIndex:c,onToggle:l}=e,d=ye(),u=RM(!1),f=new Set(i),p=Cp(s),m=kp(s),h=Sp(s),v=Sx(s);return Ie("box",{flexDirection:"column",flexGrow:1,overflow:"hidden",children:[Ie("box",{height:1,flexDirection:"row",overflow:"hidden",children:[Ie(Gt,{width:2,fg:t.muted,text:""},void 0,!1,void 0,this),Ie(Gt,{width:4,fg:t.accent,text:"sel"},void 0,!1,void 0,this),Ie(Gt,{width:2,fg:t.muted,text:""},void 0,!1,void 0,this),Ie(Gt,{width:v,fg:t.muted,text:"name"},void 0,!1,void 0,this),Ie(Gt,{width:$t,fg:t.muted,text:""},void 0,!1,void 0,this),Ie(Gt,{width:Tr,fg:t.muted,text:"state"},void 0,!1,void 0,this),h?Ie(Gt,{width:Ur,fg:t.muted,text:"health"},void 0,!1,void 0,this):null,m?Ie(Gt,{width:Oc,fg:t.muted,text:"port"},void 0,!1,void 0,this):null,p?Ie(Gt,{width:sn,fg:t.muted,text:"pid"},void 0,!1,void 0,this):null]},void 0,!0,void 0,this),r.map((x,g)=>{let R=n?.services[x],_=tr(R),S=R?.health??"UNKNOWN",y=f.has(x),b=g===o,w=y?"[x]":"[ ]",k=b?"\u203A":" ";return Ie("box",{height:d.rowH,flexDirection:"row",overflow:"hidden",backgroundColor:b?t.highlight:y?t.element:void 0,onMouseDown:()=>{if(u.current)return;if(b){a(x);return}c(g)},children:[Ie("box",{width:2,flexShrink:0,children:Ie("text",{fg:t.primary,children:k},void 0,!1,void 0,this)},void 0,!1,void 0,this),Ie("box",{width:4,flexShrink:0,onMouseDown:()=>{u.current=!0,l?.(x),setTimeout(()=>{u.current=!1},0)},children:Ie("text",{fg:_M(t,y,b),children:w},void 0,!1,void 0,this)},void 0,!1,void 0,this),Ie("box",{width:2,flexShrink:0,children:Ie("text",{fg:et(t,_),children:hr(_)},void 0,!1,void 0,this)},void 0,!1,void 0,this),Ie(Gt,{width:v,fg:b?t.primary:Ze(x,t),text:oe(x,v)},void 0,!1,void 0,this),Ie(Gt,{width:$t,fg:t.muted,text:""},void 0,!1,void 0,this),Ie(Gt,{width:Tr,fg:et(t,_),text:_},void 0,!1,void 0,this),h?Ie(Gt,{width:Ur,fg:et(t,S),text:S},void 0,!1,void 0,this):null,m?Ie(Gt,{width:Oc,fg:t.text,text:vo(R)},void 0,!1,void 0,this):null,p?Ie(Gt,{width:sn,fg:t.text,text:R?.pid?String(R.pid):""},void 0,!1,void 0,this):null]},x,!0,void 0,this)})]},void 0,!0,void 0,this)}function Il(e){let{palette:t,checked:r}=e,n=r.length;if(n>0)return Ie(ut,{palette:t,items:[{text:`${n} selected`,tone:"primary"}],hints:[{key:"space",label:"unmark"},{key:"n",label:"start these"},{key:"x",label:"stop these"}]},void 0,!1,void 0,this);return null}function _M(e,t,r){if(t)return e.success;if(r)return e.primary;return e.muted}function Gt(e){return Ie("box",{width:e.width,flexShrink:0,overflow:"hidden",children:Ie("text",{fg:e.fg,wrapMode:"none",children:e.text},void 0,!1,void 0,this)},void 0,!1,void 0,this)}var Rf=D(()=>{Mt();Ot();At();ke();tt()});import{jsxDEV as fe}from"@opentui/react/jsx-dev-runtime";function ck(e){let{palette:t,cfg:r,snap:n,logs:o,names:i,selected:s,selectedLog:a=-1,checked:c,width:l,followTick:d,logService:u,logSources:f,errorOnly:p,showSystemLogs:m,onOpen:h,onSelectIndex:v,onToggle:x,onFilterService:g,onToggleErrors:R,onShowErrors:_,wrapMode:S="clip",view:y,follow:b=!0,onLeaveLatest:w,onPickLog:k,viewStart:L=0,viewTotal:O,newer:F=0,onJumpLatest:Z,facets:W,leftover:C,search:j="",regex:B=!1}=e,z=ye();if(!r||i.length===0)return fe(ze,{palette:t,title:"No services configured",body:"Add services to .devctl/config.yaml or finish setup.",hint:"open the setup screen with /setup"},void 0,!1,void 0,this);let de=l<ci,Ee=Hc(n),Ne=al(n,i),ne=i.filter((ge)=>n?.services[ge]?.state==="FAILED").length,H=Bx(o),ae=Ee?Px(C,n?.session_id):void 0,X=Nc(l,i,de),nt=Math.max(24,de?l-4:l-X-4),ft=m?o:o.filter((ge)=>!si(ge.source)),ie=xo(o,{service:u,errorOnly:p,systemLogs:m,search:j,regex:B}),Le=y??ie,Me=O??ie.length,Ke=Math.min(Me,L+Le.length),gt=Me===0?"empty":`${L+1}\u2013${Ke} of ${Me}`,Xe=u===""?"all":u,Ct=[...Ne.running>0?[{text:`${Ne.running}/${i.length}`,tone:"success"}]:[],...ne>0?[{text:`${ne} failed`,tone:"error"}]:[],...c.length>0?[{text:`${c.length} selected`,tone:"primary"}]:[],...H>0?[{text:`${H} errors`,tone:"error",onMouseDown:_}]:[]];return fe("box",{flexGrow:1,flexDirection:de?"column":"row",overflow:"hidden",children:[fe("box",{flexGrow:de?1:0,flexShrink:0,minWidth:de?void 0:X,width:de?void 0:X,border:!0,borderStyle:"rounded",borderColor:Ee?t.border:t.borderActive,backgroundColor:t.panel,title:"services",titleColor:t.primary,flexDirection:"column",overflow:"hidden",children:[Ct.length>0?fe(ut,{palette:t,items:Ct},void 0,!1,void 0,this):null,fe(Il,{palette:t,checked:c},void 0,!1,void 0,this),fe("box",{flexGrow:1,paddingLeft:z.pad,paddingRight:z.pad,overflow:"hidden",children:fe(Dl,{palette:t,names:i,snap:n,selected:s,checked:c,width:Fc(X,z.pad),onOpen:h,onSelectIndex:v,onToggle:x},void 0,!1,void 0,this)},void 0,!1,void 0,this),fe(LM,{palette:t,names:i,snap:n,width:X,onOpen:h},void 0,!1,void 0,this),ae?fe(IM,{palette:t,leftover:ae,width:X},void 0,!1,void 0,this):null]},void 0,!0,void 0,this),fe("box",{position:"relative",flexGrow:2,minWidth:de?void 0:32,minHeight:de?8:void 0,border:!0,borderStyle:"rounded",borderColor:t.border,backgroundColor:t.panel,title:`logs \xB7 ${Xe} \xB7 ${gt}`,titleColor:t.primary,overflow:"hidden",flexDirection:"column",children:[fe(bi,{palette:t,logs:ft,names:f??i,service:u,errorOnly:p,width:nt,onService:g,onToggleErrors:R,facets:W},void 0,!1,void 0,this),Me>Le.length?fe(Ns,{palette:t,start:L,count:Le.length,total:Me},void 0,!1,void 0,this):null,ie.length===0?fe(ze,{palette:t,title:Ee?"Waiting for logs":o.length===0?"No events yet":"No events in this filter",body:Ee?"Start services to stream output here. Later starts keep earlier logs.":o.length===0?"New lines appear as services write output.":"Click All or another service chip, or turn off ERROR+.",hint:"\u2190 \u2192 cycle service e errors g latest"},void 0,!1,void 0,this):fe(wi,{palette:t,logs:Le,width:nt,followTick:d,focused:!1,wrapMode:S,selected:a,follow:b,onLeaveLatest:w,onPick:k,viewStart:L,search:j,regex:B},d,!1,void 0,this),!b?fe(xi,{palette:t,width:nt,newer:F,bottom:0,onJump:Z},void 0,!1,void 0,this):null]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)}function TM(e){if(!e)return!1;return e.state===dt||e.health===Oe||e.state===fr||e.restarts>0||e.last_error!==""}function ak(e){if(e.state===dt)return 0;if(e.health===Oe)return 1;if(e.state===fr)return 2;return 3}function AM(e){if(e.last_error!=="")return e.last_error;if(e.restarts>0)return`restarted ${e.restarts}x, no error recorded`;return e.health===Oe?"failing health check":e.state.toLowerCase()}function LM(e){let{palette:t,names:r,snap:n,width:o,onOpen:i}=e,s=r.map((d)=>({name:d,rt:n?.services[d]})).filter((d)=>TM(d.rt)).sort((d,u)=>ak(d.rt)-ak(u.rt));if(s.length===0)return null;let a=s.slice(0,EM),c=s.length-a.length,l=Math.max(8,o-_f-4);return fe("box",{flexDirection:"column",flexShrink:0,paddingLeft:1,paddingRight:1,overflow:"hidden",children:[fe("box",{height:1,overflow:"hidden",children:fe("text",{fg:t.error,wrapMode:"none",children:`issues (${s.length})`},void 0,!1,void 0,this)},void 0,!1,void 0,this),a.map(({name:d,rt:u})=>{let f=tr(u);return fe("box",{height:1,flexDirection:"row",overflow:"hidden",onMouseDown:()=>i(d),children:[fe("box",{width:2,flexShrink:0,children:fe("text",{fg:et(t,f),children:hr(f)},void 0,!1,void 0,this)},void 0,!1,void 0,this),fe("box",{width:_f,flexShrink:0,overflow:"hidden",children:fe("text",{fg:Ze(d,t),wrapMode:"none",children:oe(d,_f)},void 0,!1,void 0,this)},void 0,!1,void 0,this),fe("box",{width:l,flexShrink:0,overflow:"hidden",children:fe("text",{fg:t.muted,wrapMode:"none",children:_e(AM(u),l)},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},d,!0,void 0,this)}),c>0?fe("text",{fg:t.muted,wrapMode:"none",children:`+${c} more \u2014 open a service to see details`},void 0,!1,void 0,this):null]},void 0,!0,void 0,this)}function IM(e){let{palette:t,leftover:r,width:n}=e,o=r.processes.slice(0,DM),i=r.processes.length-o.length,s=Math.max(8,n-Ef-4);return fe("box",{flexDirection:"column",flexShrink:0,paddingLeft:1,paddingRight:1,overflow:"hidden",children:[fe("box",{height:1,overflow:"hidden",children:fe("text",{fg:t.warning,wrapMode:"none",children:"last session"},void 0,!1,void 0,this)},void 0,!1,void 0,this),fe("text",{fg:t.muted,wrapMode:"none",children:_e(`${r.session_id} ${r.profile||"(none)"}`,Math.max(8,n-4))},void 0,!1,void 0,this),o.map((a)=>fe("box",{height:1,flexDirection:"row",overflow:"hidden",children:[fe("box",{width:Ef,flexShrink:0,overflow:"hidden",children:fe("text",{fg:t.text,wrapMode:"none",children:oe(a.name,Ef)},void 0,!1,void 0,this)},void 0,!1,void 0,this),fe("box",{width:s,flexShrink:0,overflow:"hidden",children:fe("text",{fg:t.muted,wrapMode:"none",children:_e(`pid ${a.pid} leftover`,s)},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},`${a.name}-${a.pid}`,!0,void 0,this)),i>0?fe("text",{fg:t.muted,wrapMode:"none",children:`+${i} more leftover pids`},void 0,!1,void 0,this):null]},void 0,!0,void 0,this)}var EM=4,_f=14,DM=4,Ef=14;var lk=D(()=>{Tt();Yt();Mt();vr();Ot();gr();At();ll();ke();tt();Pf();Rf()});import MM from"cli-spinners";import{useEffect as OM,useState as $M}from"react";import{jsxDEV as re}from"@opentui/react/jsx-dev-runtime";function dk(e){let{palette:t,report:r,loading:n,progress:o,error:i,selected:s,onPick:a,onReload:c}=e,l=pi(s,Af);if(n)return re(FM,{palette:t,progress:o},void 0,!1,void 0,this);if(i)return re(zb,{palette:t,title:"Doctor failed",body:i},void 0,!1,void 0,this);if(!r)return re(ze,{palette:t,title:"Doctor",body:"Diagnostics have not run yet.",hint:"opening this screen runs doctor"},void 0,!1,void 0,this);let d=r.checks.filter((v)=>v.severity==="ok").length,u=r.checks.filter((v)=>v.severity==="warn").length,f=r.checks.filter((v)=>v.severity==="error").length,p=r.checks.length,m=ii(p>0?d/p:0),h=f>0?t.error:u>0?t.warning:t.success;return re(kt,{palette:t,title:`doctor ${r.issues} issue(s)`,children:[re("box",{height:4,flexDirection:"column",flexShrink:0,alignItems:"center",justifyContent:"center",overflow:"hidden",border:!0,borderStyle:"rounded",borderColor:t.primary,backgroundColor:t.element,onMouseDown:c,children:[re("text",{wrapMode:"none",children:[re("span",{fg:t.inverse,bg:t.primary,children:" r "},void 0,!1,void 0,this),re("span",{fg:t.primary,children:" RUN DOCTOR AGAIN"},void 0,!1,void 0,this)]},void 0,!0,void 0,this),re("text",{fg:t.muted,wrapMode:"none",children:"results stay cached until you refresh"},void 0,!1,void 0,this)]},void 0,!0,void 0,this),re("box",{height:1,flexShrink:0},void 0,!1,void 0,this),re("box",{height:1,flexShrink:0,overflow:"hidden",children:re("text",{wrapMode:"none",children:[re("span",{fg:h,children:`[${m}]`},void 0,!1,void 0,this),re("span",{fg:t.success,children:` ${d} ok`},void 0,!1,void 0,this),u>0?re("span",{fg:t.warning,children:` \xB7 ${u} warn`},void 0,!1,void 0,this):null,f>0?re("span",{fg:t.error,children:` \xB7 ${f} error`},void 0,!1,void 0,this):null,u===0&&f===0?re("span",{fg:t.muted,children:" \xB7 all clear"},void 0,!1,void 0,this):null]},void 0,!0,void 0,this)},void 0,!1,void 0,this),re("box",{height:1,flexShrink:0},void 0,!1,void 0,this),re("scrollbox",{ref:l,focused:!1,stickyScroll:!1,scrollX:!1,style:Ue(t),children:re("box",{flexDirection:"column",overflow:"hidden",gap:1,children:r.checks.map((v,x)=>re(NM,{palette:t,check:v,index:x,active:x===s,onPick:a},`${v.name}-${x}`,!1,void 0,this))},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function FM({palette:e,progress:t}){let[r,n]=$M(0);return OM(()=>{let o=setInterval(()=>n((i)=>(i+1)%Tf.frames.length),Tf.interval);return()=>clearInterval(o)},[]),re("box",{flexGrow:1,alignItems:"center",justifyContent:"center",overflow:"hidden",children:re("box",{width:"80%",maxWidth:52,height:10,flexShrink:1,border:!0,borderStyle:"rounded",borderColor:e.borderActive,backgroundColor:e.panel,flexDirection:"column",alignItems:"center",justifyContent:"center",overflow:"hidden",children:[re("text",{wrapMode:"none",children:[re("span",{fg:e.primary,children:`${Tf.frames[r]} `},void 0,!1,void 0,this),re("span",{fg:e.text,children:"devctl doctor"},void 0,!1,void 0,this)]},void 0,!0,void 0,this),re("box",{height:1,flexShrink:0},void 0,!1,void 0,this),re("text",{fg:e.text,wrapMode:"none",children:`Checking ${t.active}`},void 0,!1,void 0,this),re("box",{height:1,flexShrink:0},void 0,!1,void 0,this),t.checks.slice(-4).map((o,i)=>re("text",{wrapMode:"none",children:[re("span",{fg:et(e,o.severity==="error"?"ERROR":o.severity==="warn"?"WARN":"OK"),children:`${hr(o.severity==="error"?"ERROR":o.severity==="warn"?"WARN":"OK")} `},void 0,!1,void 0,this),re("span",{fg:e.muted,children:o.name},void 0,!1,void 0,this)]},`${o.name}-${i}`,!0,void 0,this)),t.checks.length===0?re("text",{fg:e.muted,children:"Waiting for the first result\u2026"},void 0,!1,void 0,this):null]},void 0,!0,void 0,this)},void 0,!1,void 0,this)}function NM(e){let{palette:t,check:r,index:n,active:o,onPick:i}=e,s=r.severity==="error"?"ERROR":r.severity==="warn"?"WARN":"OK";if(r.severity==="ok")return re("box",{id:`${Af}-${n}`,flexDirection:"row",flexShrink:0,paddingLeft:1,overflow:"hidden",backgroundColor:o?t.highlight:void 0,onMouseDown:()=>i(n),children:re("text",{wrapMode:"none",children:[re("span",{fg:t.primary,children:o?"\u203A ":" "},void 0,!1,void 0,this),re("span",{fg:et(t,s),children:hr(s)},void 0,!1,void 0,this),re("span",{fg:o?t.primary:t.text,children:` ${r.name}`},void 0,!1,void 0,this),re("span",{fg:t.muted,children:r.message?` \xB7 ${r.message}`:""},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this);let c=r.severity==="error"?t.error:t.warning;return re("box",{id:`${Af}-${n}`,border:!0,borderStyle:"rounded",borderColor:o?t.borderActive:c,title:r.name,titleColor:c,flexDirection:"column",flexShrink:0,paddingLeft:1,paddingRight:1,overflow:"hidden",backgroundColor:o?t.highlight:void 0,onMouseDown:()=>i(n),children:[re("text",{wrapMode:"word",children:[re("span",{fg:et(t,s),children:`${hr(s)} `},void 0,!1,void 0,this),re("span",{fg:t.text,children:r.message},void 0,!1,void 0,this)]},void 0,!0,void 0,this),r.hint?re("text",{fg:t.info,wrapMode:"word",children:`\u2192 ${r.hint}`},void 0,!1,void 0,this):null,o&&r.action?.kind==="free-port"?re("text",{fg:t.primary,wrapMode:"none",children:`enter stop ${r.action.holder.command} (pid ${r.action.holder.pid})`},void 0,!1,void 0,this):null]},void 0,!0,void 0,this)}var Af="doctor-row",Tf;var uk=D(()=>{Yt();Ot();ke();tt();Tf=MM.bouncingBall});import{jsxDEV as We,Fragment as HM}from"@opentui/react/jsx-dev-runtime";function fk(e){let{palette:t,logs:r,names:n,logSources:o,service:i,errorOnly:s,showSystemLogs:a,search:c,searchFocused:l,followTick:d,width:u,onSearch:f,onService:p,onToggleErrors:m,onSelect:h,wrapMode:v="focus",selected:x=-1,follow:g=!0,newer:R=0,viewStart:_=0,viewTotal:S,onLeaveLatest:y,onJumpLatest:b,view:w,fullscreen:k=!1,facets:L,split:O=!1,splitFocus:F=0,serviceB:Z="",viewB:W,selectedB:C=-1,followB:j=!0,newerB:B=0,viewStartB:z=0,viewTotalB:de,onServiceB:Ee,onSelectB:Ne,onLeaveLatestB:ne,onFocusPane:H}=e,ae=ye(),X=Rp(u,ae.pad,k),nt=a?r:r.filter((ge)=>!si(ge.source)),ft=xo(r,{service:i,services:e.services,errorOnly:s,search:c,regex:e.regex,source:e.source,systemLogs:a}),ie=w??ft,Le=S??ft.length,Me=Math.min(Le,_+ie.length),Ke=Le===0?"empty":`${_+1}\u2013${Me} of ${Le}`,gt=e.services??[],Xe=gt.length>0?gt.join(","):i===""?"all services":i,Ct=[...g?[]:[{text:R>0?`${O?"left ":""}pinned \xB7 +${R} new`:`${O?"left ":""}pinned`,tone:"warning"}],...O&&!j?[{text:B>0?`right pinned \xB7 +${B} new`:"right pinned",tone:"warning"}]:[],...e.source?[{text:`src ${e.source}`,tone:"info"}]:[],...e.regex?[{text:"regex",tone:"accent"}]:[],...c===""?[]:[{text:`search ${c}`,tone:"accent"}],...O?[{text:F===0?"left pane":"right pane",tone:"accent"}]:[]];if(O){let ge=Math.max(20,Math.floor(u/2)),vt=Math.max(20,u-ge),at=W??[],yt=de??at.length;return We("box",{flexGrow:1,flexDirection:"column",overflow:"hidden",children:[l?We("box",{height:1,paddingLeft:1,backgroundColor:t.highlight,overflow:"hidden",children:We("input",{focused:!0,value:c,placeholder:"search messages or service names",onInput:f,backgroundColor:t.highlight,focusedBackgroundColor:t.highlight,textColor:t.text,cursorColor:t.primary},void 0,!1,void 0,this)},void 0,!1,void 0,this):We(ut,{palette:t,items:Ct},void 0,!1,void 0,this),We("box",{flexGrow:1,flexDirection:"row",overflow:"hidden",children:[We(pk,{palette:t,names:o??n,logs:r,service:i,errorOnly:s,showSystemLogs:a,search:c,regex:e.regex,source:e.source,width:ge,shown:ie,shownTotal:Le,selected:x,follow:g,newer:R,viewStart:_,wrapMode:v,showTimestamps:e.showTimestamps!==!1,showMeta:e.showMeta!==!1,followTick:d,facets:L,focused:F===0,onService:p,onToggleErrors:m,onSelect:h,onLeaveLatest:y,onFocus:()=>H?.(0)},void 0,!1,void 0,this),We(pk,{palette:t,names:o??n,logs:r,service:Z,errorOnly:s,showSystemLogs:a,search:c,regex:e.regex,source:e.source,width:vt,shown:at,shownTotal:yt,selected:C,follow:j,newer:B,viewStart:z,wrapMode:v,showTimestamps:e.showTimestamps!==!1,showMeta:e.showMeta!==!1,followTick:d,facets:L,focused:F===1,onService:Ee??p,onToggleErrors:m,onSelect:Ne,onLeaveLatest:ne,onFocus:()=>H?.(1)},void 0,!1,void 0,this)]},void 0,!0,void 0,this),!g||!j?We(xi,{palette:t,width:u,newer:Math.max(R,B),onJump:b},void 0,!1,void 0,this):null]},void 0,!0,void 0,this)}return We("box",{flexGrow:1,flexDirection:"column",overflow:"hidden",children:[k?null:We(HM,{children:[Ct.length>0?We(ut,{palette:t,items:Ct},void 0,!1,void 0,this):null,We(bi,{palette:t,logs:nt,names:o??n,service:i,errorOnly:s,width:u,onService:p,onToggleErrors:m,facets:L},void 0,!1,void 0,this)]},void 0,!0,void 0,this),l?We("box",{height:1,paddingLeft:1,backgroundColor:t.highlight,overflow:"hidden",children:We("input",{focused:!0,value:c,placeholder:"search messages or service names",onInput:f,backgroundColor:t.highlight,focusedBackgroundColor:t.highlight,textColor:t.text,cursorColor:t.primary},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,w&&Le>ie.length?We(Ns,{palette:t,start:_,count:ie.length,total:Le},void 0,!1,void 0,this):null,We("box",{flexGrow:1,border:!k,borderStyle:"rounded",borderColor:t.border,title:k?void 0:`logs ${Xe} \xB7 ${Ke}`,titleColor:t.primary,padding:k?0:ae.pad,overflow:"hidden",children:ie.length===0?We(ze,{palette:t,title:r.length===0?"No log events":"No events in this filter",body:r.length===0?"Start services to stream logs.":"Pick All, another service, or clear search / ERROR+.",hint:`\u2190 \u2192 cycle filters e errors i internal ${It("l")} clear`},void 0,!1,void 0,this):We(wi,{palette:t,logs:ie,width:X,followTick:d,focused:!1,wrapMode:v,selected:x,follow:g,showTimestamps:e.showTimestamps!==!1,showMeta:e.showMeta!==!1,onPick:h,onLeaveLatest:y,viewStart:_,search:c,regex:e.regex===!0},d,!1,void 0,this)},void 0,!1,void 0,this),!g?We(xi,{palette:t,width:u,newer:R,onJump:b},void 0,!1,void 0,this):null]},void 0,!0,void 0,this)}function pk(e){let{palette:t,width:r,service:n,shown:o,shownTotal:i,viewStart:s,focused:a}=e,c=e.showSystemLogs?e.logs:e.logs.filter((u)=>!si(u.source)),l=n===""?"all services":n,d=i===0?"empty":`${s+1}\u2013${Math.min(i,s+o.length)} of ${i}`;return We("box",{width:r,flexGrow:0,flexShrink:0,flexDirection:"column",overflow:"hidden",onMouseDown:e.onFocus,children:[We(bi,{palette:t,logs:c,names:e.names,service:n,errorOnly:e.errorOnly,width:r,onService:(u)=>{e.onFocus(),e.onService(u)},onToggleErrors:()=>{e.onFocus(),e.onToggleErrors()},facets:e.facets},void 0,!1,void 0,this),We("box",{flexGrow:1,border:!0,borderStyle:"rounded",borderColor:a?t.borderActive:t.border,title:`logs ${l} \xB7 ${d}`,titleColor:a?t.primary:t.muted,overflow:"hidden",children:o.length===0?We(ze,{palette:t,title:"No events in this pane",body:"Click the pane, then \u2190 \u2192 to pick a service.",hint:"\\\\ split | focus"},void 0,!1,void 0,this):We(wi,{palette:t,logs:o,width:Rp(r,0,!1),followTick:e.followTick,focused:!1,wrapMode:e.wrapMode,selected:e.selected,follow:e.follow,showTimestamps:e.showTimestamps,showMeta:e.showMeta,onPick:(u)=>{e.onFocus(),e.onSelect?.(u)},onLeaveLatest:e.onLeaveLatest,viewStart:s,search:e.search,regex:e.regex===!0},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}var mk=D(()=>{Pf();Yt();Mt();gr();ke();_r()});import{jsxDEV as Ge}from"@opentui/react/jsx-dev-runtime";function gk(e){let{palette:t,cfg:r,snap:n,profile:o,selected:i,onPick:s}=e,a=ye(),c=pi(i,hk),l=Object.keys(r?.profiles??{}).sort();if(l.length===0)return Ge(ze,{palette:t,title:"No profiles",body:"Add profiles.<name>.services in config.yaml."},void 0,!1,void 0,this);return Ge(kt,{palette:t,title:"profiles",children:[Ge("box",{height:1,flexShrink:0,overflow:"hidden",children:Ge("text",{fg:t.muted,wrapMode:"none",children:[`${l.length} profile${l.length===1?"":"s"} \xB7 current: `,Ge("span",{fg:t.success,children:o||"(none)"},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this),Ge("box",{height:1,flexShrink:0},void 0,!1,void 0,this),Ge("scrollbox",{ref:c,focused:!1,stickyScroll:!1,scrollX:!1,style:Ue(t),children:Ge("box",{flexDirection:"column",overflow:"hidden",gap:a.gap||1,children:l.map((d,u)=>{let f=d===o,p=u===i,m=r?.profiles[d]?.services??[],h=Object.keys(r?.profiles[d]?.environment??{}).length,v=p?t.borderActive:f?t.success:t.border,x=p?t.primary:f?t.success:t.muted;return Ge("box",{id:`${hk}-${u}`,border:!0,borderStyle:"rounded",borderColor:v,title:d,titleColor:x,flexDirection:"column",flexShrink:0,paddingLeft:1,paddingRight:1,overflow:"hidden",backgroundColor:p?t.highlight:void 0,onMouseDown:()=>s(u),children:[Ge("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,children:Ge("text",{wrapMode:"none",children:[f?Ge("span",{fg:t.success,children:"\u25CF current profile"},void 0,!1,void 0,this):Ge("span",{fg:t.muted,children:"\u25CB not active"},void 0,!1,void 0,this),Ge("span",{fg:t.muted,children:` \xB7 ${m.length} service${m.length===1?"":"s"}`},void 0,!1,void 0,this),h>0?Ge("span",{fg:t.muted,children:` \xB7 ${h} env var${h===1?"":"s"}`},void 0,!1,void 0,this):null]},void 0,!0,void 0,this)},void 0,!1,void 0,this),Ge("box",{flexDirection:"row",overflow:"hidden",flexShrink:0,children:m.length===0?Ge("text",{fg:t.muted,wrapMode:"none",children:"(no services)"},void 0,!1,void 0,this):Ge("text",{wrapMode:"word",children:m.map((g,R)=>{let _=tr(n?.services[g]);return Ge("span",{children:[Ge("span",{fg:et(t,_),children:hr(_)},void 0,!1,void 0,this),Ge("span",{fg:Ze(g,t),children:` ${g}`},void 0,!1,void 0,this),R<m.length-1?Ge("span",{fg:t.muted,children:" "},void 0,!1,void 0,this):null]},g,!0,void 0,this)})},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},d,!0,void 0,this)})},void 0,!1,void 0,this)},void 0,!1,void 0,this),Ge(Ut,{palette:t,backgroundColor:t.element,edge:"top",children:Ge(it,{palette:t,hints:[{key:"space",label:"set current"},{key:"enter",label:"set and start"}]},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}var hk="profile-row";var vk=D(()=>{Yt();Mt();At();ke();tt()});import{jsxDEV as V,Fragment as wk}from"@opentui/react/jsx-dev-runtime";function jM(e){let t=e.toLowerCase();if(t==="iap")return"info";if(t==="service"||t==="service_account")return"primary";return"idle"}function BM(e,t,r){if(r||t>=500)return e.error;if(t===0||t>=400)return e.warning;return e.success}function UM(e,t){if(t.startsWith("sa:"))return{label:"SA",color:e.primary};if(t!=="")return{label:"USR",color:e.info};return{label:"\u2014",color:e.muted}}function qM(e){let{palette:t,req:r,cols:n}=e,o=BM(t,r.status,r.error),i=r.status>0?String(r.status):"ERR",s=UM(t,r.identity),a=r.route||Sk,c=r.error?`${r.path} \u2014 ${_e(r.error,KM)}`:r.path;return V("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,children:[V("box",{width:Lf,flexShrink:0,overflow:"hidden",children:V("text",{fg:t.muted,children:r.timestamp.slice(11,19)},void 0,!1,void 0,this)},void 0,!1,void 0,this),n.showMethod?V("box",{width:Ml,flexShrink:0,overflow:"hidden",children:V("text",{fg:t.text,wrapMode:"none",children:oe(r.method,Ml)},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,V("box",{width:Ol,flexShrink:0,overflow:"hidden",children:V("text",{fg:o,wrapMode:"none",children:oe(i,Ol)},void 0,!1,void 0,this)},void 0,!1,void 0,this),n.showDur?V("box",{width:$l,flexShrink:0,overflow:"hidden",children:V("text",{fg:t.muted,wrapMode:"none",children:oe(`${r.durationMs}ms`,$l)},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,V("box",{width:Fl,flexShrink:0,overflow:"hidden",children:V("text",{fg:s.color,wrapMode:"none",children:oe(s.label,Fl)},void 0,!1,void 0,this)},void 0,!1,void 0,this),V("box",{width:n.routeCol,flexShrink:0,overflow:"hidden",children:V("text",{fg:r.route?t.info:t.muted,wrapMode:"none",children:oe(a,n.routeCol)},void 0,!1,void 0,this)},void 0,!1,void 0,this),V("box",{flexGrow:1,overflow:"hidden",children:V("text",{fg:r.error?t.error:t.text,wrapMode:"none",children:c},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function VM(e){let{palette:t,cols:r}=e;return V("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,backgroundColor:t.element,children:[V("box",{width:Lf,flexShrink:0,overflow:"hidden",children:V("text",{fg:t.muted,children:oe("TIME",Lf)},void 0,!1,void 0,this)},void 0,!1,void 0,this),r.showMethod?V("box",{width:Ml,flexShrink:0,overflow:"hidden",children:V("text",{fg:t.muted,children:oe("METHOD",Ml)},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,V("box",{width:Ol,flexShrink:0,overflow:"hidden",children:V("text",{fg:t.muted,children:oe("ST",Ol)},void 0,!1,void 0,this)},void 0,!1,void 0,this),r.showDur?V("box",{width:$l,flexShrink:0,overflow:"hidden",children:V("text",{fg:t.muted,children:oe("DUR",$l)},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,V("box",{width:Fl,flexShrink:0,overflow:"hidden",children:V("text",{fg:t.muted,children:oe("WHO",Fl)},void 0,!1,void 0,this)},void 0,!1,void 0,this),V("box",{width:r.routeCol,flexShrink:0,overflow:"hidden",children:V("text",{fg:t.muted,children:oe("ROUTE",r.routeCol)},void 0,!1,void 0,this)},void 0,!1,void 0,this),V("box",{flexGrow:1,overflow:"hidden",children:V("text",{fg:t.muted,children:"PATH"},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function zM(e){let{palette:t,name:r,auth:n,identity:o,upstream:i,match:s,onMouseDown:a}=e;return V("box",{flexDirection:"column",flexShrink:0,overflow:"hidden",onMouseDown:a,children:[V("text",{fg:t.text,wrapMode:"none",children:r},void 0,!1,void 0,this),V("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,children:[V(Be,{palette:t,label:n||"no auth",tone:jM(n)},void 0,!1,void 0,this),o?V(Be,{palette:t,label:o,tone:"idle"},void 0,!1,void 0,this):null]},void 0,!0,void 0,this),s?V("text",{fg:t.muted,wrapMode:"word",children:`match: ${s}`},void 0,!1,void 0,this):null,V("text",{fg:t.muted,wrapMode:"word",children:`\u2192 ${i}`},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function kk(e){let{palette:t,cfg:r,snap:n,width:o,onSelectRoute:i}=e,s=n?.proxy.routes??[],a=new Map((r?.proxy.routes??[]).map((S)=>[S.name,S])),c=new Map((r?.proxy.routes??[]).map((S)=>[S.name,S.match])),l=n?.proxy.recentRequests??[],d=n?.proxy.requestTotal??0,u=n?.proxy.requestErrors??0,f=o<ci,p=Math.max(yk,Math.min(YM,Math.floor(o*0.36))),m=f?o-xk:o-p-xk,h=Math.max(20,m-2),v=h>=bk,x=h>=JM,g=h>=bk?XM:QM,R=l.reduce((S,y)=>Math.min(g,Math.max(S,(y.route||Sk).length+GM)),WM),_={showMethod:v,showDur:x,routeCol:R};return V(kt,{palette:t,title:"proxy",children:[V(ut,{palette:t,items:[{text:n?.proxy.running?"RUNNING":"STOPPED",tone:n?.proxy.running?"success":"idle"},...n?.proxy.address?[{text:n.proxy.address,tone:"info"}]:[],{text:`${s.length} routes`,tone:s.length>0?"primary":"idle"},...d>0?[{text:`${d} requests`,tone:"idle"},...u>0?[{text:`${u} errors`,tone:"warning"}]:[]]:[]]},void 0,!1,void 0,this),V("box",{flexGrow:1,flexDirection:f?"column":"row",overflow:"hidden",children:[V("box",{flexGrow:f?1:0,flexBasis:f?0:void 0,flexShrink:0,minWidth:f?void 0:yk,width:f?void 0:p,minHeight:f?8:void 0,border:!0,borderStyle:"rounded",borderColor:t.border,title:"routes",titleColor:t.primary,paddingLeft:1,paddingRight:1,flexDirection:"column",overflow:"hidden",children:s.length===0?V(ze,{palette:t,title:"No proxy routes",body:"Add routes under proxy.routes or .devctl/proxy/routes.yaml."},void 0,!1,void 0,this):V(wk,{children:[i?V("text",{fg:t.muted,wrapMode:"word",children:"click a route for full details"},void 0,!1,void 0,this):null,V("scrollbox",{focused:!1,stickyScroll:!1,scrollX:!1,style:Ue(t),children:V("box",{flexDirection:"column",overflow:"hidden",gap:1,children:s.map((S)=>{let y=c.get(S.name),b=y&&(y.host||y.path)?`${y.host||"*"}${y.path?y.path:""}`:void 0,w=a.get(S.name);return V(zM,{palette:t,name:S.name,auth:S.auth,identity:S.identity,upstream:S.upstream,match:b,onMouseDown:i?()=>i({name:S.name,authType:w?.auth.type||S.auth,identityType:w?.auth.identity.type??"",serviceAccount:w?.auth.identity.service_account||w?.auth.service_account||"",audience:w?.auth.audience??"",clientId:w?.auth.client_id??"",clientSecret:Ha(w?.auth.client_secret??"")??((w?.auth.client_secret??"").trim()?"inline":""),matchHost:w?.match.host??"",matchPath:w?.match.path??"",upstream:S.upstream}):void 0},S.name,!1,void 0,this)})},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this),V("box",{flexGrow:1,flexBasis:0,minWidth:f?void 0:30,minHeight:f?10:void 0,border:!0,borderStyle:"rounded",borderColor:t.borderActive,title:"requests",titleColor:t.primary,flexDirection:"column",overflow:"hidden",children:l.length===0?V("box",{paddingLeft:1,paddingRight:1,children:V("text",{fg:t.muted,wrapMode:"word",children:n?.proxy.running?"No requests seen yet. Send one through the proxy to see it show up here \u2014 no need to restart or press r.":"Start the proxy, then send it a request to see live traffic here."},void 0,!1,void 0,this)},void 0,!1,void 0,this):V(wk,{children:[V(VM,{palette:t,cols:_},void 0,!1,void 0,this),V("scrollbox",{focused:!1,stickyScroll:!1,scrollX:!1,style:Ue(t),children:V("box",{flexDirection:"column",overflow:"hidden",children:l.map((S)=>V(qM,{palette:t,req:S,cols:_},S.requestId,!1,void 0,this))},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this),V(Ut,{palette:t,backgroundColor:t.element,edge:"top",children:V(it,{palette:t,hints:[{key:"n",label:"start proxy"},{key:"x",label:"stop proxy"}]},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}var Lf=9,Ml=8,Ol=4,$l=8,Fl=4,WM=8,GM=2,KM=64,Sk="(none)",yk=26,YM=56,xk=2,bk=46,JM=54,XM=20,QM=12;var Ck=D(()=>{Yt();vr();Ot();ke()});import{jsxDEV as pe}from"@opentui/react/jsx-dev-runtime";function _k(e){let{palette:t,name:r}=e;return pe("box",{flexGrow:1,border:!0,borderStyle:"rounded",borderColor:t.borderActive,backgroundColor:t.panel,title:r,titleColor:Ze(r,t),flexDirection:"column",overflow:"hidden",children:pe(Df,{...e,compact:!1},void 0,!1,void 0,this)},void 0,!1,void 0,this)}function Df(e){let{palette:t,cfg:r,snap:n,name:o,reveal:i,width:s,compact:a=!0,envScrollRef:c}=e,l=ye(),d=r?.services[o];if(!d)return pe(ze,{palette:t,title:"Unknown service",body:`No service named ${o}.`,hint:"j/k move"},void 0,!1,void 0,this);let u=n?.services[o],f=tr(u),p=u?.health??"UNKNOWN",m=vo(u)||Rs(d,u),h=Cx(d,i,r?.secrets.extra_markers??[],r?.secrets.extra_patterns??[],e.resolvedEnv),v=e.envStatus==="error"?"error":e.envStatus==="resolved"?"success":e.envStatus==="loading"?"info":"muted",x=e.envStatus==="resolved"?"resolved":e.envStatus==="loading"?"resolving":e.envStatus==="error"?"config fallback":"config",g=s>=eO,R=[{label:"health",value:Uc(d)},{label:"workdir",value:d.working_dir||"."},{label:"restart",value:Gc(d)},{label:"caps",value:d.capabilities.join(", ")||"\u2014"},{label:"runtime",value:d.container?`${d.container.runtime||"docker"} \xB7 ${d.container.image}`:"host"},...d.watch.enabled?[{label:"watch",value:d.watch.paths.join(" ")||"on"}]:[]],_=[{label:"identity",value:Wc(d,u)},{label:"depends",value:d.dependencies.map(Pa).join(", ")||"\u2014"},{label:"ports",value:Rs(d,u)},{label:"tries",value:String(u?.restarts??0),tone:u?.restarts?"warning":"muted"}],S=Math.max(Po+8,s-2),y=g?Math.floor(S/2):S;return pe("box",{padding:l.pad,flexGrow:1,flexDirection:"column",overflow:"hidden",children:[pe(ut,{palette:t,items:[{text:f,tone:Rk(f)},{text:p,tone:Rk(p)},{text:u?.pid?`pid ${u.pid}`:"pid \u2014",tone:u?.pid?"info":"idle"},{text:m==="\u2014"?"port \u2014":`port ${m}`,tone:m==="\u2014"?"idle":"info"}]},void 0,!1,void 0,this),d.description?pe("box",{height:1,overflow:"hidden",children:pe("text",{fg:t.muted,wrapMode:"none",children:_e(d.description,Math.max(8,s-4))},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,u?.last_error?pe("box",{height:1,overflow:"hidden",children:pe("text",{fg:t.error,wrapMode:"none",children:_e(u.last_error,ZM)},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,pe(If,{palette:t,item:{label:"command",value:Bc(d)},valueWidth:Math.max(8,S-Po)},void 0,!1,void 0,this),pe(tO,{palette:t,left:R,right:_,wide:g,colWidth:y},void 0,!1,void 0,this),pe(rO,{palette:t,entries:h,reveal:i,width:s,focused:!a,scrollRef:c,source:x,sourceTone:v,error:e.envError,onSelect:e.onSelectEnv},void 0,!1,void 0,this),a?null:pe(it,{palette:t,hints:[{key:"n",label:"start"},{key:"x",label:"stop"},{key:"R",label:"restart"},{key:"o",label:"config"},{key:"l",label:"logs"},{key:"/reveal",label:"secrets"},{key:"esc",label:"back"}]},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function tO(e){let{palette:t,left:r,right:n,wide:o,colWidth:i}=e,s=Math.max(4,i-Po);if(!o)return pe("box",{width:i,flexShrink:0,flexDirection:"column",overflow:"hidden",children:[...r,...n].map((a)=>pe(If,{palette:t,item:a,valueWidth:s},a.label,!1,void 0,this))},void 0,!1,void 0,this);return pe("box",{height:r.length,flexShrink:0,flexDirection:"row",overflow:"hidden",children:[pe(Pk,{palette:t,items:r,width:i},void 0,!1,void 0,this),pe(Pk,{palette:t,items:n,width:i},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function Pk(e){let t=Math.max(4,e.width-Po);return pe("box",{width:e.width,flexShrink:0,flexDirection:"column",overflow:"hidden",children:e.items.map((r)=>pe(If,{palette:e.palette,item:r,valueWidth:t},r.label,!1,void 0,this))},void 0,!1,void 0,this)}function If(e){let{palette:t,item:r,valueWidth:n}=e,o=r.tone==="warning"?t.warning:r.tone==="muted"?t.muted:t.text;return pe("box",{height:1,width:Po+n,flexDirection:"row",overflow:"hidden",children:[pe("box",{width:Po,flexShrink:0,overflow:"hidden",children:pe("text",{fg:t.muted,wrapMode:"none",children:oe(r.label,Po)},void 0,!1,void 0,this)},void 0,!1,void 0,this),pe("box",{width:n,flexShrink:0,overflow:"hidden",children:pe("text",{fg:o,wrapMode:"none",children:oe(r.value,n)},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function rO(e){let{palette:t,entries:r,reveal:n,width:o,focused:i,scrollRef:s,source:a,sourceTone:c,error:l,onSelect:d}=e;return pe("box",{flexGrow:1,flexDirection:"column",overflow:"hidden",children:[pe(ut,{palette:t,items:[{text:`env ${r.length}`,tone:r.length>0?"info":"idle"},{text:a,tone:c},{text:n?"secrets shown":"redacted",tone:n?"warning":"muted"}],hints:i?[{key:"j/k",label:"scroll"}]:[{key:"/reveal",label:"secrets"}]},void 0,!1,void 0,this),l?pe("box",{height:1,overflow:"hidden",children:pe("text",{fg:t.warning,wrapMode:"none",children:_e(l,Math.max(8,o-4))},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,r.length===0?pe("text",{fg:t.muted,children:"no service env overrides"},void 0,!1,void 0,this):pe("box",{flexGrow:1,height:"100%",overflow:"hidden",children:[d?pe("text",{fg:t.muted,wrapMode:"word",children:"click a variable for its full value"},void 0,!1,void 0,this):null,pe("scrollbox",{ref:s,focused:i,stickyScroll:!1,scrollX:!1,style:{rootOptions:{flexGrow:1,height:"100%",overflow:"hidden",backgroundColor:t.panel},viewportOptions:{backgroundColor:t.panel},contentOptions:{backgroundColor:t.panel},scrollbarOptions:{trackOptions:{foregroundColor:t.primary,backgroundColor:t.element}}},children:pe("box",{flexDirection:"column",overflow:"hidden",children:r.map((u)=>{let f=u.required?t.warning:u.fromConfig?t.accent:t.muted,p=u.value===""&&u.required?"(required)":u.value;return pe("box",{flexDirection:"row",overflow:"hidden",onMouseDown:d?()=>d(u):void 0,children:pe("text",{wrapMode:"word",children:[pe("span",{fg:f,children:u.required?`${u.key}*`:u.key},void 0,!1,void 0,this),pe("span",{fg:u.value===""?t.muted:t.text,children:` ${p}`},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},u.key,!1,void 0,this)})},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)}function Rk(e){switch(e.toUpperCase()){case"HEALTHY":case"RUNNING":return"success";case"UNHEALTHY":case"WARNING":case"WARN":case"STARTING":case"RESTARTING":return"warning";case"FAILED":case"ERROR":return"error";default:return"muted"}}var ZM=72,eO=56,Po=9;var Mf=D(()=>{ht();Yt();Mt();Ot();At();ke();tt()});import{jsxDEV as Hn}from"@opentui/react/jsx-dev-runtime";function Ek(e){let{palette:t,cfg:r,names:n,snap:o,selected:i,checked:s,width:a,reveal:c,onOpen:l,onSelectIndex:d,onToggle:u,resolvedEnv:f,envStatus:p,envError:m,onSelectEnv:h}=e;if(n.length===0)return Hn(ze,{palette:t,title:"No services",body:"This configuration does not define any services."},void 0,!1,void 0,this);let v=a<ci,x=n[i]??"",g=Nc(a,n,v),R=4,_=v?Math.max(Of,a-R):Math.max(Of,a-g-R);return Hn("box",{flexGrow:1,flexDirection:v?"column":"row",overflow:"hidden",children:[Hn("box",{flexGrow:v?1:0,flexShrink:0,minWidth:v?void 0:Mc,width:v?void 0:g,border:!0,borderStyle:"rounded",borderColor:t.borderActive,backgroundColor:t.panel,title:"services",titleColor:t.primary,flexDirection:"column",overflow:"hidden",children:[Hn(Il,{palette:t,checked:s},void 0,!1,void 0,this),Hn("box",{flexGrow:1,overflow:"hidden",children:Hn(Dl,{palette:t,names:n,snap:o,selected:i,checked:s,width:Fc(g),onOpen:l,onSelectIndex:d,onToggle:u},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this),Hn("box",{flexGrow:2,minWidth:v?void 0:Of,minHeight:v?nO:void 0,border:!0,borderStyle:"rounded",borderColor:t.border,backgroundColor:t.panel,title:x||"detail",titleColor:x?Ze(x,t):t.primary,overflow:"hidden",flexDirection:"column",children:Hn(Df,{palette:t,cfg:r,snap:o,name:x,reveal:c,width:_,resolvedEnv:f,envStatus:p,envError:m,onSelectEnv:h},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}var Of=36,nO=12;var Tk=D(()=>{Yt();vr();At();tt();Mf();Rf()});import{jsxDEV as we}from"@opentui/react/jsx-dev-runtime";function iO(e){switch(e){case"cycle":return"\u2194";case"toggle":return"\u23FB";case"page":return"\u25B8";case"action":return"!";default:return"\xB7"}}function Ik(e){let{palette:t,items:r,selected:n,locked:o,width:i,onPick:s,onActivate:a}=e,c=ye(),l=hp(r,n),d=Ln(r,l);return we("box",{flexGrow:1,border:!0,borderStyle:"rounded",borderColor:t.borderActive,backgroundColor:t.panel,title:"settings",titleColor:t.primary,flexDirection:"column",overflow:"hidden",children:[we("box",{height:2,paddingLeft:c.pad,flexShrink:0,flexDirection:"column",overflow:"hidden",children:[we("text",{fg:t.primary,wrapMode:"none",children:"Preferences"},void 0,!1,void 0,this),we("text",{fg:t.muted,wrapMode:"none",children:o?"DEVCTL_TUI_CONFIG is set \u2014 changes stay in this session.":"Changes save to your user tui.json unless noted."},void 0,!1,void 0,this)]},void 0,!0,void 0,this),we("box",{flexGrow:1,paddingLeft:c.pad,paddingRight:c.pad,overflow:"hidden",flexShrink:1,children:we(sO,{palette:t,items:r,active:l,scale:c,width:i,onPick:s,onActivate:a},void 0,!1,void 0,this)},void 0,!1,void 0,this),we(cO,{palette:t,item:d,scale:c},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function sO(e){let{palette:t,items:r,active:n,scale:o,width:i,onPick:s,onActivate:a}=e,c=pi(n,Dk),l=Math.max(8,i-Ak-Lk-10),d=0,u=gx(r);return we("scrollbox",{ref:c,focused:!1,stickyScroll:!1,scrollX:!1,style:Ue(t),children:we("box",{flexDirection:"column",overflow:"hidden",children:u.map((f,p)=>{let m=d;d+=f.items.length;let h=n>=m&&n<m+f.items.length;return we("box",{flexDirection:"column",flexShrink:0,marginBottom:p<u.length-1?Math.max(1,o.gap):0,overflow:"hidden",children:[we("box",{height:1,overflow:"hidden",children:we("text",{fg:h?t.primary:t.muted,wrapMode:"none",children:f.group},void 0,!1,void 0,this)},void 0,!1,void 0,this),f.group==="MCP"?we("box",{height:1,overflow:"hidden",children:we("text",{fg:t.muted,wrapMode:"none",children:"dedicated page \u2014 also /mcp or /agent"},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,f.items.map((v,x)=>{let g=m+x;return we(aO,{index:g,palette:t,item:v,active:g===n,rowH:o.rowH,valueWidth:l,onPick:()=>s(g),onActivate:()=>a(v)},v.id,!1,void 0,this)})]},f.group,!0,void 0,this)})},void 0,!1,void 0,this)},void 0,!1,void 0,this)}function aO(e){let{palette:t,index:r,item:n,active:o,rowH:i,valueWidth:s,onPick:a,onActivate:c}=e;return we("box",{id:`${Dk}-${r}`,height:i,flexDirection:"row",overflow:"hidden",flexShrink:0,backgroundColor:o?t.highlight:void 0,onMouseDown:()=>{if(o){c();return}a()},children:[we("box",{width:2,flexShrink:0,overflow:"hidden",children:we("text",{fg:t.primary,children:o?"\u203A":" "},void 0,!1,void 0,this)},void 0,!1,void 0,this),we("box",{width:Lk,flexShrink:0,overflow:"hidden",children:we("text",{fg:o?t.primary:t.muted,children:iO(n.kind)},void 0,!1,void 0,this)},void 0,!1,void 0,this),we("box",{width:Ak,flexShrink:0,overflow:"hidden",children:we("text",{fg:o?t.primary:t.text,wrapMode:"none",children:n.name},void 0,!1,void 0,this)},void 0,!1,void 0,this),we("box",{flexGrow:1,overflow:"hidden",children:we("text",{fg:lO(t,n,o),wrapMode:"none",children:_e(n.value,s)},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function cO(e){let{palette:t,item:r,scale:n}=e,o=r?.id==="font",i=r?.kind==="page"?`${r.name} \u2192 dedicated page`:r?.name??"";return we("box",{height:oO,flexShrink:0,border:!0,borderStyle:"rounded",borderColor:t.border,title:"selected",titleColor:t.muted,backgroundColor:t.element,paddingLeft:n.pad,paddingRight:n.pad,flexDirection:"column",overflow:"hidden",children:[we("box",{height:1,overflow:"hidden",children:we("text",{fg:t.primary,wrapMode:"none",children:o?`${mx(n.steps)} ${n.label}`:i},void 0,!1,void 0,this)},void 0,!1,void 0,this),we("box",{height:1,overflow:"hidden",children:we("text",{fg:t.text,wrapMode:"word",children:r?.detail??""},void 0,!1,void 0,this)},void 0,!1,void 0,this),we("box",{height:1,overflow:"hidden",children:we("text",{fg:t.muted,wrapMode:"none",children:r?r.kind==="info"?"read-only":r.hint:""},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function lO(e,t,r){if(t.id==="mouse")return t.value==="on"?e.success:e.text;if(t.kind==="page")return e.info;if(t.kind==="info")return e.text;return r?e.primary:e.text}var Ak=14,Lk=2,oO=5,Dk="settings-row";var Mk=D(()=>{Mt();Ot();ke();Er()});import{jsxDEV as Ae}from"@opentui/react/jsx-dev-runtime";function Ok(e){let{palette:t,cfg:r,google:n,bootError:o,bootErrorMissing:i=!1,step:s=0}=e,a=ye();if(o&&!r)return i?Ae(ze,{palette:t,title:"No configuration found",body:"Would you like to run setup?",hint:"[Enter] 9-step setup [Esc] Exit"},void 0,!1,void 0,this):Ae(ze,{palette:t,title:"Configuration error",body:o,hint:"Fix .devctl/config.yaml, then restart devctl [Esc] Exit"},void 0,!1,void 0,this);let c=e.issues,l=dO(r,n,c),d=l.filter((g)=>g.ok).length,u=l.length,f=u>0?Math.round(d/u*100):0,p=20,m=Math.round(f/100*p),h="\u2588".repeat(m)+"\u2591".repeat(Math.max(0,p-m)),v=f===100?t.success:f===0?t.muted:t.primary,x=l[s];return Ae(kt,{palette:t,title:"setup",scroll:!0,children:[Ae("box",{flexDirection:"row",overflow:"hidden",flexShrink:0,children:Ae("text",{fg:t.primary,children:"Developer onboarding"},void 0,!1,void 0,this)},void 0,!1,void 0,this),Ae("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,children:Ae("text",{wrapMode:"none",children:[Ae("span",{fg:v,children:`[${h}]`},void 0,!1,void 0,this),Ae("span",{fg:t.text,children:` ${d}/${u} ready`},void 0,!1,void 0,this),Ae("span",{fg:t.muted,children:` (${f}%)`},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this),Ae("box",{height:1,flexShrink:0},void 0,!1,void 0,this),l.map((g,R)=>{let _=R===s;return Ae("box",{height:a.rowH,flexDirection:"row",overflow:"hidden",backgroundColor:_?t.highlight:void 0,paddingLeft:1,children:[Ae("box",{width:2,flexShrink:0,children:Ae("text",{fg:et(t,g.ok?"OK":"WARN"),children:hr(g.ok?"OK":"WARN")},void 0,!1,void 0,this)},void 0,!1,void 0,this),Ae("box",{width:3,flexShrink:0,children:Ae("text",{fg:_?t.primary:t.muted,children:String(R+1).padStart(2," ")},void 0,!1,void 0,this)},void 0,!1,void 0,this),Ae("box",{width:20,flexShrink:0,overflow:"hidden",children:Ae("text",{fg:_?t.primary:t.text,children:g.name},void 0,!1,void 0,this)},void 0,!1,void 0,this),Ae("box",{flexGrow:1,overflow:"hidden",children:Ae("text",{fg:_?t.text:t.muted,wrapMode:"none",children:g.detail},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},g.name,!0,void 0,this)}),Ae("box",{height:1,flexShrink:0},void 0,!1,void 0,this),x?Ae("box",{border:!0,borderStyle:"rounded",borderColor:x.ok?t.success:t.warning,title:x.name,titleColor:x.ok?t.success:t.warning,flexDirection:"column",paddingLeft:1,paddingRight:1,flexShrink:0,overflow:"hidden",children:Ae("text",{fg:x.ok?t.text:t.warning,wrapMode:"word",children:x.detail},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,Ae("box",{height:1,flexShrink:0},void 0,!1,void 0,this),Ae("box",{flexDirection:"row",overflow:"hidden",flexShrink:0,children:Ae(Be,{palette:t,label:"never hard-coded",tone:"idle"},void 0,!1,void 0,this)},void 0,!1,void 0,this),Ae("text",{fg:t.muted,wrapMode:"word",children:"Service accounts and IAP audiences stay in configuration. Run `devctl setup` for the interactive CLI wizard."},void 0,!1,void 0,this),Ae(it,{palette:t,hints:[{key:"j/k",label:"steps"},{key:"enter",label:r?"start":"begin setup"},{key:"esc",label:"exit"},{key:"o",label:"profiles"}]},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function dO(e,t,r=[]){let n=Object.values(e?.services??{}).flatMap((s)=>s.ports.filter((a)=>!a.auto)),o=(e?.proxy.routes??[]).some((s)=>s.auth.type.toLowerCase()==="iap"),i=El(e,void 0,t).project;return[{name:ln[0],ok:Boolean(e?.repoRoot),detail:e?.repoRoot||"unknown"},{name:ln[1],ok:Boolean(e?.project.name),detail:e?.project.name||"set project.name"},{name:ln[2],ok:Boolean(i),detail:i||"optional for local-only"},{name:ln[3],ok:t?.adcAvailable===!0,detail:t?.adcAvailable?"ADC available":"needed only for cloud identity / IAP"},{name:ln[4],ok:!0,detail:"read from configuration; never hard-coded"},{name:ln[5],ok:!o||(e?.proxy.routes??[]).every((s)=>s.auth.type.toLowerCase()!=="iap"||s.auth.audience!==""),detail:o?"IAP routes present":"no IAP routes"},{name:ln[6],ok:n.length===0||Boolean(e),detail:`${n.length} fixed ports from configuration`},{name:ln[7],ok:Object.keys(e?.profiles??{}).length>0,detail:`${Object.keys(e?.profiles??{}).length} profile(s)`},{name:ln[8],ok:r.length===0,detail:r.length===0?"configuration is valid":r[0]??"invalid"}]}var ln;var $k=D(()=>{Yt();Mt();ke();tt();ln=["Repository","Environment","Google project","Authentication","Service accounts","IAP","Ports","Profiles","Validation"]});import{useEffect as uO,useState as pO}from"react";import{jsxDEV as ee}from"@opentui/react/jsx-dev-runtime";function Si(e){let{palette:t,title:r,tone:n="muted",children:o}=e,i=n==="success"?t.success:n==="warning"?t.warning:n==="error"?t.error:t.border;return ee("box",{border:!0,borderStyle:"rounded",borderColor:i,title:r,titleColor:i,flexDirection:"column",flexShrink:0,paddingLeft:1,paddingRight:1,marginBottom:1,overflow:"hidden",children:o},void 0,!1,void 0,this)}function $f(e,t){if(t.color)return t.color;switch(t.tone){case"success":return e.success;case"warning":return e.warning;case"error":return e.error;case"primary":return e.primary;case"muted":return e.muted;default:return e.text}}function js(e){let{palette:t,cells:r,header:n,gap:o=0,stripe:i}=e;return ee("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,backgroundColor:i?t.element:void 0,children:r.map((s,a)=>ee("box",{flexDirection:"row",flexShrink:0,overflow:"hidden",children:[a>0&&o>0?ee("box",{width:o,flexShrink:0},void 0,!1,void 0,this):null,ee("box",{width:s.width,flexShrink:0,overflow:"hidden",children:ee("text",{fg:n?t.primary:$f(t,s),wrapMode:"none",children:oe(s.text,s.width)},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},`${s.text}-${a}`,!0,void 0,this))},void 0,!1,void 0,this)}function Fk(e){let{palette:t,width:r}=e;return ee("box",{height:1,flexShrink:0,overflow:"hidden",children:ee("text",{fg:t.border,wrapMode:"none",children:oe("\u2500".repeat(Math.max(1,r)),r)},void 0,!1,void 0,this)},void 0,!1,void 0,this)}function Hs(e){let{palette:t,facts:r,width:n}=e,o=Up(r,n),i=[{text:"what",width:o.what},{text:"reading",width:o.reading}];if(o.meter>0)i.push({text:"used",width:o.meter});return i.push({text:"meaning",width:o.meaning}),ee("box",{flexDirection:"column",overflow:"hidden",flexShrink:0,children:[ee(js,{palette:t,header:!0,gap:ui,cells:i},void 0,!1,void 0,this),ee(Fk,{palette:t,width:n},void 0,!1,void 0,this),r.map((s,a)=>{let c=s.meaning===""?[""]:Jc(s.meaning,Math.max(1,o.meaning)),l=s.meter?Mb(s.meter):o.meter>0?"":void 0,d=[{text:s.what,width:o.what,tone:"text"},{text:s.reading,width:o.reading,tone:s.tone??"text"}];if(o.meter>0)d.push({text:l??"",width:o.meter,tone:s.meter?s.tone??"text":"muted"});return d.push({text:c[0]??"",width:o.meaning,tone:"muted"}),ee("box",{flexDirection:"column",overflow:"hidden",flexShrink:0,children:[ee(js,{palette:t,gap:ui,stripe:a%2===1,cells:d},void 0,!1,void 0,this),c.slice(1).map((u,f)=>{let p=[{text:"",width:o.what},{text:"",width:o.reading}];if(o.meter>0)p.push({text:"",width:o.meter});return p.push({text:u,width:o.meaning,tone:"muted"}),ee(js,{palette:t,gap:ui,stripe:a%2===1,cells:p},`${s.what}-more-${f}`,!1,void 0,this)})]},`${s.what}-${s.reading}-${a}`,!0,void 0,this)})]},void 0,!0,void 0,this)}function Nk(e){let{palette:t,cfg:r,snap:n,width:o,onRefresh:i}=e,s=ye(),[,a]=pO(0);if(uO(()=>{let B=setInterval(()=>{a((z)=>{let de=z+1;if(i&&de%mO===0)i();return de})},fO);return()=>clearInterval(B)},[i]),!r)return ee(ze,{palette:t,title:"No configuration loaded",body:"Add services to .devctl/config.yaml or finish setup."},void 0,!1,void 0,this);let c=Object.keys(r.services).sort(),l=c.some((B)=>Ob(r.services[B])),d=_b(c,n),u=Ub(d,l),f=l&&d.live>0?d.healthy/d.live:d.total>0?d.live/d.total:0,p=ii(f,hO),m=d.failed>0?"error":l&&d.live>0&&d.healthy<d.live?"warning":"success",h=Lb(o,s.pad),v=n?.session_id?Yl(n.session_id):void 0,x=v?ws(Date.now()-v.getTime()):"just started",g=n?.logs.total??0,R=n?.logs.errors??0,_=Ab(n?.logs.counts??{}),S=n?.proxy.routes??[],y=n?.credentials,b=(y?.entries??[]).filter((B)=>B.valid).length,w=n?.system,k=w?w.memAvailableKB??w.memFreeKB:0,L=w?Fb(w.loadAvg1,w.cpuCount):void 0,O=w?Nb(k,w.memTotalKB):void 0,F=[];if(w&&L&&O){F.push(L,O,{what:"Computer",reading:$b(w.platform),meaning:`on for ${ws(w.hostUptimeSec*gO)}`,tone:"text"});let B=n?.stats_series;if(B&&B.cpu.length>1)F.push({what:"Load trend",reading:Bp(B.cpu),meaning:`${Math.round(B.interval_ms/1000)}s samples`,tone:"text"});if(B&&B.mem.length>1)F.push({what:"RAM trend",reading:Bp(B.mem),meaning:"used memory over the same window",tone:"text"})}let Z=d.failed>0||L?.tone==="error"||O?.tone==="error"?"error":m==="warning"||L?.tone==="warning"||O?.tone==="warning"?"warning":"success",W=[{what:"This session",reading:x,meaning:"how long this run has been going",tone:"text"},{what:"Profile",reading:n?.profile||"none",meaning:"which group of services is selected",tone:n?.profile?"text":"muted"},{what:"This window",reading:n?.detached?"closed":"watching",meaning:n?.detached?"services keep running without this screen":"you are watching the services from here",tone:n?.detached?"warning":"text"},{what:"Project folder",reading:"",meaning:r.repoRoot,tone:"muted"}],C=[{what:"Lines kept",reading:String(g),meaning:"log lines still in memory",tone:"text"},{what:"Error lines",reading:String(R),meaning:R>0?"open Logs and press e to see only errors":"no ERROR or FATAL lines",tone:R>0?"error":"muted"},..._.map(([B,z])=>({what:B,reading:String(z),meaning:"lines from this service",tone:"text"}))],j=[{what:"Web proxy",reading:n?.proxy.running?"on":"off",meaning:n?.proxy.running?`${S.length} apps${n.proxy.address?` at ${n.proxy.address}`:""}`:"not forwarding local URLs",tone:n?.proxy.running?"success":"muted"},{what:"Agent tools",reading:n?.mcp?.running?"on":"off",meaning:n?.mcp?.address?`Cursor talks to devctl at ${n.mcp.address}`:"turn on from Settings \u2192 MCP",tone:n?.mcp?.running?"success":"muted"},{what:"Google account",reading:n?.identity.user?"signed in":"not signed in",meaning:[n?.identity.user,n?.identity.project].filter(Boolean).join(" \xB7 ")||"no Google user for this session",tone:n?.identity.user?"success":"muted"},{what:"Saved tokens",reading:y?`${b} of ${y.entries.length} good`:"none",meaning:y?Bb(y.backend):"nothing stored yet",tone:y&&b<y.entries.length?"warning":y?"success":"muted"}];return ee(kt,{palette:t,title:"stats",scroll:!0,children:[ee(Si,{palette:t,title:`Your services ${d.live} of ${d.total} started`,tone:Z,children:[ee(Hs,{palette:t,facts:u,width:h},void 0,!1,void 0,this),ee("box",{height:1,flexShrink:0},void 0,!1,void 0,this),ee("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,children:ee("text",{wrapMode:"none",children:[ee("span",{fg:t.muted,children:oe(l?"Ready":"Started",Up(u,h).what)},void 0,!1,void 0,this),ee("span",{children:" ".repeat(ui)},void 0,!1,void 0,this),ee("span",{fg:$f(t,{text:"",width:1,tone:m}),children:`[${p}]`},void 0,!1,void 0,this),ee("span",{fg:$f(t,{text:"",width:1,tone:m}),children:l?` ${d.healthy} of ${d.live||d.total} passing checks`:` ${d.live} of ${d.total} up`},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this),w?ee(Si,{palette:t,title:"This computer",tone:L?.tone==="error"||O?.tone==="error"?"error":L?.tone==="warning"||O?.tone==="warning"?"warning":"success",children:ee(Hs,{palette:t,facts:F,width:h},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,ee(Si,{palette:t,title:"This session",children:ee(Hs,{palette:t,facts:W,width:h},void 0,!1,void 0,this)},void 0,!1,void 0,this),ee(Si,{palette:t,title:"Each service",tone:d.failed>0?"error":l&&d.live>0&&d.healthy<d.live?"warning":"success",children:d.total===0?ee("text",{fg:t.muted,children:"No services in this project yet."},void 0,!1,void 0,this):ee("box",{flexDirection:"column",overflow:"hidden",flexShrink:0,children:[ee(vO,{palette:t,names:c,snap:n,width:h},void 0,!1,void 0,this),ee("box",{height:1,flexShrink:0},void 0,!1,void 0,this),ee("text",{fg:t.muted,wrapMode:"none",children:oe("CPU is % of one core. RAM is what that process is holding now.",h)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this),ee(Si,{palette:t,title:"Log lines",tone:R>0?"warning":"muted",children:ee(Hs,{palette:t,facts:C,width:h},void 0,!1,void 0,this)},void 0,!1,void 0,this),ee(Si,{palette:t,title:"Other parts",children:ee(Hs,{palette:t,facts:j,width:h},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function vO(e){let{palette:t,names:r,snap:n,width:o}=e,i=Db(o),s=[{text:"service",width:i.name},{text:"status",width:Tr}];if(i.health)s.push({text:"check",width:Ur});if(i.cpu)s.push({text:"cpu",width:Cs});if(i.mem)s.push({text:"ram",width:Ps});if(i.up)s.push({text:"up",width:ks});if(i.rst)s.push({text:"restarts",width:cl});if(i.pid)s.push({text:"pid",width:sn});return ee("box",{flexDirection:"column",overflow:"hidden",flexShrink:0,children:[ee(js,{palette:t,header:!0,gap:$t,cells:s},void 0,!1,void 0,this),ee(Fk,{palette:t,width:o},void 0,!1,void 0,this),r.map((a,c)=>ee(wO,{palette:t,name:a,rt:n?.services[a],cols:i,stripe:c%2===1},a,!1,void 0,this))]},void 0,!0,void 0,this)}function bO(e){if(e===void 0)return"muted";if(e>xO)return"error";if(e>yO)return"warning";return"text"}function wO(e){let{palette:t,name:r,rt:n,cols:o,stripe:i}=e,s=Hb(n),a=jb(n),c=n?.restarts??0,l=[{text:r,width:o.name,color:Ze(r,t)},{text:s,width:Tr,color:et(t,n?.state??"STOPPED")}];if(o.health)l.push({text:a,width:Ur,color:et(t,n?.health??"UNKNOWN")});if(o.cpu)l.push({text:n?.cpuPercent!==void 0?xx(n.cpuPercent):"\u2014",width:Cs,tone:bO(n?.cpuPercent)});if(o.mem)l.push({text:n?.memoryKB!==void 0?Ss(n.memoryKB):"\u2014",width:Ps,tone:"text"});if(o.up)l.push({text:Tb(n),width:ks,tone:"text"});if(o.rst)l.push({text:String(c),width:cl,tone:c>0?"warning":"muted"});if(o.pid)l.push({text:n?.pid?String(n.pid):"\u2014",width:sn,tone:"muted"});return ee("box",{flexDirection:"column",flexShrink:0,overflow:"hidden",children:[ee(js,{palette:t,gap:$t,stripe:i,cells:l},void 0,!1,void 0,this),n?.last_error?ee("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,children:ee("text",{fg:t.error,wrapMode:"none",children:oe(` ${n.last_error}`,Math.max(24,o.name+$t+Tr+Ur))},void 0,!1,void 0,this)},void 0,!1,void 0,this):null]},void 0,!0,void 0,this)}var fO=1000,mO=5,hO=16,gO=1000,yO=80,xO=100;var Hk=D(()=>{Yt();Mt();Ot();gr();At();ll();ke();tt()});import{useRenderer as SO,useTerminalDimensions as kO}from"@opentui/react";import{useCallback as jn,useEffect as CO,useMemo as jk,useRef as Vr,useState as Je}from"react";import{jsxDEV as he,Fragment as PO}from"@opentui/react/jsx-dev-runtime";function Bk({controller:e,tui:t,onQuit:r,onDown:n,onAttached:o,bootError:i,bootErrorMissing:s=!1,terminalBackground:a,workspace:c}){let{saveTuiPreferences:l,resolveTuiOverridePath:d,userTuiConfigPath:u,validateConfigText:f,freePort:p,readTextFile:m,writeTextFile:h,createStarterConfig:v,validate:x}=c,[g,R]=Je(e),[_,S]=Je(i),y=SO(),{width:b,height:w}=kO(),[k,L]=Je(g?"dashboard":"setup"),[O,F]=Je("none"),[Z,W]=Je(""),[C,j]=Je("commands"),[B,z]=Je(0),[de,Ee]=Je(0),[Ne,ne]=Je(0),[H,ae]=Je([]),[X,nt]=Je(),[ft,ie]=Je(_??""),[Le,Me]=Je({}),[Ke,gt]=Je(),[Xe,Ct]=Je(kx(g?.cfg)),[ge,vt]=Je(""),[at,yt]=Je(!1),[rr,Qe]=Je(),[Xt,qe]=Je(),[mt,xr]=Je(),Kt=Vr(null),br=Vr(null),nr=Vr(null),qt=Vr(null),or=Vr(null),ir=Vr(null),sr=Vr(null),Vt=Vr(""),[ce,K]=Je("quit"),T=Vr(void 0),ve=Vr(0),[Y,Lt]=Je(g?.cfg),ct=g?.previousPersisted,[Ce,Ro]=Je(void 0),P=hS({tui:t,controller:g,resolveTuiOverridePath:d,terminalBackground:a,userTuiConfigPath:u,snap:X,setStatus:ie,saveTuiPreferences:l,setPaletteIndex:z,setOverlay:F,setConfirmKind:K,setScreen:L,setSelected:ne,screen:k}),{themeName:ue,setThemeName:Yr,fontSize:Jr,prefsLocked:wr,palette:se,rootBackground:Bn,settingRows:Un,persistPrefs:Dr,persistTheme:dn,activateSetting:Bs}=P,ki=nS({controller:g,workspace:c,cfg:Y,snap:X,screen:k,configReloadError:Ce,setSnap:nt,setStatus:ie}),{google:Ci,doctor:le,doctorLoading:xt,doctorError:Pt,doctorProgress:Xr,setDoctorTick:Us}=ki,_o=t.keybinds.copy??Zr(),Sr=jk(()=>Object.keys(Y?.services??{}).sort(),[Y]),un=jn(async()=>{if(!g)return;try{let J=await g.status();if(nt(J),J.profile&&Xe==="")Ct(J.profile);return J}catch(J){ie(A(J));return}},[g,Xe]),Nl=lS({controller:g,tui:t,names:Sr,screen:k,refresh:un,setStatus:ie}),{logs:Uf,setLogs:pC,paused:Wf,errorOnly:Hl,setErrorOnly:jl,showSystemLogs:Gf,logSearch:Kf,setLogSearch:qf,logSearchFocused:fC,setLogSearchFocused:mC,logFollow:Vf,logSince:hC,logService:zf,setLogService:Bl,logServiceB:gC,setLogServiceB:vC,splitLogs:Ul,splitFocus:Wl,setSplitFocus:yC,logSliceB:Yf,logWindowB:Jf,logSelectedB:Xf,logPinnedB:xC,pinLogViewB:bC,logServices:wC,logShowTimestamps:SC,logShowMeta:kC,logSource:CC,logRegex:Qf,logWrap:Zf,logPinned:em,logSelected:tm,logsFullscreen:Gl,dashboardLogCursor:PC,setDashboardLogCursor:RC,logFacets:rm,logSources:nm,filteredLogs:om,filteredLogsB:_C,logWindow:Ws,logSlice:Gs,pinLogView:Kl,applyLogCursorA:EC,applyLogCursorB:TC,jumpToLatestLogs:im}=Nl,Eo=jk(()=>{if(C==="tasks")return $p(Object.keys(Y?.tasks??{}),Z,"tasks","run this task");if(C==="services")return $p(Object.keys(Y?.services??{}),Z,"services","exec in this service");return kb(Z,{services:Object.keys(Y?.services??{}),tasks:Object.keys(Y?.tasks??{})})},[Y,Z,C]);CO(()=>{Ee(0)},[Z]);let ql=zx(k,{doctor:le?.checks.length??0,settings:Un.length,profiles:Object.keys(Y?.profiles??{}).length,services:Sr.length,logs:Ul&&Wl===1?Yf.length:Gs.length,mcp:cw(),config:Object.keys(Y?.tasks??{}).length}),sm=k==="logs"?Ul&&Wl===1?Xf:tm:Ne,Ir=ql<=0?Math.max(0,sm):Math.max(0,Math.min(sm,ql-1)),AC=k==="detail"?ge:k==="services"?Sr[Ir]??"":"",{inspectorEnv:am,inspectorEnvStatus:cm,inspectorEnvError:lm,resolveEnvironment:LC}=SS({controller:g,cfg:Y,envService:AC}),To=jn(()=>{if(F("none"),W(""),j("commands"),mC(!1),T.current)clearTimeout(T.current)},[]),Vl=jn((J)=>{ae((bt)=>bt.includes(J)?bt.filter((qC)=>qC!==J):[...bt,J])},[]),dm=pS({tui:t,controller:g,cfg:Y,persistPrefs:Dr,snap:X,refresh:un,setStatus:ie}),{mcpPortDraft:DC,mcpPort:IC,toggleMcpTool:MC,toggleMcp:OC,copyMcpSnippet:$C}=dm;Zw({controller:g,cfg:Y,paused:Wf,logSince:hC,refresh:un,setLogs:pC,setCfg:Lt,setConfigReloadError:Ro,setStatus:ie});let Ks=jn((J)=>{vt(J),L("detail")},[]),um=jn((J)=>{let bt=J.value!==""?J.value:J.required?"(required, not set)":"(empty)";xr({title:J.key,body:bt}),F("scroll-text")},[]),zl=iS({controller:g,workspace:c,cfg:Y,snap:X,refresh:un,setStatus:ie,setOverlay:F}),{plan:qs,planInitiallyRunning:FC,planBusy:NC,lifecycle:pm}=zl,Qr=xS({workspace:c,renderer:y,setOverlay:F,setStatus:ie,setScreen:L,onAttached:o,setController:R,setCfg:Lt,setBootError:S}),{configEditRef:HC,configEditText:jC,configEditError:BC,setConfigEditError:UC,openConfigBuffer:fm,saveConfigBuffer:WC}=Xw({cfg:Y,readTextFile:m,setOverlay:F,setStatus:ie,validateConfigText:f,writeTextFile:h,controller:g,setConfirmKind:K,refresh:un}),mm=jn(async()=>{let J=y.getSelection()?.getSelectedText()??"";if(J.trim()===""){ie("Nothing selected \u2014 drag to highlight text");return}try{await fl(J);let bt=J.split(`
2161
- `).length;ie(bt===1?"Copied selection":`Copied ${bt} selected lines`)}catch(bt){ie(A(bt))}},[y]),{runCommand:Vs}=Vw({setOverlay:F,setQuery:W,setSlashPicker:j,checked:H,setConfirmKind:K,setStatus:ie,persistTheme:dn,setPaletteIndex:z,themeName:ue,setScreen:L,setSelected:ne,screen:k,renderer:y,cfg:Y,setScrollText:xr,reveal:at,controller:g,refresh:un,profile:Xe,setReveal:yt,resolveEnvironment:LC,openDetail:Ks,copySelection:mm,lastExportPath:Vt,openConfigBuffer:fm,onDown:n??((J)=>r(J)),workspace:c,logView:Nl,diagnostics:ki,lifecycleActions:zl}),hm=jn(()=>{let J=di(Eo,de);if(C==="tasks"){if(!J){ie(Eo.length===0?"no tasks configured":"pick a task");return}j("commands");let bt=Wr("run");if(bt)Vs(bt,[J.name]);return}if(C==="services"){if(!J){ie(Eo.length===0?"no services configured":"pick a service");return}j("commands"),W(`exec ${J.name} -- `);return}if(!J){ie(Z.trim()===""?"pick a command from the list":`unknown command /${Z}`),To();return}Vs(J,Sb(J,Z))},[To,Eo,Z,Vs,de,C]),gm=jn((J)=>{dn(J),To()},[To,dn]);Ew({tui:t,controller:g,cfg:Y,snap:X,ui:{screen:k,overlay:O,onQuit:r,onDown:n??((J)=>r(J)),closeOverlay:To,confirmKind:ce,confirmDetail:Le,portTarget:Ke,profile:Xe,listCursor:Ir,names:Sr,listCount:ql,openDetail:Ks,copySelection:mm,height:w,paletteIndex:B,applyTheme:gm,runCommand:Vs,saveConfigBuffer:WC,filtered:Eo,slashIndex:de,submitSlash:hm,advanceWizard:()=>void Qr.advanceWizard(),bootErrorMissing:s,bootError:_,createStarterConfig:v,startWizard:Qr.startWizard,toggleChecked:Vl,checked:H,detailName:ge,refresh:un,openConfigBuffer:fm,setOverlay:F,setConfirmKind:K,setConfirmDetail:Me,setPortTarget:gt,setLogDetail:Qe,logDetail:rr,setLogSearch:qf,setProfile:Ct,setStatus:ie,setPaletteIndex:z,setSlashIndex:Ee,setQuery:W,setSlashPicker:j,setScreen:L,setSelected:ne,setChecked:ae,setConfigEditError:UC,freePort:p},logView:Nl,lifecycleActions:zl,mcp:dm,preferences:P,diagnostics:ki,refs:{interruptArmedAt:ve,leaderTimer:T,logDetailsScrollRef:qt,scrollTextScrollRef:ir,routeDetailsScrollRef:or,planScrollRef:sr,helpScrollRef:br,configScrollRef:Kt,detailScrollRef:nr}});let vm=rb(ce,Xe,Le),GC=new Set(qs?.waves.flat()??[]),KC=qs===void 0||pm==="stop"?"":Object.entries(X?.services??{}).find(([J,bt])=>GC.has(J)&&bt.state==="FAILED")?.[0]??"";return he(vp.Provider,{value:xs(Jr),children:he("box",{flexDirection:"column",width:b,height:w,backgroundColor:Bn,overflow:"hidden",children:[Gl?null:he(PO,{children:[he(Kb,{palette:se,cfg:Y,snap:X,google:Ci,profile:Xe,reveal:at,width:b},void 0,!1,void 0,this),he(qb,{palette:se,screen:k,width:b,onSelect:L},void 0,!1,void 0,this),Ce?he("box",{height:1,paddingLeft:1,backgroundColor:se.panel,overflow:"hidden",children:he("text",{fg:se.error,wrapMode:"none",children:`\u26A0 configuration reload failed: ${Ce}`},void 0,!1,void 0,this)},void 0,!1,void 0,this):null]},void 0,!0,void 0,this),he("box",{flexGrow:1,overflow:"hidden",children:[k==="dashboard"?he(ck,{palette:se,cfg:Y,snap:X,logs:Uf,names:Sr,selected:Ir,checked:H,width:b,followTick:Vf,logService:zf,errorOnly:Hl,showSystemLogs:Gf,onOpen:Ks,onSelectIndex:ne,selectedLog:PC,onToggle:Vl,logSources:nm,onFilterService:Bl,onToggleErrors:()=>jl((J)=>!J),onShowErrors:()=>{Bl(""),jl(!0)},wrapMode:Zf,view:Gs,follow:!em,onLeaveLatest:Kl,onPickLog:(J)=>{let bt=Gs[J];if(!bt)return;RC(J),Qe(bt),Kl()},viewStart:Ws.start,viewTotal:om.length,newer:Ws.newer,onJumpLatest:im,facets:rm,leftover:ct,search:Kf,regex:Qf},void 0,!1,void 0,this):null,k==="services"?he(Ek,{palette:se,cfg:Y,names:Sr,snap:X,selected:Ir,checked:H,width:b,reveal:at,onOpen:Ks,onSelectIndex:ne,onToggle:Vl,resolvedEnv:am,envStatus:cm,envError:lm,onSelectEnv:um},void 0,!1,void 0,this):null,k==="detail"?he(_k,{palette:se,cfg:Y,snap:X,name:ge,reveal:at,width:b,envScrollRef:nr,resolvedEnv:am,envStatus:cm,envError:lm,onSelectEnv:um},void 0,!1,void 0,this):null,k==="logs"?he(fk,{palette:se,logs:Uf,names:Sr,logSources:nm,service:zf,errorOnly:Hl,showSystemLogs:Gf,search:Kf,searchFocused:fC,followTick:Vf,width:b,onSearch:qf,onService:Bl,onToggleErrors:()=>jl((J)=>!J),source:CC,regex:Qf,services:wC,showTimestamps:SC,showMeta:kC,view:Gs,wrapMode:Zf,selected:tm,follow:!em,newer:Ws.newer,viewStart:Ws.start,viewTotal:om.length,onSelect:EC,fullscreen:Gl,onLeaveLatest:Kl,onJumpLatest:im,facets:rm,split:Ul,splitFocus:Wl,serviceB:gC,viewB:Yf,selectedB:Xf,followB:!xC,newerB:Jf.newer,viewStartB:Jf.start,viewTotalB:_C.length,onServiceB:vC,onSelectB:TC,onLeaveLatestB:bC,onFocusPane:yC},void 0,!1,void 0,this):null,k==="auth"?he(VS,{palette:se,cfg:Y,google:Ci,identity:X?.identity},void 0,!1,void 0,this):null,k==="credentials"?he(ek,{palette:se,credentials:X?.credentials},void 0,!1,void 0,this):null,k==="proxy"?he(kk,{palette:se,cfg:Y,snap:X,width:b,onSelectRoute:(J)=>{qe(J),F("route-details")}},void 0,!1,void 0,this):null,k==="mcp"?he(lw,{palette:se,snap:X,port:IC,portDraft:DC,selected:Ir,onPick:ne,onToggle:()=>{OC()},onCopy:$C,onToggleTool:(J)=>{MC(J)}},void 0,!1,void 0,this):null,k==="doctor"?he(dk,{palette:se,report:le,loading:xt,progress:Xr,error:Pt,selected:Ir,onPick:ne,onReload:()=>{Us((J)=>J+1),ie("Re-running doctor")}},void 0,!1,void 0,this):null,k==="stats"?he(Nk,{palette:se,cfg:Y,snap:X,width:b,onRefresh:un},void 0,!1,void 0,this):null,k==="config"?he(QS,{palette:se,cfg:Y,width:b,selectedTask:Ir,scrollRef:Kt},void 0,!1,void 0,this):null,k==="profiles"?he(gk,{palette:se,cfg:Y,snap:X,profile:Xe,selected:Ir,onPick:ne},void 0,!1,void 0,this):null,k==="setup"?he(Ok,{issues:Y?x(Y):["configuration not loaded"],palette:se,cfg:Y,google:Ci,bootError:_,bootErrorMissing:s,step:Ir},void 0,!1,void 0,this):null,k==="settings"?he(Ik,{palette:se,items:Un,selected:Ir,locked:wr,width:b,onPick:ne,onActivate:Bs},void 0,!1,void 0,this):null]},void 0,!0,void 0,this),O==="slash"?he(jS,{palette:se,items:Eo,query:Z,selected:de,title:C==="tasks"?"tasks":C==="services"?"services":"commands",onQuery:W,onSubmit:hm},void 0,!1,void 0,this):null,O==="themes"?he(KS,{palette:se,themeName:ue,selected:B%St.length,termW:b,termH:w,onIndex:z,onPreview:Yr,onPick:gm},void 0,!1,void 0,this):null,O==="help"?he(fw,{palette:se,termW:b,termH:w,copyKey:_o,scrollRef:br},void 0,!1,void 0,this):null,O==="leader"?he(TS,{palette:se,termW:b,termH:w},void 0,!1,void 0,this):null,O==="confirm"?he(RS,{palette:se,title:vm.title,body:vm.body,kind:ce,termW:b,termH:w},void 0,!1,void 0,this):null,O==="setup-wizard"?he(vS,{palette:se,termW:b,termH:w,step:Qr.step,answers:Qr.answers,repo:Qr.repo,draft:Qr.draft,authStatus:Qr.authStatus,onDraft:Qr.setDraft,onSubmit:()=>void Qr.advanceWizard()},void 0,!1,void 0,this):null,O==="log-details"?he(LS,{palette:se,event:rr,termW:b,termH:w,scrollRef:qt},void 0,!1,void 0,this):null,O==="route-details"?he($S,{palette:se,route:Xt,termW:b,termH:w,scrollRef:or},void 0,!1,void 0,this):null,O==="scroll-text"&&mt?he(NS,{palette:se,title:mt.title,body:mt.body,termW:b,termH:w,scrollRef:ir},void 0,!1,void 0,this):null,O==="config-edit"&&Y?he(CS,{palette:se,path:Y.configPath,initialValue:jC,error:BC,termW:b,termH:w,textareaRef:HC},void 0,!1,void 0,this):null,O==="plan"&&qs?he(MS,{palette:se,plan:qs,snap:X,busy:NC,failed:KC,kind:pm,termW:b,termH:w,scrollRef:sr,initiallyRunning:FC,onDismiss:To},void 0,!1,void 0,this):null,Gl?null:he(Vb,{palette:se,screen:k,overlay:O,status:ft,paused:Wf,errorOnly:Hl,width:b,copyKey:_o},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this)}var Uk=D(()=>{te();Yt();Gp();Mt();vr();So();In();At();Tw();zw();Qw();eS();oS();sS();dS();fS();gS();bS();kS();PS();_S();uf();AS();DS();OS();FS();HS();vf();BS();qS();zS();ZS();tk();lk();uk();mk();Ms();vk();Ck();Mf();Tk();Mk();$k();Hk();Er();tt();_r()});var Wk={};JC(Wk,{renderApp:()=>Nf,runTui:()=>Ff,runTuiWithController:()=>EO});import{createCliRenderer as RO}from"@opentui/core";import{createRoot as _O}from"@opentui/react";import{jsxDEV as TO}from"@opentui/react/jsx-dev-runtime";async function EO(e,t){let r=e.loadTuiConfig(t.cfg.repoRoot,t.cfg.ui.keymap);await Nf(e,t,r)}async function Ff(e,t){let r,n,o=!1;try{r=await e.openTui("",t)}catch(s){n=A(s),o=va(s,Vn)}let i=e.loadTuiConfig(r?.cfg.repoRoot??process.cwd(),r?.cfg.ui.keymap);await Nf(e,r,i,n,o)}async function Nf(e,t,r=e.loadTuiConfig(t?.cfg.repoRoot??process.cwd(),t?.cfg.ui.keymap),n,o=!1){zs();let i=bm(),s=await RO({exitOnCtrlC:!1,useMouse:r.mouse,enableMouseMovement:r.mouse,useKittyKeyboard:{}}),a=_O(s),c=null;try{c=(await s.getPalette({timeout:120,size:16})).defaultBackground}catch{}await new Promise((l)=>{let d=(f)=>{let p=()=>{a.unmount(),i(),s.destroy(),l(),process.exit(0)};if(!t){p();return}t.close({detach:f,shutdownSupervisor:!0}).finally(p)},u=(f)=>{let p=()=>{a.unmount(),i(),s.destroy(),l(),process.exit(0)};if(!t){p();return}t.shutdown({stopServices:!f}).then(()=>t?.close({detach:!0})).finally(p)};a.render(TO(Bk,{workspace:ox(e),controller:t,tui:r,onQuit:d,onDown:u,onAttached:(f)=>{t=f},bootError:n,bootErrorMissing:o,terminalBackground:c},void 0,!1,void 0,this))})}var Hf=D(()=>{ix();te();Uk()});if(process.env.METADATA_SERVER_DETECTION===void 0)process.env.METADATA_SERVER_DETECTION="bios-only";if(process.env.GCE_METADATA_TIMEOUT===void 0)process.env.GCE_METADATA_TIMEOUT="0";import{existsSync as na,readFileSync as ea}from"fs";import{dirname as RP,isAbsolute as Tm,join as ra,resolve as _P}from"path";Ys();Ys();import{spawnSync as oP}from"child_process";import{randomBytes as Ql}from"crypto";import{copyFileSync as iP,existsSync as pn,mkdirSync as sP,readdirSync as aP,readFileSync as Zl,renameSync as ed,statSync as cP,unlinkSync as Jl,writeFileSync as lP}from"fs";import{homedir as dP}from"os";import{basename as uP,dirname as Xl,join as Dt}from"path";var pP=448,fP=384;function cr(){let e=process.env.DEVCTL_HOME;if(e&&e!=="")return e;return Dt(dP(),".devctl")}function Mr(e){sP(e,{recursive:!0,mode:pP})}function Ao(e){let t=Dt(cr(),"state",ar(e)),r=Dt(cr(),"sessions",ar(e));if(!pn(t)&&pn(r)){Mr(Xl(t));try{ed(r,t)}catch{Mr(t);for(let n of["state.json","devctl.lock","devctl.sock"]){let o=Dt(r,n);if(pn(o))iP(o,Dt(t,n))}}}return Mr(t),t}function Js(){let e=Dt(cr(),"logs");return Mr(e),e}function Xs(){let e=Dt(cr(),"exports");return Mr(e),e}function kr(){let e=Dt(cr(),"credentials");return Mr(e),e}function Qs(e,t=process.platform){if(t==="win32")return`\\\\.\\pipe\\devctl-${ar(e)}`;return Dt(Ao(e),"devctl.sock")}function mP(e){return Dt(Ao(e),"devctl.lock")}function wm(e){return Dt(Ao(e),"state.json")}function Pi(e){return Dt(Ao(e),"bootstrap.log")}var hP=5,Sm="bootstrap-";function km(e){let t=Pi(e);if(!pn(t))return;let r=Ao(e),n=Dt(r,`${Sm}${td()}.log`);try{ed(t,n)}catch{return}gP(r)}function gP(e){let t;try{t=aP(e).filter((n)=>n.startsWith(Sm)&&n.endsWith(".log"))}catch{return}let r=t.map((n)=>{let o=Dt(e,n);try{return{path:o,mtime:cP(o).mtimeMs}}catch{return}}).filter((n)=>n!==void 0).sort((n,o)=>o.mtime-n.mtime);for(let n=hP;n<r.length;n++)try{Jl(r[n].path)}catch{}}function Cr(e,t){Mr(Xl(e));let r=Dt(Xl(e),`.${uP(e)}.tmp-${process.pid}-${Ql(4).toString("hex")}`);lP(r,t,{mode:fP}),ed(r,e)}function td(e=new Date){return`${e.toISOString().slice(0,19).replace(/:/g,"-")+"Z"}-${Ql(3).toString("hex")}`}function fn(e){return rd(wm(e))}function rd(e){if(!pn(e))return;try{let t=JSON.parse(Zl(e,"utf8"));if(Array.isArray(t.processes)&&typeof t.repo_root==="string")return t;return}catch{return}}function Cm(e,t){Cr(wm(e),`${JSON.stringify(t,null,2)}
2162
- `)}function Pm(e,t){let r=mP(e);if(pn(r)){let n=vP(r);if(n&&Qt(n.pid))throw Error(`supervisor already running (pid ${n.pid})`);try{Jl(r)}catch{}}return Cr(r,JSON.stringify({pid:process.pid,socket:t})),{release:()=>{try{Jl(r)}catch{}}}}function vP(e){if(!pn(e))return;try{let t=JSON.parse(Zl(e,"utf8"));if(typeof t.pid==="number"&&typeof t.socket==="string")return{pid:t.pid,socket:t.socket}}catch{return}return}function Qt(e){if(e<=0)return!1;if(process.platform==="win32")return yP(e);try{return process.kill(e,0),!0}catch{return!1}}function nd(e){try{let t=oP("cmd.exe",["/d","/c",`tasklist /FO CSV /NH /FI "PID eq ${e}"`],{encoding:"buffer",windowsHide:!0,timeout:5000});return xP(t.stdout)}catch{return""}}function yP(e){try{return process.kill(e,0),!0}catch{}let t=nd(e).toLowerCase();if(t===""||t.includes("no tasks")||t.includes("no matching"))return!1;return t.includes(String(e))}function xP(e){if(!e)return"";if(typeof e==="string")return e;if(e.length>=2&&e[0]===255&&e[1]===254)return e.toString("utf16le");if(e.length>=4&&e[1]===0&&e[3]===0)return e.toString("utf16le");return e.toString("utf8")}function od(){return Ql(24).toString("hex")}function bP(e){return Dt(Ao(e),"mcp-token")}function Rm(e){let t=bP(e);if(pn(t)){let n=Zl(t,"utf8").trim();if(n!=="")return n}let r=od();return Cr(t,r),r}Zs();Zs();var EP="OPENCODE_TUI_CONFIG";function oa(){return ra(cr(),"tui.json")}function ia(e=""){for(let t of[_m,EP]){let r=process.env[t];if(!r||r==="")continue;let n=Tm(r)?r:_P(e||process.cwd(),r);if(na(n))return n}return}function TP(e,t){if(!t||Object.keys(t).length===0)return e;return{...e,keybinds:{...e.keybinds,...t}}}function Io(e,t){let r=TP(Em(),t),n=ia(e);if(n)return ad(r,ta(ea(n,"utf8")),n);let o=AP(e),i=o?ad(r,ta(ea(o,"utf8")),o):r,s=oa();if(!na(s)||o===s)return i;return ad(i,ta(ea(s,"utf8")),s)}function Am(e){let t=oa(),r={};if(na(t)){let o=ta(ea(t,"utf8"));if(Wn(o))r=o}let n={...r};if(e.theme!==void 0)n.theme=e.theme;if(e.mouse!==void 0)n.mouse=e.mouse;if(e.leader_timeout!==void 0)n.leader_timeout=e.leader_timeout;if(e.font_size!==void 0)n.font_size=e.font_size;if(e.log_timestamps!==void 0)n.log_timestamps=e.log_timestamps;if(e.log_metadata!==void 0)n.log_metadata=e.log_metadata;if(e.mcp_enabled!==void 0)n.mcp_enabled=e.mcp_enabled;if(e.mcp_port===null)delete n.mcp_port;else if(e.mcp_port!==void 0)n.mcp_port=e.mcp_port;if(e.mcp_disabled_tools!==void 0)n.mcp_disabled_tools=e.mcp_disabled_tools;return Cr(t,`${JSON.stringify(n,null,2)}
2163
- `),t}function AP(e){let t=ia(e);if(t)return t;let r=[e||process.cwd(),ra(e||process.cwd(),".devctl"),cr()],n=["tui.json","tui.jsonc"];for(let o of r)for(let i of n){let s=ra(o,i);if(na(s))return s}return}function ad(e,t,r){let n=Wn(t)?t:{},o={...e.keybinds};if(Wn(n.keybinds)){for(let[c,l]of Object.entries(n.keybinds))if(typeof l==="string")o[c]=l}let i={...e.cursor};if(Wn(n.cursor)){if(DP(n.cursor.style))i.style=n.cursor.style;if(typeof n.cursor.blinking==="boolean")i.blinking=n.cursor.blinking}let s={...e.scroll_acceleration};if(Wn(n.scroll_acceleration)&&typeof n.scroll_acceleration.enabled==="boolean")s.enabled=n.scroll_acceleration.enabled;let a={...e.attention,sounds:{...e.attention.sounds}};if(Wn(n.attention)){if(typeof n.attention.enabled==="boolean")a.enabled=n.attention.enabled;if(typeof n.attention.notifications==="boolean")a.notifications=n.attention.notifications;if(typeof n.attention.sound==="boolean")a.sound=n.attention.sound;if(typeof n.attention.volume==="number")a.volume=n.attention.volume;if(typeof n.attention.sound_pack==="string")a.sound_pack=n.attention.sound_pack;if(Wn(n.attention.sounds)){for(let[c,l]of Object.entries(n.attention.sounds))if(typeof l==="string")a.sounds[c]=LP(l,r)}}return{theme:typeof n.theme==="string"?n.theme:e.theme,leader_timeout:typeof n.leader_timeout==="number"?n.leader_timeout:e.leader_timeout,font_size:typeof n.font_size==="number"&&Number.isFinite(n.font_size)?Math.round(n.font_size):e.font_size,keybinds:o,scroll_speed:typeof n.scroll_speed==="number"?n.scroll_speed:e.scroll_speed,scroll_acceleration:s,diff_style:n.diff_style==="stacked"?"stacked":"auto",cursor:i,mouse:typeof n.mouse==="boolean"?n.mouse:e.mouse,attention:a,log_timestamps:typeof n.log_timestamps==="boolean"?n.log_timestamps:e.log_timestamps,log_metadata:typeof n.log_metadata==="boolean"?n.log_metadata:e.log_metadata,mcp_enabled:typeof n.mcp_enabled==="boolean"?n.mcp_enabled:e.mcp_enabled,mcp_port:typeof n.mcp_port==="number"&&Number.isInteger(n.mcp_port)?n.mcp_port:e.mcp_port,mcp_disabled_tools:Array.isArray(n.mcp_disabled_tools)?n.mcp_disabled_tools.filter((c)=>typeof c==="string"):e.mcp_disabled_tools,path:r}}function LP(e,t){if(e.startsWith("file://")||Tm(e))return e;return ra(RP(t),e)}function DP(e){return e==="block"||e==="underline"||e==="line"||e==="default"}function Wn(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}function ta(e){let t=e.replace(/\/\*[\s\S]*?\*\//g,"").replace(/^\s*\/\/.*$/gm,"");return JSON.parse(t)}import{existsSync as IT,readFileSync as MT,writeFileSync as OT}from"fs";import{spawn as wd}from"child_process";import{createWriteStream as aR,existsSync as Ii,mkdirSync as cR,readdirSync as Cd,readFileSync as lR,rmSync as dR,statSync as kd,writeFileSync as uR}from"fs";import{homedir as pR}from"os";import{dirname as Zm,isAbsolute as fR,join as qn,resolve as mR}from"path";ma();ma();var hR=50000,Pd="session-";function Qm(e){return Buffer.from(JSON.stringify(e),"utf8").toString("base64url")}function gR(e){try{let t=JSON.parse(Buffer.from(e,"base64url").toString("utf8"));if(typeof t==="object"&&t!==null&&typeof t.session==="string"&&typeof t.seq==="number")return t}catch{}return}function Sd(e,t){let r={...e};return r[t]=void 0,r}class Rd{events=[];eventStart=0;nextSeq=1;max;bus;detector;persistDir;persist;sessionID;streams=new Map;lastWrite=new Map;parsers=[];constructor(e,t,r,n,o,i,s=0,a=0){this.max=e>0?e:hR,this.bus=t,this.detector=r,this.sessionID=i;let c=o===""||o.startsWith("~/")?Js():o;if(this.persist=n&&i!=="",this.persistDir=this.persist?qn(c,`${Pd}${i}`):"",this.persist)cR(this.persistDir,{recursive:!0,mode:448}),xR(c,s,a)}sessionDir(){return this.persistDir}setParsers(e){this.parsers=e}append(e){let t=hd(e.message),r=this.parseLine(t),n=(c)=>this.detector?this.detector.redactText(c):c,o=r.raw!==void 0,i=n(t),s=r.message!==void 0?n(hd(r.message)):void 0,a={...e,timestamp:e.timestamp||new Date().toISOString(),level:e.level||r.level||vd(t),request_id:e.request_id||r.request_id||yd(t),message:o&&s!==void 0?s:i,raw:o?i:void 0,seq:this.nextSeq++};if(this.events.length<this.max)this.events.push(a);else this.events[this.eventStart]=a,this.eventStart=(this.eventStart+1)%this.max;if(this.bus?.publish(Se(Mo,a.service,{event:a,level:a.level})),this.persist){let c=`${a.timestamp} ${a.service} ${a.level} ${a.raw??a.message}
2164
- `,l=yR(a.service),d=this.streamFor(l);this.lastWrite.set(l,new Promise((u)=>{d.write(c,()=>u())}))}return a}async flush(){await Promise.all([...this.lastWrite.values()])}parseLine(e){let t={};for(let r of this.parsers)try{let n=r.parse(e);if(n)t={...t,...n}}catch{}return t}streamFor(e){let t=this.streams.get(e);if(t===void 0)t=aR(qn(this.persistDir,`${e}.log`),{flags:"a",mode:384}),t.on("error",()=>{}),this.streams.set(e,t);return t}async close(){await this.flush(),await Promise.all([...this.streams.values()].map((e)=>new Promise((t)=>{e.end(()=>t())}))),this.streams.clear(),this.lastWrite.clear()}query(e){let t=bd(e),r=[];return this.forEachEvent((n)=>{if(t(n))r.push(n)}),r}queryPage(e,t={}){let r=Jm(t.limit),n=t.cursor?gR(t.cursor):void 0,o=n!==void 0&&n.session!==this.sessionID,i=o?void 0:n,s=i?t.direction??"backward":"backward",a=bd(e),c=[];this.forEachEvent((m)=>{if(a(m))c.push(m)});let l;if(!i)l=c.slice(Math.max(0,c.length-r));else if(s==="forward")l=c.filter((m)=>m.seq>i.seq).slice(0,r);else{let m=c.filter((h)=>h.seq<i.seq);l=m.slice(Math.max(0,m.length-r))}let d=l[0]?.seq,u=l[l.length-1]?.seq,f=d!==void 0&&c.some((m)=>m.seq<d),p=u!==void 0&&c.some((m)=>m.seq>u);return{events:l,prevCursor:Qm({session:this.sessionID,seq:d??i?.seq??0}),nextCursor:Qm({session:this.sessionID,seq:u??i?.seq??this.nextSeq-1}),hasNext:p,hasPrev:f,sessionChanged:o}}queryFacets(e){let t=Sd(e,"services"),r=Sd(e,"level"),n=Sd(e,"source"),o=xd(e),i=0,s={},a={},c={};return this.forEachEvent((l)=>{if(!o(l))return;if(No(e,l))i+=1;if(No(t,l))s[l.service]=(s[l.service]??0)+1;if(No(r,l))a[l.level]=(a[l.level]??0)+1;if(No(n,l))c[l.source]=(c[l.source]??0)+1}),{total:i,byService:s,byLevel:a,bySource:c}}snapshot(){let e={},t=0;return this.forEachEvent((r)=>{if(e[r.service]=(e[r.service]??0)+1,r.level===Kn||r.level===$o)t+=1}),{total:this.events.length,errors:t,counts:e}}forEachEvent(e){let t=this.events.length;for(let r=0;r<t;r+=1){let n=this.events[(this.eventStart+r)%t];if(n)e(n)}}exportTo(e,t){_d(e,this.query(t))}}function eh(e){return{append:(t)=>{e.append(t)},query:async(t)=>e.query(t),queryPage:async(t,r)=>e.queryPage(t,r),queryFacets:async(t)=>e.queryFacets(t),snapshot:()=>e.snapshot(),exportTo:async(t,r)=>{e.exportTo(t,r)},setParsers:(t)=>{e.setParsers(t)},setSecrets:(t,r)=>{},close:()=>e.close()}}function vR(e=new Date){let t=e.toISOString().replace(/[:.]/g,"-");return qn(Xs(),`devctl-logs-${t}.log`)}function th(e=""){if(e==="")return vR();let t=e.startsWith("~/")?qn(pR(),e.slice(2)):e;return fR(t)?t:mR(process.cwd(),t)}function _d(e,t){Mr(Zm(e));let r=t.map((n)=>`${n.timestamp} ${n.service} ${n.level} ${n.raw??n.message}`);uR(e,`${r.join(`
2229
+ Enter is preview only. /import compose ${Yr} --write to save.`}),e("scroll-text")}catch(se){i(L(se))}return}case"diff":if(!p){i("no configuration loaded");return}m({title:"config sources",body:Yw(p,h)}),e("scroll-text");return;case"daemon":{let P=p?.repoRoot;if(!P){i("no repository");return}let ue=B(P);if(!z(ue)){i("no daemon bootstrap log yet for this repository");return}m({title:"daemon bootstrap",body:de(ue)}),e("scroll-text");return}case"reload":if(!v)return;v.reload().then((P)=>{if(i(P.restart_required.length===0?"Configuration reloaded":`Reload requires restart: ${P.restart_required.join(", ")}`),P.restart_required.length>0)o("reload"),e("confirm");b()});return;case"doctor":l("doctor");return;case"stats":l("stats");return;case"start":await K(Po,g);return;case"stop":await A(Po);return;case"restart":{let P=vx(Ce),ue=Rp(P.services,n);await ve(ue,g,P.cascade);return}case"run":{let P=Ce[0]??"";if(!P){r("tasks"),t(""),e("slash"),i(Object.keys(p?.tasks??{}).length===0?"no tasks configured":"pick a task");return}if(!v){i("no daemon attached");return}try{let ue=await v.runTask(P);Ke(`task:${P}`),l("logs"),i(`task ${P} exited ${ue.code}`)}catch(ue){throw Ke(`task:${P}`),l("logs"),ue}return}case"exec":{let P=yx(Ce);if(!P.service){r("services"),t(""),e("slash"),i(Object.keys(p?.services??{}).length===0?"no services configured":"pick a service");return}if(!v){i("no daemon attached");return}if(P.printEnv){if(!p?.services[P.service]){i(`unknown service ${P.service}`);return}if(P.reveal)_(!0);i(`Resolving environment for ${P.service}\u2026`);try{await R(P.service),S(P.service),i(`Resolved environment for ${P.service}`)}catch(ue){S(P.service),i(L(ue))}return}if(P.command.length===0){i("usage: /exec <service> -- <command\u2026>");return}try{let ue=await v.execService(P.service,P.command);Ke(`${P.service}:exec`),l("logs"),i(`${P.service}:exec exited ${ue.code}`)}catch(ue){throw Ke(`${P.service}:exec`),l("logs"),ue}return}case"refresh":if(u==="auth"){await Vt();return}await b(),i("Refreshed status and logs");return;case"pause":Xe((P)=>!P),l("logs");return;case"filter":Ct((P)=>!P),l("logs");return;case"system":ge(),l("logs");return;case"clear":vt();return;case"reveal":{let P=!h;_(P),i(P?"Secrets visible this session":"Secrets hidden");return}case"wrap":{let P=Qc(at);yt(P),i(`Log ${el(P)}`);return}case"copy":await y();return;case"export":{let P=ne(Ce[0]);if(v)await v.logs({export:P,services:rr,level:Qe?"ERROR":Xt,search:qe,regex:mt,source:br});else H(P,Kt);x.current=P,i(`Exported ${P}`);return}case"exports":{let P=x.current||ae();Q(P),i(`Opened ${ae()}`);return}case"regex":xr((P)=>!P),i(mt?"Substring search":"Regex search");return;case"since":nr(Ce[0]||new Date(Date.now()-3600000).toISOString()),i(`Logs since ${Ce[0]||"1h"}`);return;case"until":qt(Ce[0]||new Date().toISOString()),i(`Logs until ${Ce[0]||"now"}`);return;case"history":{let P=nt(),ue=Ce[0]||P[0];if(!ue){i("No persisted log sessions");return}or(ft(ue)),i(`Loaded session ${ue}`);return}case"buffer":w();return;case"edit":{if(!p)return;let P=process.env.DEVCTL_EDITOR||process.env.EDITOR,ue=P?[P,p.configPath]:process.platform==="darwin"?["open",p.configPath]:["xdg-open",p.configPath];Bun.spawn({cmd:ue,stdout:"ignore",stderr:"ignore",stdin:"ignore"}),i(`Opened ${p.configPath} in ${ue[0]}. Run /reload after saving.`);return}default:i(`/${ct.name}`)}}catch(P){i(L(P))}finally{setTimeout(()=>{J.current=!1},aM)}},[ve,K,A,n,p,vt,v,y,Qe,Kt,Xt,mt,qe,rr,br,at,k,w,S,s,g,b,Vt,f,h,u,ir,r,c,sr,ge])}}var aM=50;var eS=I(()=>{te();hf();gf();gr();Lt();tt()});import{useCallback as tS,useRef as cM,useState as rS}from"react";function nS({cfg:e,readTextFile:t,setOverlay:r,setStatus:n,validateConfigText:o,writeTextFile:i,controller:s,setConfirmKind:a,refresh:c}){let l=cM(null),[d,u]=rS(""),[f,p]=rS(""),m=tS(()=>{if(!e)return;try{u(t(e.configPath)),p(""),r("config-edit")}catch(v){n(L(v))}},[e]),h=tS(()=>{if(!e)return;let v=l.current?.plainText??d,b=o(e.repoRoot,e.configPath,v);if(b.length>0){p(b.join(`
2230
+ `)),n("Config buffer not saved");return}if(i(e.configPath,v),p(""),r("none"),!s){n(`Wrote ${e.configPath}`);return}s.reload().then((g)=>{if(n(g.restart_required.length===0?"Configuration saved and reloaded":`Saved; restart required: ${g.restart_required.join(", ")}`),g.restart_required.length>0)a("reload"),r("confirm");c()})},[e,d,s,c]);return{configEditRef:l,configEditText:d,configEditError:f,setConfigEditError:p,openConfigBuffer:m,saveConfigBuffer:h}}var oS=I(()=>{te()});import{useEffect as lM}from"react";function iS({controller:e,cfg:t,paused:r,logSince:n,refresh:o,setLogs:i,setCfg:s,setConfigReloadError:a,setStatus:c}){return lM(()=>{if(!e)return;let l,d=!1,u=[],f=t&&t.logs.max_memory_events>0?t.logs.max_memory_events:50000,p=()=>{if(l=void 0,u.length>0){let h=u.splice(0,u.length);i((v)=>Gb(v,h,n,f))}if(d)d=!1,o()},m=e.onEvent((h)=>{if(h.type===Io&&h.payload&&typeof h.payload==="object"&&"event"in h.payload){let v=h.payload.event;if(!r)u.push(v);if(!l)l=setTimeout(p,30);return}if(h.type===Ti){a(dx(h));return}if(h.type===da)a(void 0),e.configSnapshot().then(s).catch((v)=>c(L(v)));if(d=!0,!l)l=setTimeout(p,30)});return()=>{if(m(),l)clearTimeout(l)}},[e,n,r,o]),{}}var sS=I(()=>{te();vr();gr()});import{useCallback as dM,useEffect as aS,useRef as cS,useState as yi}from"react";function lS({controller:e,workspace:t,cfg:r,snap:n,screen:o,configReloadError:i,setSnap:s,setStatus:a}){let{detectGoogle:c,runDoctor:l,loadPath:d}=t,[u,f]=yi(),[p,m]=yi(),[h,v]=yi(!1),[b,g]=yi(""),[_,R]=yi(0),[S,y]=yi({active:"Preparing diagnostics",checks:[]}),x=cS(void 0),w=cS(0),k=dM(async()=>{if(!e){a("Supervisor is not running");return}a("Probing configured identities\u2026");try{let D=await e.refreshAuth();s((W)=>W?{...W,identity:D}:W);let O=Object.values(D.service_account_status),F=O.filter((W)=>W==="available").length,Z=O.filter((W)=>W==="unavailable").length;a(O.length===0?"Identity refreshed \u2014 no service accounts configured":`Identity refreshed \u2014 ${F} available${Z>0?`, ${Z} unavailable`:""}`)}catch(D){a(L(D))}},[e]);return aS(()=>{c(r?.google.project_id??"").then(f).catch((D)=>{a(L(D))})},[r?.google.project_id,n?.identity.project]),aS(()=>{if(o!=="doctor"||!r)return;let D=x.current,O=n?.session_id??"";if(D?.cfg===r&&D.tick===_&&D.sessionID===O&&D.reloadError===i)return;x.current={cfg:r,tick:_,sessionID:O,reloadError:i};let F=++w.current;v(!0),g(""),y({active:"Preparing diagnostics",checks:[]});let Z;if(r.configPath!==""){Z="";try{d(r.repoRoot,r.configPath)}catch(W){Z=L(W)}}l(r,(W)=>{if(F===w.current)y(W)},{services:n?.services,proxyRunning:n?.proxy.running,repositoryConfigError:Z}).then((W)=>{if(F!==w.current)return;m(W)}).catch((W)=>{if(F!==w.current)return;g(L(W))}).finally(()=>{if(F===w.current)v(!1)})},[o,r,_,n?.session_id,i]),{google:u,setGoogle:f,doctor:p,doctorLoading:h,doctorError:b,doctorProgress:S,setDoctorTick:R,refreshAuth:k}}var dS=I(()=>{te()});import{useCallback as vf,useState as Cl}from"react";function uS({controller:e,workspace:t,cfg:r,snap:n,refresh:o,setStatus:i,setOverlay:s}){let{resolveStartRequest:a,startupPlan:c,shutdownPlan:l,shutdownPlanExact:d}=t,[u,f]=Cl(),[p,m]=Cl([]),[h,v]=Cl(!1),[b,g]=Cl("start"),_=vf(async(y,x)=>{if(!e||!r){i("no configuration loaded");return}try{let w=y.length>0,k=a(r,{services:y,profile:w?void 0:x}),D=c(r,k.services,k.profile);g("start"),m(D.waves.flat().filter((Z)=>vo(n?.services[Z]))),f(D),s("plan"),v(!0);let O=k.services.filter((Z)=>!vo(n?.services[Z])),F=await e.start({services:O.length>0?O:k.services,profile:w?void 0:k.profile});await o(),v(!1),i(Cw(F))}catch(w){v(!1),await o(),i(L(w))}},[r,e,o,n]),R=vf(async(y)=>{if(!e||!r){i("no configuration loaded");return}let x=y.length>0?y:Object.entries(n?.services??{}).filter(([,w])=>w.state!=="STOPPED"&&w.state!=="UNKNOWN").map(([w])=>w);if(x.length===0){i("nothing to stop");return}try{let w=l(r,x);g("stop"),m([]),f(w),s("plan"),v(!0),await e.stop(y),await o(),v(!1),i(Pw(w))}catch(w){v(!1),await o(),i(L(w))}},[r,e,o,n]),S=vf(async(y,x,w=!1)=>{if(!e||!r){i("no configuration loaded");return}try{let k=kw(r,y,x),D=w?l(r,k.services):d(r,k.services);g("restart"),m([]),f(D),s("plan"),v(!0),await e.restart(k.services,w),await o(),v(!1);let O=bl(D);i(O===""?"Restarted selected services":`Restarted ${O}`)}catch(k){v(!1),await o(),i(L(k))}},[r,e,o]);return{plan:u,planInitiallyRunning:p,planBusy:h,lifecycle:b,beginStart:_,beginStop:R,beginRestart:S}}var pS=I(()=>{te();xl();Lt()});import{useCallback as Lr,useEffect as Kr,useMemo as Fn,useRef as uM,useState as be}from"react";function mS(e,t,r,n,o){let i=Ap(e,t.sliceLen,t.windowStart,t.windowNewer);if(i.startDelta!==0){r(Math.max(0,t.windowStart+i.startDelta)),n(!0),o(i.selected);return}let s=Math.max(t.sliceLen-1,0),a=t.sliceLen>0&&(i.selected<s||t.windowNewer>0);if(a&&!t.pinned)r(Zc(t.filteredLen));n(a),o(i.selected)}function hS({controller:e,tui:t,names:r,screen:n,refresh:o,setStatus:i}){let[s,a]=be([]),[c,l]=be(!1),[d,u]=be(!1),[f,p]=be(!0),[m,h]=be(""),[v,b]=be(!1),[g,_]=be(0),[R,S]=be(""),[y,x]=be(""),[w,k]=be(""),[D,O]=be(""),[F,Z]=be(!1),[W,C]=be(0),[j,B]=be(Ar-1),[z,de]=be(!1),[Ee,Ne]=be(0),ne=fS,[H,ae]=be(t.log_timestamps!==!1),[Q,nt]=be(t.log_metadata!==!1),[ft,ie]=be(()=>[...Wb]),[Le]=be(""),[Me]=be(""),[Ke,gt]=be(!1),[Xe,Ct]=be("focus"),[ge,vt]=be(!1),[at,yt]=be(Ar-1),[rr,Qe]=be(!1),[Xt,qe]=be(-1),[mt,br]=be(),[Kt,xr]=be(""),[nr,qt]=be(!1),[or,ir]=be(!1),sr=uM([]),[Vt,ce]=be(0),K=Fn(()=>tl(r,s,ft),[r,s,ft]);Kr(()=>{ie((le)=>{let bt=new Set(le),Pt=!1;for(let Xr of s)if(Xr.service!==""&&!r.includes(Xr.service)&&!bt.has(Xr.service))bt.add(Xr.service),Pt=!0;return Pt?[...bt].sort():le})},[s,r]);let A=Fn(()=>yo(s,{service:w,services:ne,errorOnly:d,search:m,regex:Ke,source:Me,since:R,until:y,systemLogs:f}),[d,Ke,m,w,ne,R,Me,y,s,f]),ve=Fn(()=>Tp(A,ge,Vt),[A,ge,Vt]),J=ve.items,Dt=Fn(()=>yo(s,{service:D,services:ne,errorOnly:d,search:m,regex:Ke,source:Me,since:R,until:y,systemLogs:f}),[d,Ke,m,D,ne,R,Me,y,s,f]),ct=Fn(()=>Tp(Dt,z,Ee),[Dt,z,Ee]),Ce=ct.items;Kr(()=>{qe(-1),vt(!1),yt(Math.max(0,Math.min(Ar,A.length)-1))},[d,m,w,f]),Kr(()=>{de(!1),B(Math.max(0,Math.min(Ar,Dt.length)-1))},[d,m,D,f]),Kr(()=>{if(n!=="logs")Qe(!1)},[n]),Kr(()=>{if(n!=="logs"||ge)return;yt(Math.max(0,Math.min(Ar,A.length)-1))},[A.length,ge,n]),Kr(()=>{if(n!=="logs"||z)return;B(Math.max(0,Math.min(Ar,Dt.length)-1))},[Dt.length,z,n]);let Po=Lr(()=>{if(!ge)ce(Zc(A.length)),vt(!0)},[A.length,ge]),P=Lr(()=>{if(!z)Ne(Zc(Dt.length)),de(!0)},[Dt.length,z]),ue=Fn(()=>({sliceLen:J.length,windowStart:ve.start,windowNewer:ve.newer,filteredLen:A.length,pinned:ge}),[A.length,ge,J.length,ve.newer,ve.start]),Yr=Fn(()=>({sliceLen:Ce.length,windowStart:ct.start,windowNewer:ct.newer,filteredLen:Dt.length,pinned:z}),[Dt.length,z,Ce.length,ct.newer,ct.start]),Jr=Lr((le)=>mS(le,ue,ce,vt,yt),[ue]),wr=Lr((le)=>mS(le,Yr,Ne,de,B),[Yr]),se=Lr((le)=>{if(F&&W===1){wr(le);return}Jr(le)},[Jr,wr,W,F]),Bn=Lr((le)=>{let bt=J.length,Pt=Ap(le,bt,ve.start,ve.newer);if(Pt.startDelta!==0)ce(Math.max(0,ve.start+Pt.startDelta)),vt(!0);qe(Pt.selected)},[J.length,ve.newer,ve.start]),Un=Lr(()=>{if(vt(!1),de(!1),qe(-1),n==="logs")yt(Math.max(0,Math.min(Ar,A.length)-1)),B(Math.max(0,Math.min(Ar,Dt.length)-1));_((bt)=>bt+1);let le=Math.max(ve.newer,ct.newer);i(le>0?`Jumped to latest (+${le} new)`:"Jumped to latest logs")},[A.length,Dt.length,ve.newer,ct.newer,n]),Dr=Fn(()=>({services:F?fS:w!==""?[w]:ne,level:d?"ERROR":Le,search:m,regex:Ke,source:Me,since:R,until:y}),[d,Le,Ke,m,w,ne,R,Me,y,F]),dn=Lr(async()=>{if(!e)return;try{br(await e.logsStats(Dr))}catch(le){i(L(le))}},[e,Dr]),Ws=Lr(async()=>{if(!e||c)return;try{let le=await e.logsPage(Dr);a((bt)=>Kb(bt,le.events)),xr(le.prevCursor),qt(le.hasPrev),await dn()}catch(le){i(L(le))}},[e,Dr,c,dn]),Ci=Lr(()=>{a([]),S(new Date().toISOString()),vt(!1),de(!1),i("Cleared on-screen log buffer")},[]),Pi=Lr(()=>{p((le)=>!le)},[]);return Kr(()=>{o().then(()=>Ws())},[o,Ws]),Kr(()=>{sr.current=s},[s]),Kr(()=>{let le=Lp(ge,ve.start,nr),bt=F&&Lp(z,ct.start,nr);if(!e||or||!le&&!bt)return;ir(!0),e.logsPage({...Dr,cursor:Kt,direction:"backward"}).then((Pt)=>{if(Pt.sessionChanged){i("Daemon session changed \u2014 older log history is no longer available"),qt(!1);return}let Xr=sr.current,Gs=qb(Xr,Pt.events);a(Gs);let _o=Gs.length-Xr.length;if(_o>0){if(ge)ce((Sr)=>Sr+_o);if(z)Ne((Sr)=>Sr+_o)}xr(Pt.prevCursor),qt(Pt.hasPrev)}).catch((Pt)=>i(L(Pt))).finally(()=>ir(!1))},[e,Dr,or,nr,ge,z,Kt,ve.start,ct.start,F]),Kr(()=>{if(!e||c||n!=="logs")return;let le=setInterval(()=>{dn()},pM);return()=>clearInterval(le)},[e,c,dn,n]),{logs:s,setLogs:a,paused:c,setPaused:l,errorOnly:d,setErrorOnly:u,showSystemLogs:f,logSearch:m,setLogSearch:h,logSearchFocused:v,setLogSearchFocused:b,logFollow:g,logSince:R,setLogSince:S,setLogUntil:x,logService:w,setLogService:k,logServiceB:D,setLogServiceB:O,splitLogs:F,splitFocus:W,setSplitFocus:C,toggleSplitLogs:()=>{if(F){C(0),Z(!1);return}if(D===""){let le=r.find((bt)=>bt!==w);if(le!==void 0)O(le)}Z(!0)},cycleSplitFocus:()=>C((le)=>le===0?1:0),setActiveLogService:(le)=>{let bt=(Pt)=>typeof le==="function"?le(Pt):le;if(F&&W===1){O(bt);return}k(bt)},logSliceB:Ce,logWindowB:ct,logSelectedB:j,logPinnedB:z,logServices:ne,logShowTimestamps:H,setLogShowTimestamps:ae,logShowMeta:Q,setLogShowMeta:nt,logLevel:Le,logSource:Me,logRegex:Ke,setLogRegex:gt,logWrap:Xe,setLogWrap:Ct,logPinned:ge,logSelected:at,logsFullscreen:rr,setLogsFullscreen:Qe,dashboardLogCursor:Xt,setDashboardLogCursor:qe,logFacets:mt,logSources:K,filteredLogs:A,filteredLogsB:Dt,logWindow:ve,logSlice:J,pinLogView:Po,pinLogViewB:P,applyLogCursor:se,applyLogCursorA:Jr,applyLogCursorB:wr,applyDashboardLogCursor:Bn,jumpToLatestLogs:Un,clearLogs:Ci,toggleSystemLogs:Pi}}var fS,pM=2000;var gS=I(()=>{te();gr();fS=[]});import{useCallback as ko,useState as vS}from"react";function yS({tui:e,controller:t,cfg:r,persistPrefs:n,snap:o,refresh:i,setStatus:s}){let[a,c]=vS(""),[l,d]=vS(()=>e.mcp_port??fo(t?.cfg.repoRoot??process.cwd())),u=ko((g)=>{let _=Rn(g);d(_);let R=r?.repoRoot??process.cwd();if(Dy(R,_)){n({mcp_port:null},`MCP port ${_} (default)`);return}n({mcp_port:_},`MCP port ${_}`)},[r,n]),f=ko(()=>{if(a==="")return l;let g=Ly(a,l);return c(""),u(g),g},[l,a,u]),p=ko(async(g)=>{if(!t||o?.mcp?.running!==!0)return;await t.mcpStop(),await t.mcpStart({port:g}),await i()},[t,i,o?.mcp?.running]),m=ko(async(g)=>{if(!t){s("Supervisor is not running");return}let _=o?.mcp?.disabled_tools??[],R=An(g.name,_),S=R?[..._,g.name]:_.filter((y)=>y!==g.name);try{let y=await t.mcpSetTools(S);n({mcp_disabled_tools:y},`${g.label} ${R?"disabled":"enabled"}`),await i()}catch(y){s(L(y))}},[t,n,i,o]),h=ko(async()=>{if(!t){s("Supervisor is not running");return}try{if(o?.mcp?.running)await t.mcpStop(),n({mcp_enabled:!1},"MCP off"),s("MCP stopped");else await t.mcpStart({port:l}),n({mcp_enabled:!0},"MCP on"),s("MCP started");await i()}catch(g){s(L(g))}},[t,l,n,i,o?.mcp?.running]),v=ko((g)=>{ml(g.text).then(()=>{s(`Copied ${g.title} snippet`)}).catch((_)=>{s(L(_))})},[]),b=ko((g)=>{let _=nf(g);if(_===void 0)return!1;let R=o?.mcp?.address??mi(o?.mcp?.port??l),S=Ms(R,o?.mcp?.token??"")[_];if(!S)return!1;return v(S),!0},[v,l,o?.mcp?.address,o?.mcp?.port,o?.mcp?.token]);return{mcpPortDraft:a,setMcpPortDraft:c,mcpPort:l,persistMcpPort:u,applyMcpPortDraft:f,restartMcpOnPort:p,toggleMcpTool:m,toggleMcp:h,copyMcpSnippet:v,copyFocusedMcpSnippet:b}}var bS=I(()=>{te();yl();fs();qp();$s()});import{RGBA as fM}from"@opentui/core";import{useCallback as cn,useEffect as mM,useMemo as xS,useRef as Pl,useState as _l}from"react";function wS({tui:e,controller:t,resolveTuiOverridePath:r,terminalBackground:n,userTuiConfigPath:o,snap:i,setStatus:s,saveTuiPreferences:a,setPaletteIndex:c,setOverlay:l,setConfirmKind:d,setScreen:u,setSelected:f,screen:p}){let[m,h]=_l(e.theme||t?.cfg.ui.theme||"devctl"),v=Pl(m),[b,g]=_l(e.mouse),_=Pl(e.mouse),[R,S]=_l(e.leader_timeout),y=Pl(e.leader_timeout),[x,w]=_l(()=>bs(e.font_size)),k=Pl(x),D=vb(r()),O=xS(()=>{let H=pb(m);if(Tc(m)!=="terminal"||!fp(n))return H;let ae=n??H.background;return{...H,background:ae,panel:ae,element:ae,inverse:ae}},[n,m]),F=Tc(m)==="terminal"&&fp(n)?fM.defaultBackground(n??void 0):O.background,Z=xS(()=>wb({themeName:m,fontSize:x,mouse:b,leaderMs:R,locked:D,configPath:D?e.path||yp(r(),o()):yp(r(),o()),mcpRunning:i?.mcp?.running===!0}),[x,R,b,D,i?.mcp?.running,m,e.path]),W=cn((H,ae)=>{if(D){s(`${ae} session only`);return}let Q=a(H);s(`${ae} saved ${Q}`)},[D]),C=cn((H)=>{h(H),v.current=H,W({theme:H},`theme ${H}`)},[W]),j=cn(()=>{let H=!b;g(H),_.current=H,W({mouse:H},`mouse ${H?"on":"off"} restart TUI to apply clicks`)},[b,W]),B=cn((H)=>{S(H),y.current=H,W({leader_timeout:H},`leader ${H}ms`)},[W]),z=cn((H)=>{let ae=bs(H);w(ae),k.current=ae,W({font_size:ae},`display ${Ic(ae)}`)},[W]),de=cn(()=>{let H=Mc();h(H.theme),v.current=H.theme,g(H.mouse),_.current=H.mouse,S(H.leader_timeout),y.current=H.leader_timeout,w(H.font_size),k.current=H.font_size,W(H,"restored default preferences")},[W]),Ee=cn((H)=>{if(H.id==="theme"){c(Math.max(0,St.indexOf(m))),l("themes");return}if(H.id==="mouse"){j();return}if(H.id==="leader"){B(R);return}if(H.id==="font"){z(x);return}if(H.id==="reset"){d("reset-prefs"),l("confirm");return}if(H.id==="mcp"){u("mcp"),f(0);return}s(H.detail)},[z,B,x,R,m,j]),Ne=cn((H,ae)=>{let Q=Ln(Z,ae);if(!Q)return;if(Q.id==="theme"){C(yb(m,H));return}if(Q.id==="leader"){B(bb(R,H));return}if(Q.id==="font"){z(xs(x,H));return}if(Q.id==="mouse")j()},[z,B,x,R,C,Z,m,j]),ne=cn(()=>{h(v.current)},[]);return mM(()=>{if(p==="settings")return;h(v.current),w(k.current),S(y.current),g(_.current)},[p]),{themeName:m,setThemeName:h,leaderMs:R,fontSize:x,prefsLocked:D,palette:O,rootBackground:F,settingRows:Z,persistPrefs:W,persistTheme:C,toggleMouse:j,applyFont:z,applyReset:de,activateSetting:Ee,cycleSetting:Ne,revertThemePreview:ne}}var SS=I(()=>{Er();tt()});import{jsxDEV as Ns}from"@opentui/react/jsx-dev-runtime";function kS(e){let{palette:t,termW:r,termH:n,step:o,answers:i,repo:s,draft:a,authStatus:c,onDraft:l,onSubmit:d}=e,u=wt[o]??wt[0],f=wt.length,p=hM(u.id,i,s);return Ns(pt,{palette:t,title:`setup ${o+1}/${f} ${u.title}`,bottomTitle:"enter next \xB7 esc cancel",termW:r,termH:n,preferW:64,preferH:12,borderColor:t.primary,gap:1,children:[Ns("text",{fg:t.muted,wrapMode:"word",children:p},void 0,!1,void 0,this),u.id==="auth"||u.id==="write"||u.id==="repo"?Ns("text",{fg:t.text,wrapMode:"word",children:u.id==="auth"?c:u.id==="repo"?s:"Write .devctl/config.yaml and attach the daemon."},void 0,!1,void 0,this):Ns("input",{focused:!0,value:a,placeholder:u.prompt,onInput:l,onSubmit:d,backgroundColor:t.element,focusedBackgroundColor:t.element,textColor:t.text,cursorColor:t.primary},void 0,!1,void 0,this),Ns(it,{palette:t,hints:[{key:"enter",label:u.id==="write"?"write and attach":"next"},{key:"esc",label:"cancel"}]},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function yf(e,t,r){if(e==="name")return t.name;if(e==="project")return t.project;if(e==="sa")return t.sa;if(e==="audience")return t.audience;if(e==="port")return String(t.proxyPort);if(e==="profile")return t.profile;if(e==="repo")return r;return""}function CS(e,t,r,n){let o={...t};if(e==="name")o.name=r.trim()||wn(n).name;else if(e==="project")o.project=r.trim();else if(e==="sa")o.sa=r.trim();else if(e==="audience")o.audience=r.trim();else if(e==="port")o.proxyPort=Aa(r,t.proxyPort);else if(e==="profile")o.profile=r.trim();return o}function hM(e,t,r){if(e==="repo")return`Using ${r}. The TUI always writes under this checkout.`;if(e==="auth")return"ADC login needs a real TTY. Enter continues; if ADC is missing, login runs gcloud on the terminal.";if(e==="write")return`Project ${t.name||"(unnamed)"}. Enter writes the starter file and attaches the daemon.`;return wt.find((n)=>n.id===e)?.prompt??""}var bf=I(()=>{La();ke()});import{useCallback as xf,useState as Rl}from"react";function PS({workspace:e,renderer:t,setOverlay:r,setStatus:n,setScreen:o,onAttached:i,setController:s,setCfg:a,setBootError:c}){let l=process.cwd(),[d,u]=Rl(0),[f,p]=Rl(()=>wn(l)),[m,h]=Rl(""),[v,b]=Rl("Checking Application Default Credentials\u2026"),g=xf(()=>{let S=wn(l);u(0),p(S),h(yf("repo",S,l)),r("setup-wizard"),e.detectGoogle("").then((y)=>{p((x)=>({...x,project:y.projectID||x.project})),b(y.adcAvailable?`ADC available${y.userEmail?` (${y.userEmail})`:""}. Enter continues.`:"ADC is not available. Enter runs gcloud ADC login on this TTY, then continues.")})},[l,r,e]),_=xf(async(S)=>{try{Ia(l,S);let y=await e.openTui(l,"");i?.(y),s(y),a(y.cfg),c(void 0),r("none"),o("dashboard"),n("Wrote .devctl/config.yaml and attached the daemon")}catch(y){n(L(y))}},[i,l,c,a,s,r,o,n,e]),R=xf(async()=>{let S=wt[d];if(!S)return;let y=CS(S.id,f,m,l);if(S.id==="auth"){if(!(await e.detectGoogle(y.project)).adcAvailable)try{await kl(t,()=>e.loginGoogle())}catch(D){n(L(D))}}if(p(y),S.id==="write"){await _(y);return}let x=Math.min(d+1,wt.length-1),w=wt[x];u(x),h(w?yf(w.id,y,l):"")},[f,m,_,t,l,d,e,n]);return{step:d,answers:f,draft:m,authStatus:v,repo:l,setDraft:h,startWizard:g,advanceWizard:R}}var _S=I(()=>{La();Ma();te();bf()});import{useCallback as gM,useEffect as RS,useState as wf}from"react";function ES({controller:e,cfg:t,envService:r}){let[n,o]=wf(),[i,s]=wf(!1),[a,c]=wf(""),l=n?.name===r,d=l?n?.env:void 0,u=r===""?"config":i&&!l?"loading":l?"resolved":a!==""?"error":"config",f=l||r===""?"":a;RS(()=>{o(void 0),c("")},[t]),RS(()=>{if(!e||r===""||!t?.services[r])return;if(n?.name===r)return;let m=!1;return s(!0),c(""),e.execService(r,[],!0).then((h)=>{if(m)return;o({name:r,env:h.environment??{}}),c("")}).catch((h)=>{if(m)return;c(L(h))}).finally(()=>{s(!1)}),()=>{m=!0}},[t,e,r,n?.name]);let p=gM(async(m)=>{if(!e)return;try{let h=await e.execService(m,[],!0);o({name:m,env:h.environment??{}}),c("")}catch(h){throw c(L(h)),h}},[e]);return{inspectorEnv:d,inspectorEnvStatus:u,inspectorEnvError:f,resolveEnvironment:p}}var TS=I(()=>{te()});import{jsxDEV as Nn}from"@opentui/react/jsx-dev-runtime";function AS(e){let{palette:t,path:r,initialValue:n,error:o,termW:i,termH:s,textareaRef:a}=e;return Nn(pt,{palette:t,title:"config buffer",bottomTitle:`${It("s")} save \xB7 esc discard \xB7 e still opens $EDITOR`,termW:i,termH:s,preferW:88,preferH:28,gap:1,children:[Nn("box",{height:1,overflow:"hidden",children:Nn("text",{fg:t.muted,wrapMode:"none",children:r},void 0,!1,void 0,this)},void 0,!1,void 0,this),Nn("textarea",{ref:a,focused:!0,initialValue:n,backgroundColor:t.panel,focusedBackgroundColor:t.panel,textColor:t.text,focusedTextColor:t.text,cursorColor:t.primary},void 0,!1,void 0,this),o!==""?Nn("box",{height:2,overflow:"hidden",children:Nn("text",{fg:t.error,wrapMode:"word",children:o},void 0,!1,void 0,this)},void 0,!1,void 0,this):Nn("box",{height:1,overflow:"hidden",children:Nn("text",{fg:t.muted,children:"validate, then write. invalid YAML is not saved."},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}var LS=I(()=>{ke();Rr()});import{jsxDEV as Sf}from"@opentui/react/jsx-dev-runtime";function DS(e){let{palette:t,title:r,body:n,kind:o,termW:i,termH:s}=e,a=lx(o),c=a.map((l)=>`${l.key} ${l.label}`).join(" \xB7 ");return Sf(pt,{palette:t,title:r,bottomTitle:c,termW:i,termH:s,preferW:52,preferH:8,borderColor:t.warning,gap:1,children:[Sf("text",{fg:t.text,wrapMode:"word",children:n},void 0,!1,void 0,this),Sf(it,{palette:t,hints:a},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}var IS=I(()=>{vr();ke()});import{jsxDEV as MS}from"@opentui/react/jsx-dev-runtime";function OS(e){let{palette:t,termW:r,termH:n}=e,o=Hp();return MS(pt,{palette:t,title:"leader",termW:r,termH:n,preferW:68,preferH:5,anchor:"bottom",children:MS(it,{palette:t,hints:o},void 0,!1,void 0,this)},void 0,!1,void 0,this)}var $S=I(()=>{wo();ke()});import{jsxDEV as Jt,Fragment as vM}from"@opentui/react/jsx-dev-runtime";function FS(e){let{palette:t,event:r,termW:n,termH:o,scrollRef:i}=e;if(!r)return null;let s=r.raw?Fb(r.raw):void 0,a=r.message.length>120||r.message.includes(`
2231
+ `)||s!==void 0;return Jt(pt,{palette:t,title:"log details",bottomTitle:"j/k scroll \xB7 esc close",termW:n,termH:o,preferW:a?84:72,preferH:a?22:14,gap:1,children:Jt("scrollbox",{ref:i,focused:!1,stickyScroll:!1,scrollX:!1,style:Ue(t),children:Jt("box",{flexDirection:"column",overflow:"hidden",children:[Jt("text",{fg:t.text,wrapMode:"word",children:Jc(r.message)},void 0,!1,void 0,this),Jt("text",{fg:t.muted,children:`time ${r.timestamp}`},void 0,!1,void 0,this),Jt("text",{fg:Ze(r.service,t),children:`service ${r.service}`},void 0,!1,void 0,this),Jt("text",{fg:t.muted,children:`source ${r.source}${r.stream?` / ${r.stream}`:""}`},void 0,!1,void 0,this),Jt("text",{fg:t.muted,children:`level ${Yc(r.level)}`},void 0,!1,void 0,this),Jt("text",{fg:t.muted,children:`pid ${r.pid||"\u2014"}`},void 0,!1,void 0,this),Jt("text",{fg:t.muted,children:`request ${r.request_id||"\u2014"}`},void 0,!1,void 0,this),Jt("text",{fg:t.muted,children:`identity ${r.identity||"\u2014"}`},void 0,!1,void 0,this),s===void 0?null:Jt(vM,{children:[Jt("text",{fg:t.muted,children:"raw json"},void 0,!1,void 0,this),Jt("text",{fg:t.text,wrapMode:"word",children:s},void 0,!1,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this)}var NS=I(()=>{gr();ke();tt()});import{jsxDEV as U}from"@opentui/react/jsx-dev-runtime";function kf(e,t){switch(e){case"completed":return{icon:"\u2713",label:"Completed",tone:"success",color:t.success};case"failed":return{icon:"\u2717",label:"Failed",tone:"error",color:t.error};case"unhealthy":return{icon:"\u26A0",label:"Unhealthy",tone:"warning",color:t.warning};case"active":return{icon:"\u25CF",label:"In progress",tone:"primary",color:t.primary};default:return{icon:"\u25CB",label:"Queued",tone:"muted",color:t.muted}}}function yM(e,t,r,n){if(t!=="")return{icon:"\u2717",label:"Failed",tone:"error",color:n.error};if(!e)return{icon:"\u2713",label:"Finished",tone:"success",color:n.success};if(r==="unhealthy")return{icon:"\u26A0",label:"Waiting on health",tone:"warning",color:n.warning};return{icon:"\u25CF",label:"Running",tone:"primary",color:n.primary}}function HS(e){return U("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,children:U("text",{wrapMode:"none",children:[U("span",{fg:e.palette.primary,children:e.label.toUpperCase()},void 0,!1,void 0,this),e.detail?U("span",{fg:e.palette.muted,children:` ${e.detail}`},void 0,!1,void 0,this):null]},void 0,!0,void 0,this)},void 0,!1,void 0,this)}function jS(e){let{palette:t,plan:r,snap:n,busy:o,failed:i,kind:s,termW:a=80,termH:c=24,scrollRef:l,initiallyRunning:d=[],onDismiss:u}=e,f=new Set(d),p=r.waves.map((C,j)=>({wave:C.filter((B)=>!f.has(B)),waveIdx:j})).filter(({wave:C})=>C.length>0),m=Rw(r,n,s),h=r.waves[m.currentWaveIndex]??[],v=Fs(h,n,s),b=kf(v,t),g=yM(o,i,v,t),_=Tw(s,o,i,r.profile),R=Aw(o,i),S=i?t.inverse:t.text,y=i?t.error:t.element,x=Math.min(92,Math.max(40,a-8)),w=x>=82?22:x>=66?17:13,k=12,D=9,O=x>=56,F=x>=72,Z=x<72||r.waves.length>3,W=r.profile?`PROFILE ${r.profile}`:s.toUpperCase();return U(pt,{palette:t,title:_,bottomTitle:"enter close \xB7 esc hide (operation continues)",termW:a,termH:c,preferW:98,preferH:28,borderColor:g.color,gap:0,children:[U("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,backgroundColor:t.element,children:[U(Be,{palette:t,label:`${g.icon} ${g.label.toUpperCase()}`,tone:g.tone},void 0,!1,void 0,this),U("box",{paddingLeft:1,flexGrow:1,overflow:"hidden",children:U("text",{fg:t.text,wrapMode:"none",children:W},void 0,!1,void 0,this)},void 0,!1,void 0,this),U(Be,{palette:t,label:`${m.ready}/${m.total} READY`,tone:m.isComplete?"success":"info"},void 0,!1,void 0,this),U(Be,{palette:t,label:`WAVE ${Math.min(m.currentWaveIndex+1,Math.max(1,m.totalWaves))}/${Math.max(1,m.totalWaves)}`,tone:b.tone},void 0,!1,void 0,this)]},void 0,!0,void 0,this),U("box",{height:2,flexDirection:"column",overflow:"hidden",flexShrink:0,paddingLeft:1,paddingRight:1,children:[U("box",{height:1,flexDirection:"row",overflow:"hidden",children:[U("text",{wrapMode:"none",children:[U("span",{fg:t.muted,children:"PROGRESS "},void 0,!1,void 0,this),U("span",{fg:g.color,children:`[${m.progressBar}]`},void 0,!1,void 0,this),U("span",{fg:t.text,children:` ${m.percent}%`},void 0,!1,void 0,this)]},void 0,!0,void 0,this),U("box",{flexGrow:1},void 0,!1,void 0,this),U("text",{wrapMode:"none",children:U("span",{fg:b.color,children:`${b.icon} ${b.label}`},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this),U("box",{height:1,overflow:"hidden",children:U("text",{fg:t.muted,wrapMode:"none",children:o?s==="stop"?"Dependents stop first; each wave waits for the previous wave.":"Each wave waits until its services are healthy before the next wave starts.":R.primary},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this),U(HS,{palette:t,label:"Pipeline",detail:`${r.waves.length} execution wave${r.waves.length===1?"":"s"}`},void 0,!1,void 0,this),U("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,backgroundColor:t.element,children:r.waves.map((C,j)=>{let B=Fs(C,n,s),z=kf(B,t),de=Z?`W${j+1} ${z.icon} ${C.length}`:`W${j+1} ${z.icon} ${z.label} \xB7 ${C.length}`;return U("box",{flexDirection:"row",overflow:"hidden",flexShrink:0,children:[U(Be,{palette:t,label:de,tone:z.tone},void 0,!1,void 0,this),j<r.waves.length-1?U("box",{width:3,flexShrink:0,overflow:"hidden",children:U("text",{fg:t.muted,children:" \u2500\u25BA"},void 0,!1,void 0,this)},void 0,!1,void 0,this):null]},`step-${j}`,!0,void 0,this)})},void 0,!1,void 0,this),(r.blockers??[]).length>0?U("box",{minHeight:1,flexDirection:"row",overflow:"hidden",flexShrink:0,backgroundColor:t.element,paddingLeft:1,children:U("text",{fg:t.error,wrapMode:"word",children:`BLOCKED ${r.blockers?.map((C)=>`${C.name}: ${C.message}`).join(" \xB7 ")}`},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,d.length>0?U("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,paddingLeft:1,children:U("text",{wrapMode:"none",children:[U("span",{fg:t.success,children:"\u2713 ALREADY READY"},void 0,!1,void 0,this),U("span",{fg:t.muted,children:` ${d.join(", ")}`},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this):null,U(HS,{palette:t,label:"Wave details",detail:"live process and health state"},void 0,!1,void 0,this),U("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,backgroundColor:t.element,paddingLeft:1,children:[U("box",{width:2,flexShrink:0},void 0,!1,void 0,this),U("box",{width:w,flexShrink:0,overflow:"hidden",children:U("text",{fg:t.muted,children:oe("SERVICE",w)},void 0,!1,void 0,this)},void 0,!1,void 0,this),U("box",{width:12,flexShrink:0,overflow:"hidden",children:U("text",{fg:t.muted,children:oe("STATE",12)},void 0,!1,void 0,this)},void 0,!1,void 0,this),O?U("box",{width:9,flexShrink:0,overflow:"hidden",children:U("text",{fg:t.muted,children:oe("PORT",9)},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,F?U("box",{flexGrow:1,overflow:"hidden",children:U("text",{fg:t.muted,children:"DETAIL"},void 0,!1,void 0,this)},void 0,!1,void 0,this):null]},void 0,!0,void 0,this),U("scrollbox",{ref:l,focused:!1,stickyScroll:!1,scrollX:!1,style:Ue(t),children:U("box",{flexDirection:"column",overflow:"hidden",gap:1,children:p.map(({wave:C,waveIdx:j})=>{let B=Fs(C,n,s),z=kf(B,t),de=`${Ew(s,j,B)} \xB7 ${C.length} service${C.length===1?"":"s"}`;return U("box",{border:!0,borderStyle:"rounded",borderColor:z.color,title:de,titleColor:z.color,flexDirection:"column",paddingLeft:1,paddingRight:1,flexShrink:0,overflow:"hidden",children:C.map((Ee)=>{let Ne=n?.services[Ee],ne=tr(Ne),H=_w(Ee,r,n,s),ae=Ee===i||ne==="FAILED",Q=ne==="UNHEALTHY",nt=ne==="HEALTHY"||s==="stop"&&ne==="STOPPED",ft=go(Ne),ie=ae?"\u2717":nt?"\u2713":Q?"\u26A0":ne==="STARTING"||ne==="RUNNING"||ne==="STOPPING"||ne==="RESTARTING"?"\u25CF":"\u25CB",Le=ae?t.error:et(t,ne),Me=ae?t.error:Q?t.warning:nt?t.success:t.muted;return U("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,children:[U("box",{width:2,flexShrink:0,overflow:"hidden",children:U("text",{fg:Le,children:ie},void 0,!1,void 0,this)},void 0,!1,void 0,this),U("box",{width:w,flexShrink:0,overflow:"hidden",children:U("text",{fg:Ze(Ee,t),wrapMode:"none",children:oe(Ee,w)},void 0,!1,void 0,this)},void 0,!1,void 0,this),U("box",{width:12,flexShrink:0,overflow:"hidden",children:U("text",{fg:et(t,ne),wrapMode:"none",children:oe(ne,12)},void 0,!1,void 0,this)},void 0,!1,void 0,this),O?U("box",{width:9,flexShrink:0,overflow:"hidden",children:U("text",{fg:ft?t.info:t.muted,wrapMode:"none",children:oe(ft?`:${ft}`:"\u2014",9)},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,F?U("box",{flexGrow:1,overflow:"hidden",children:U("text",{fg:Me,wrapMode:"none",children:H},void 0,!1,void 0,this)},void 0,!1,void 0,this):null]},Ee,!0,void 0,this)})},`wave-card-${j}`,!1,void 0,this)})},void 0,!1,void 0,this)},void 0,!1,void 0,this),U("box",{height:3,flexShrink:0,flexDirection:"column",backgroundColor:y,border:["top"],borderStyle:"single",borderColor:g.color,paddingLeft:1,paddingRight:1,overflow:"hidden",onMouseDown:u,children:[U("text",{fg:S,wrapMode:"none",children:R.primary},void 0,!1,void 0,this),U("text",{fg:i?t.inverse:t.muted,wrapMode:"none",children:R.secondary},void 0,!1,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)}var BS=I(()=>{Ot();xl();Lt();ke();tt()});import{jsxDEV as yr}from"@opentui/react/jsx-dev-runtime";function US(e){let{palette:t,route:r,termW:n,termH:o,scrollRef:i}=e;if(!r)return null;return yr(pt,{palette:t,title:r.name,bottomTitle:"j/k scroll \xB7 esc close",termW:n,termH:o,preferW:78,preferH:18,gap:1,children:yr("scrollbox",{ref:i,focused:!1,stickyScroll:!1,scrollX:!1,style:Ue(t),children:yr("box",{flexDirection:"column",overflow:"hidden",children:[yr("text",{fg:t.muted,children:`auth ${r.authType||"none"}`},void 0,!1,void 0,this),yr("text",{fg:t.muted,children:`identity ${r.identityType||"\u2014"}`},void 0,!1,void 0,this),yr("text",{fg:t.text,wrapMode:"word",children:`account ${r.serviceAccount||"\u2014"}`},void 0,!1,void 0,this),yr("text",{fg:t.text,wrapMode:"word",children:`audience ${r.audience||"\u2014"}`},void 0,!1,void 0,this),yr("text",{fg:t.muted,wrapMode:"word",children:`client id ${r.clientId||"ADC default"}`},void 0,!1,void 0,this),yr("text",{fg:t.muted,wrapMode:"word",children:`client secret ${r.clientSecret||"\u2014"}`},void 0,!1,void 0,this),yr("text",{fg:t.muted,children:`match host ${r.matchHost||"*"}`},void 0,!1,void 0,this),yr("text",{fg:t.muted,children:`match path ${r.matchPath||"*"}`},void 0,!1,void 0,this),yr("text",{fg:t.text,wrapMode:"word",children:`upstream ${r.upstream}`},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this)}var WS=I(()=>{ke()});import{jsxDEV as El}from"@opentui/react/jsx-dev-runtime";function GS(e){let{palette:t,title:r,body:n,termW:o,termH:i,scrollRef:s}=e,a=n.length>400||n.split(`
2232
+ `).length>16;return El(pt,{palette:t,title:r,bottomTitle:"j/k scroll \xB7 esc close",termW:o,termH:i,preferW:a?88:76,preferH:a?24:16,gap:1,children:El("scrollbox",{ref:s,focused:!1,stickyScroll:!1,scrollX:!1,style:Ue(t),children:El("box",{flexDirection:"column",overflow:"hidden",children:El("text",{fg:n===""?t.muted:t.text,wrapMode:"word",children:n===""?"(empty)":n},void 0,!1,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this)}var KS=I(()=>{ke()});import{jsxDEV as Wt}from"@opentui/react/jsx-dev-runtime";function qS(e){let{palette:t,items:r,query:n,selected:o,onQuery:i,onSubmit:s,title:a="commands"}=e,c=xo(n)!=="",l=Lx(o,Cf,r.length),d=c?r.slice(l,l+Cf):Dx(r,o,Cf),u=c?[{group:"",items:d}]:$p(d),f=Math.max(d.length+(c?0:u.length),1),p=ui(r,o),m=_x(r);return Wt("box",{height:f+bM,flexShrink:0,border:!0,borderStyle:"rounded",borderColor:t.borderActive,titleColor:t.primary,backgroundColor:t.panel,title:a,flexDirection:"column",overflow:"hidden",children:[d.length===0?Wt("box",{height:1,paddingLeft:1,overflow:"hidden",children:Wt("text",{fg:t.muted,children:"no matching command"},void 0,!1,void 0,this)},void 0,!1,void 0,this):u.flatMap((h)=>[...h.group===""?[]:[Wt("box",{height:1,paddingLeft:1,overflow:"hidden",backgroundColor:t.panel,children:Wt("text",{fg:t.muted,wrapMode:"none",children:h.group},void 0,!1,void 0,this)},`g-${h.group}`,!1,void 0,this)],...h.items.map((b)=>{let g=sl(b)===(p?sl(p):"");return Wt("box",{height:1,flexDirection:"row",overflow:"hidden",paddingLeft:1,paddingRight:1,backgroundColor:g?t.highlight:t.panel,children:[Wt("box",{width:m,flexShrink:0,overflow:"hidden",children:Wt("text",{fg:g?t.primary:t.text,wrapMode:"none",children:`${g?"\u203A":" "} /${Fp(b).slice(0,Op)}`},void 0,!1,void 0,this)},void 0,!1,void 0,this),Wt("box",{flexGrow:1,overflow:"hidden",children:Wt("text",{fg:g?t.accent:t.muted,wrapMode:"none",children:Px(b)},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},sl(b),!0,void 0,this)})]),Wt("box",{height:1,flexDirection:"row",overflow:"hidden",backgroundColor:t.highlight,paddingLeft:1,children:[Wt("box",{width:2,flexShrink:0,children:Wt("text",{fg:t.primary,children:"/"},void 0,!1,void 0,this)},void 0,!1,void 0,this),Wt("box",{flexGrow:1,overflow:"hidden",children:Wt("input",{focused:!0,value:n,placeholder:"start logs settings",onInput:i,onSubmit:()=>s(),backgroundColor:t.highlight,focusedBackgroundColor:t.highlight,textColor:t.text,cursorColor:t.primary},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)}var Cf=10,bM=3;var VS=I(()=>{wo()});import{jsxDEV as JS}from"@opentui/react/jsx-dev-runtime";function XS(e){let{palette:t,themeName:r,selected:n,termW:o,termH:i,onIndex:s,onPreview:a,onPick:c}=e,l=St.map((u)=>({name:u===r?`${u} (active)`:u,description:u===r?`${pp[u]} \xB7 current`:pp[u],value:u})),d=ol(o,i,zS,YS,"center",!ho(ye()));return JS(pt,{palette:t,title:"/themes",bottomTitle:"enter save \xB7 esc revert",termW:o,termH:i,preferW:zS,preferH:YS,children:JS("select",{height:Math.max(4,d.height-3),options:l,selectedIndex:n,showDescription:!0,showSelectionIndicator:!0,wrapSelection:!0,backgroundColor:t.panel,focusedBackgroundColor:t.panel,textColor:t.text,focusedTextColor:t.text,selectedBackgroundColor:t.highlight,selectedTextColor:t.primary,descriptionColor:t.muted,selectedDescriptionColor:t.accent,onChange:(u)=>{s(u);let f=St[u];if(f)a(f)},onSelect:(u,f)=>{let p=f?.value;if(typeof p==="string"&&p!=="")c(p)}},void 0,!1,void 0,this)},void 0,!1,void 0,this)}var zS=52,YS=22;var QS=I(()=>{Mt();vr();ke();Er();tt()});function Tl(e,t,r){let n=e?.google?.project_id??"";if(n!=="")return{project:n,source:"configuration"};let o=t?.project||r?.projectID||"",i=t?.project_source||r?.projectSource||"";return{project:o,source:i}}import{jsxDEV as st}from"@opentui/react/jsx-dev-runtime";function Al(e){let{palette:t,title:r,tone:n="muted",children:o}=e,i=n==="success"?t.success:n==="warning"?t.warning:n==="error"?t.error:t.border;return st("box",{border:!0,borderStyle:"rounded",borderColor:i,title:r,titleColor:i,flexDirection:"column",flexShrink:0,paddingLeft:1,paddingRight:1,marginBottom:1,overflow:"hidden",children:o},void 0,!1,void 0,this)}function ZS(e){let{palette:t,cfg:r,google:n,identity:o}=e;if(!n&&!o)return st(fl,{palette:t,label:"Checking Application Default Credentials\u2026"},void 0,!1,void 0,this);let i=xM(r,o),s=o?.user||n?.userEmail||"",{project:a,source:c}=Tl(r,o,n),l=o?.adc??n?.adcAvailable===!0,d=o?.iap??(r?.proxy.routes??[]).some((h)=>h.auth.type.toLowerCase()==="iap"),u=i.filter((h)=>h.inactive!==!0),f=u.some((h)=>h.ok===!0),p=u.some((h)=>h.ok===!1),m=f?"AVAILABLE":p?"UNAVAILABLE":u.length>0?"NOT PROBED":i.length>0?"not required":"not configured";return st(kt,{palette:t,title:"identity",scroll:!0,children:[st(Al,{palette:t,title:"Google identity",tone:l?"success":"warning",children:[st(So,{palette:t,label:"user",value:s||"(unknown)"},void 0,!1,void 0,this),st(So,{palette:t,label:"project",value:a||"(unset)"},void 0,!1,void 0,this),st(So,{palette:t,label:"source",value:c||"\u2014"},void 0,!1,void 0,this),st(So,{palette:t,label:"ADC",value:l?"\u2713 available":"\u2717 missing",tone:l?"success":"error"},void 0,!1,void 0,this),st(So,{palette:t,label:"gcloud",value:n?.gcloudInstalled?"\u2713 installed":"\u2717 not installed",tone:n?.gcloudInstalled?"success":"error"},void 0,!1,void 0,this)]},void 0,!0,void 0,this),st(Al,{palette:t,title:`Service accounts (${i.length})`,tone:u.length===0?"muted":f?"success":"warning",children:[i.length===0?st("text",{fg:t.muted,wrapMode:"word",children:"none configured \u2014 identities come from config, never hard-coded"},void 0,!1,void 0,this):i.map((h)=>st("text",{fg:h.inactive?t.muted:h.ok===!0?t.success:h.ok===!1?t.error:t.text,wrapMode:"none",children:`${h.inactive?"\u2013":h.ok===!0?"\u2713":h.ok===!1?"\u2717":"\u25CB"} ${h.email}${h.inactive?" \u2014 inactive (route auth is none)":""}`},h.email,!1,void 0,this)),st("box",{height:1,flexShrink:0},void 0,!1,void 0,this),st(So,{palette:t,label:"impersonation",value:m,tone:f?"success":u.length>0?"warning":"muted"},void 0,!1,void 0,this)]},void 0,!0,void 0,this),st(Al,{palette:t,title:"IAP",tone:d?"success":"muted",children:[st("box",{flexDirection:"row",overflow:"hidden",flexShrink:0,children:st(Be,{palette:t,label:d?"routes present":"no IAP routes",tone:d?"success":"idle"},void 0,!1,void 0,this)},void 0,!1,void 0,this),d?st("text",{fg:t.muted,wrapMode:"word",children:"each route needs its own audience"},void 0,!1,void 0,this):null]},void 0,!0,void 0,this),!l?st(Al,{palette:t,title:"ADC unavailable",tone:"warning",children:[st("text",{fg:t.text,wrapMode:"word",children:"Local services still start. Cloud identity, impersonation, and IAP need ADC."},void 0,!1,void 0,this),st("text",{fg:t.muted,wrapMode:"word",children:"/auth login suspends this screen and runs gcloud ADC login, then restores the TUI"},void 0,!1,void 0,this)]},void 0,!0,void 0,this):null]},void 0,!0,void 0,this)}function xM(e,t){let r=new Map((e?iu(e):[]).map((i)=>[i.email,i.active])),n=t?.service_account_status??{};return[...new Set([...r.keys(),...Object.keys(n)])].sort((i,s)=>i.localeCompare(s)).map((i)=>{let s=n[i]??"unknown",a=r.get(i)===!1;return{email:i,inactive:a,ok:a||s==="unknown"?void 0:s==="available"}})}var ek=I(()=>{jr();Yt();ke()});import{jsxDEV as M}from"@opentui/react/jsx-dev-runtime";function ok(e){let{palette:t,cfg:r,width:n,selectedTask:o=0}=e,i=ye();if(!r)return M(ze,{palette:t,title:"No configuration",body:"Create .devctl/config.yaml or run setup."},void 0,!1,void 0,this);let s=Math.max(vi+8,n-6-i.pad*2),a=s>=Qw,c=a?Math.floor((s-rk)/2):s,l=Gw(r),d=Kw(r),u=qw(r),f=Vw(r),p=zw(r);return M(kt,{palette:t,title:`config ${r.project.name||"unnamed"}`,children:[M(ut,{palette:t,items:Nw(r)},void 0,!1,void 0,this),M("box",{flexGrow:1,overflow:"hidden",children:M("scrollbox",{ref:e.scrollRef,focused:!1,stickyScroll:!1,scrollX:!1,style:Ue(t),children:M("box",{flexDirection:"column",overflow:"hidden",gap:nk,children:[M(Pf,{wide:a,left:M(qr,{palette:t,title:"project",children:M(bi,{palette:t,facts:Hw(r,jt),width:c},void 0,!1,void 0,this)},void 0,!1,void 0,this),right:M(qr,{palette:t,title:"google",children:M(bi,{palette:t,facts:jw(r),width:c},void 0,!1,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this),M(Pf,{wide:a,left:M(qr,{palette:t,title:"runtime",children:M(bi,{palette:t,facts:Bw(r),width:c},void 0,!1,void 0,this)},void 0,!1,void 0,this),right:M(qr,{palette:t,title:"logs",children:M(bi,{palette:t,facts:Uw(r),width:c},void 0,!1,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this),M(qr,{palette:t,title:`proxy ${r.proxy.enabled?"enabled":"disabled"}`,children:[M(bi,{palette:t,facts:Ww(r),width:s},void 0,!1,void 0,this),M(SM,{palette:t,routes:d,width:s},void 0,!1,void 0,this)]},void 0,!0,void 0,this),M(qr,{palette:t,title:`services ${l.length}`,children:M(wM,{palette:t,rows:l,width:s},void 0,!1,void 0,this)},void 0,!1,void 0,this),M(qr,{palette:t,title:`tasks ${p.length}`,children:M(tk,{palette:t,rows:p,empty:"no tasks",width:s,selected:o},void 0,!1,void 0,this)},void 0,!1,void 0,this),M(Pf,{wide:a,left:M(qr,{palette:t,title:`profiles ${u.length}`,children:M(kM,{palette:t,rows:u,width:c},void 0,!1,void 0,this)},void 0,!1,void 0,this),right:M(qr,{palette:t,title:`templates ${f.length}`,children:M(tk,{palette:t,rows:f,empty:"no templates",width:c},void 0,!1,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this),M(qr,{palette:t,title:"extras",children:M(bi,{palette:t,facts:Jw(r),width:s},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this),M(Ut,{palette:t,backgroundColor:t.element,edge:"top",children:M(it,{palette:t,hints:[{key:"v",label:"validate/save buffer"},{key:"e",label:"open in $EDITOR"},{key:"/diff",label:"config sources"},{key:"/reload",label:"re-read .devctl after editing"},{key:"enter",label:"run task"},{key:"j/k",label:"scroll"}]},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function Pf(e){let{wide:t,left:r,right:n}=e;if(!t)return M("box",{flexDirection:"column",overflow:"hidden",gap:nk,flexShrink:0,children:[r,n]},void 0,!0,void 0,this);return M("box",{flexDirection:"row",overflow:"hidden",gap:rk,flexShrink:0,children:[M("box",{flexGrow:1,flexBasis:0,overflow:"hidden",children:r},void 0,!1,void 0,this),M("box",{flexGrow:1,flexBasis:0,overflow:"hidden",children:n},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function qr(e){let{palette:t,title:r,children:n}=e;return M("box",{border:!0,borderStyle:"rounded",borderColor:t.border,title:r,titleColor:t.accent,paddingLeft:1,paddingRight:1,flexDirection:"column",overflow:"hidden",flexShrink:0,children:n},void 0,!1,void 0,this)}function bi(e){let{palette:t,facts:r,width:n}=e,o=Math.max(4,n-vi-4);return M("box",{flexDirection:"column",overflow:"hidden",flexShrink:0,children:r.map((i)=>M("box",{height:1,flexDirection:"row",overflow:"hidden",children:[M("box",{width:vi,flexShrink:0,overflow:"hidden",children:M("text",{fg:t.muted,wrapMode:"none",children:oe(i.label,vi)},void 0,!1,void 0,this)},void 0,!1,void 0,this),M("box",{width:o,flexShrink:0,overflow:"hidden",children:M("text",{fg:_M(t,i.tone),wrapMode:"none",children:oe(i.value,o)},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},i.label,!0,void 0,this))},void 0,!1,void 0,this)}function wM(e){let{palette:t,rows:r,width:n}=e;if(r.length===0)return M(Ll,{palette:t,text:"no services"},void 0,!1,void 0,this);let o=Xw(r),i=Math.max(8,n-o-6);return M("box",{flexDirection:"column",overflow:"hidden",flexShrink:0,children:r.map((s)=>M("box",{height:1,flexDirection:"row",overflow:"hidden",children:[M("box",{width:o,flexShrink:0,overflow:"hidden",children:M("text",{fg:Ze(s.name,t),wrapMode:"none",children:oe(s.name,o)},void 0,!1,void 0,this)},void 0,!1,void 0,this),M("box",{width:i,flexShrink:0,overflow:"hidden",children:M("text",{fg:t.text,wrapMode:"none",children:Re(CM(s),i)},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},s.name,!0,void 0,this))},void 0,!1,void 0,this)}function SM(e){let{palette:t,routes:r,width:n}=e;if(r.length===0)return M(Ll,{palette:t,text:"no routes"},void 0,!1,void 0,this);let o=Math.min(vi+4,Math.max(8,...r.map((s)=>s.name.length))),i=Math.max(8,n-o-6);return M("box",{flexDirection:"column",overflow:"hidden",flexShrink:0,children:r.map((s)=>M("box",{height:1,flexDirection:"row",overflow:"hidden",children:[M("box",{width:o,flexShrink:0,overflow:"hidden",children:M("text",{fg:t.primary,wrapMode:"none",children:oe(s.name,o)},void 0,!1,void 0,this)},void 0,!1,void 0,this),M("box",{width:i,flexShrink:0,overflow:"hidden",children:M("text",{fg:t.text,wrapMode:"none",children:Re(PM(s),i)},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},`${s.name}-${s.match}`,!0,void 0,this))},void 0,!1,void 0,this)}function kM(e){let{palette:t,rows:r,width:n}=e;if(r.length===0)return M(Ll,{palette:t,text:"no profiles"},void 0,!1,void 0,this);let o=Math.min(12,Math.max(8,...r.map((s)=>s.name.length))),i=Math.max(8,n-o-6);return M("box",{flexDirection:"column",overflow:"hidden",flexShrink:0,children:r.map((s)=>M("box",{height:1,flexDirection:"row",overflow:"hidden",children:[M("box",{width:o,flexShrink:0,overflow:"hidden",children:M("text",{fg:t.primary,wrapMode:"none",children:oe(s.name,o)},void 0,!1,void 0,this)},void 0,!1,void 0,this),M("box",{width:i,flexShrink:0,overflow:"hidden",children:M("text",{fg:t.text,wrapMode:"none",children:Re(s.env==="\u2014"?s.services:`${s.services} env ${s.env}`,i)},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},s.name,!0,void 0,this))},void 0,!1,void 0,this)}function tk(e){let{palette:t,rows:r,empty:n,width:o,selected:i}=e;if(r.length===0)return M(Ll,{palette:t,text:n},void 0,!1,void 0,this);let s=Math.min(14,Math.max(8,...r.map((c)=>c.name.length))),a=Math.max(8,o-s-6);return M("box",{flexDirection:"column",overflow:"hidden",flexShrink:0,children:r.map((c,l)=>{let d=i===l;return M("box",{height:1,flexDirection:"row",overflow:"hidden",backgroundColor:d?t.highlight:void 0,children:[M("box",{width:s,flexShrink:0,overflow:"hidden",children:M("text",{fg:d?t.primary:t.text,wrapMode:"none",children:oe(c.name,s)},void 0,!1,void 0,this)},void 0,!1,void 0,this),M("box",{width:a,flexShrink:0,overflow:"hidden",children:M("text",{fg:d?t.accent:t.muted,wrapMode:"none",children:Re(c.summary,a)},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},c.name,!0,void 0,this)})},void 0,!1,void 0,this)}function Ll(e){return M("box",{height:1,overflow:"hidden",children:M("text",{fg:e.palette.muted,wrapMode:"none",children:e.text},void 0,!1,void 0,this)},void 0,!1,void 0,this)}function CM(e){let t=e.extends==="\u2014"?"":` ${e.extends}`;return` ${e.ports} ${e.identity} ${e.health} ${e.restart} \u2190 ${e.depends}${t}`}function PM(e){return` ${e.auth} ${e.identity} ${e.match} \u2192 ${e.upstream}`}function _M(e,t){if(t==="success")return e.success;if(t==="warning")return e.warning;if(t==="error")return e.error;if(t==="muted")return e.muted;return e.text}var rk=1,nk=1;var ik=I(()=>{Yt();gf();Mt();Ot();ke();tt()});import{jsxDEV as Ft}from"@opentui/react/jsx-dev-runtime";function sk(e){let{palette:t,credentials:r}=e;if(!r)return Ft(fl,{palette:t,label:"Reading credential store\u2026"},void 0,!1,void 0,this);let n=r.entries.filter((i)=>i.valid).length,o=r.entries.length-n;return Ft(kt,{palette:t,title:"credentials",scroll:!0,children:[Ft("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,children:[Ft(Be,{palette:t,label:`backend: ${r.backend}`,tone:"info"},void 0,!1,void 0,this),r.entries.length>0?Ft(Be,{palette:t,label:`${n} valid`,tone:"success"},void 0,!1,void 0,this):null,o>0?Ft(Be,{palette:t,label:`${o} expired`,tone:"warning"},void 0,!1,void 0,this):null]},void 0,!0,void 0,this),Ft("text",{fg:t.muted,wrapMode:"word",children:"Tokens stay in the OS keychain or ~/.devctl/credentials (0600). They are never shown here."},void 0,!1,void 0,this),Ft("box",{height:1,flexShrink:0},void 0,!1,void 0,this),r.entries.length===0?Ft("text",{fg:t.muted,children:"no cached credentials"},void 0,!1,void 0,this):Ft("box",{flexDirection:"column",overflow:"hidden",gap:1,children:r.entries.map((i)=>Ft("box",{border:!0,borderStyle:"rounded",borderColor:i.valid?t.success:t.warning,title:i.identity,titleColor:i.valid?t.success:t.warning,flexDirection:"column",flexShrink:0,paddingLeft:1,paddingRight:1,overflow:"hidden",children:[Ft("text",{wrapMode:"none",children:[Ft("span",{fg:i.valid?t.success:t.warning,children:i.valid?"\u2713 valid":"\u2717 expired"},void 0,!1,void 0,this),Ft("span",{fg:t.muted,children:` \xB7 expires ${i.expires_at}`},void 0,!1,void 0,this)]},void 0,!0,void 0,this),Ft("text",{fg:t.muted,wrapMode:"none",children:`audience: ${i.audience||"(none)"}`},void 0,!1,void 0,this)]},`${i.identity}|${i.audience}`,!0,void 0,this))},void 0,!1,void 0,this),Ft(pl,{palette:t,title:"Actions",body:"Use /refresh to mint again or auth refresh on the CLI. Invalidate clears the store without printing tokens.",hint:"/refresh /auth"},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}var ak=I(()=>{Yt();ke()});import{jsxDEV as Dl}from"@opentui/react/jsx-dev-runtime";function xi(e){let t=e.newer>0?`g jump to latest \xB7 ${e.newer} new`:"g jump to latest",r=Math.min(e.width,t.length+4);return Dl("box",{position:"absolute",left:Math.max(0,Math.floor((e.width-r)/2)),bottom:e.bottom??2,width:r,height:3,border:!0,borderStyle:"rounded",borderColor:e.palette.warning,backgroundColor:e.palette.panel,alignItems:"center",justifyContent:"center",onMouseDown:e.onJump,children:Dl("text",{wrapMode:"none",children:[Dl("span",{fg:e.palette.primary,children:"g"},void 0,!1,void 0,this),Dl("span",{fg:e.palette.text,children:t.slice(1)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this)}var ck=()=>{};import{jsxDEV as Hs}from"@opentui/react/jsx-dev-runtime";function wi(e){let{palette:t,logs:r,names:n,service:o,errorOnly:i,width:s,onService:a,onToggleErrors:c,facets:l}=e,d=l?Yb(n,l):zb(n,r),u=s<80,f=d.map((v)=>{let b=v.name===""?"all":v.name;return{id:b,label:u?b:`${b} \xB7 ${v.count}`,color:Ze(v.name,t)}}),p=Math.max(0,d.findIndex((v)=>v.name===o)),m=i?"ERROR+":u?"lvls":"all levels",h=Math.max(ci(f[p]?.label??"all"),s-ci(m));return Hs(Ut,{palette:t,backgroundColor:t.element,children:Hs("box",{height:1,flexDirection:"row",overflow:"hidden",backgroundColor:t.element,children:[Hs("box",{flexGrow:1,overflow:"hidden",children:Hs(ul,{palette:t,items:f,active:p,width:h,emphasis:"fill",onPick:(v)=>{let b=d[v];if(b)a(b.name)}},void 0,!1,void 0,this)},void 0,!1,void 0,this),Hs(Be,{palette:t,label:m,tone:i?"error":"muted",onMouseDown:c},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this)}var lk=I(()=>{gr();In();ke();tt()});import{jsxDEV as RM}from"@opentui/react/jsx-dev-runtime";function js(e){let t=Math.min(e.total,e.start+e.count),r=Math.max(0,e.start),n=Math.max(0,e.total-t);return RM(ut,{palette:e.palette,items:[{text:`view ${e.start+1}\u2013${t} of ${e.total}`,tone:"primary"},{text:r>0?`\u2191 ${r} older`:"start of history",tone:r>0?"info":"idle"},{text:n>0?`\u2193 ${n} newer`:"at latest",tone:n>0?"warning":"success"}],hints:[{key:"pgup/pgdn",label:"move history window"},{key:"g",label:"latest"}]},void 0,!1,void 0,this)}var dk=I(()=>{ke()});import{useEffect as _f,useRef as uk}from"react";import{jsxDEV as Ye}from"@opentui/react/jsx-dev-runtime";function Si(e){let{palette:t,logs:r,limit:n,width:o=80,followTick:i=0,focused:s=!1,wrapMode:a="clip",selected:c=-1,follow:l=!0,showTimestamps:d=!0,showMeta:u=!0,onPick:f,onLeaveLatest:p,viewStart:m=0,search:h="",regex:v=!1}=e,b=uk(null),g=uk(""),_=n===void 0?r:r.slice(-n),R=[...new Set(r.map((k)=>k.service))],S=Ub(o,R),y=Bb({width:o,serviceWidth:S,showTimestamps:d,showMeta:u}),x=_[_.length-1],w=x===void 0?"empty":String(x.seq);return _f(()=>{let k=b.current;if(!k)return;if(k.stickyScroll=l,l)k.stickyStart="bottom",k.scrollTo({x:k.scrollLeft,y:Math.max(0,k.scrollHeight)})},[l,i,w]),_f(()=>{let k=`${m}:${c}:${a}`;if(g.current===k)return;if(g.current=k,l&&(c<0||c>=_.length-1))return;let D=b.current;if(!D||c<0)return;let O=`log-row-${c}`,F=requestAnimationFrame(()=>{D.stickyScroll=!1,D.scrollChildIntoView(O)});return()=>cancelAnimationFrame(F)},[l,c,_.length,m,a]),_f(()=>{if(!l||!p)return;let k=!1,D=0,O=setInterval(()=>{let F=b.current;if(!F)return;let Z=F.viewport.height;if(Z<=0)return;let W=F.scrollHeight;if(F.scrollTop+Z>=W-TM){k=!0,D=W;return}if(W>D){F.stickyScroll=!0,F.stickyStart="bottom",F.scrollTo({x:F.scrollLeft,y:Math.max(0,W)}),D=W;return}if(k)p()},EM);return()=>{clearInterval(O)}},[l,i,p]),Ye("box",{flexGrow:1,height:"100%",overflow:"hidden",children:Ye("scrollbox",{ref:b,focused:s,stickyScroll:l,stickyStart:"bottom",scrollX:!1,style:{rootOptions:{flexGrow:1,height:"100%",overflow:"hidden",backgroundColor:t.panel},viewportOptions:{backgroundColor:t.panel},contentOptions:{backgroundColor:t.panel},scrollbarOptions:{trackOptions:{foregroundColor:t.primary,backgroundColor:t.element}}},children:Ye("box",{flexDirection:"column",overflow:"hidden",children:_.map((k,D)=>Ye(AM,{id:`log-row-${D}`,palette:t,event:k,serviceWidth:S,msgWidth:y,expanded:Hb(a,D===c),active:D===c,showTimestamps:d,showMeta:u,onPick:f?()=>f(D):void 0,search:h,regex:v},`${k.timestamp}-${k.service}-${D}`,!1,void 0,this))},void 0,!1,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this)}function AM(e){let{id:t,palette:r,event:n,serviceWidth:o,msgWidth:i,expanded:s,active:a,showTimestamps:c=!0,showMeta:l=!0,onPick:d,search:u="",regex:f=!1}=e,p=Nb(Xc(n.message,i),i,s);return Ye("box",{id:t,flexShrink:0,flexDirection:"column",overflow:"hidden",backgroundColor:a?r.highlight:void 0,onMouseDown:d,children:p.visible.map((m,h)=>Ye("box",{height:1,flexDirection:"row",overflow:"hidden",children:[c?Ye("box",{width:Vc,flexShrink:0,overflow:"hidden",children:Ye("text",{fg:r.muted,children:h===0?n.timestamp.slice(11,19):""},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,Ye("box",{width:o,flexShrink:0,overflow:"hidden",children:Ye("text",{fg:h===0?Ze(n.service,r):r.muted,wrapMode:"none",children:h===0?oe(n.service,o):""},void 0,!1,void 0,this)},void 0,!1,void 0,this),Ye("box",{width:As,flexShrink:0,overflow:"hidden",children:Ye("text",{children:" "},void 0,!1,void 0,this)},void 0,!1,void 0,this),Ye("box",{width:zc,flexShrink:0,overflow:"hidden",children:Ye("text",{fg:h===0?et(r,n.level):r.muted,children:h===0?Yc(String(n.level)):""},void 0,!1,void 0,this)},void 0,!1,void 0,this),l?Ye("box",{width:qc,flexShrink:0,overflow:"hidden",children:Ye("text",{fg:r.muted,children:h===0&&n.source?oe(n.source,qc):""},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,Ye("box",{width:As,flexShrink:0,overflow:"hidden",children:Ye("text",{fg:r.muted,children:h===0?" ":"\u2502"},void 0,!1,void 0,this)},void 0,!1,void 0,this),Ye("box",{flexGrow:1,overflow:"hidden",children:Ye(LM,{palette:r,level:n.level,text:m,mark:h===0?p.mark:"",search:u,regex:f},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},`${n.timestamp}-${h}`,!0,void 0,this))},void 0,!1,void 0,this)}function LM(e){let{palette:t,level:r,text:n,mark:o,search:i="",regex:s=!1}=e;return Ye("text",{wrapMode:"none",children:[jb(n,i,s).map((a,c)=>Ye("span",{fg:mb(t,r,a.kind),children:a.text},`${a.kind}-${c}`,!1,void 0,this)),o===""?null:Ye("span",{fg:t.muted,children:o},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}var EM=200,TM=2;var pk=I(()=>{Ot();gr();tt()});var Rf=I(()=>{ck();lk();dk();pk()});import{useRef as DM}from"react";import{jsxDEV as Ie}from"@opentui/react/jsx-dev-runtime";function Il(e){let{palette:t,names:r,snap:n,selected:o,checked:i,width:s,onOpen:a,onSelectIndex:c,onToggle:l}=e,d=ye(),u=DM(!1),f=new Set(i),p=_p(s),m=Pp(s),h=Cp(s),v=Eb(s);return Ie("box",{flexDirection:"column",flexGrow:1,overflow:"hidden",children:[Ie("box",{height:1,flexDirection:"row",overflow:"hidden",children:[Ie(Gt,{width:2,fg:t.muted,text:""},void 0,!1,void 0,this),Ie(Gt,{width:4,fg:t.accent,text:"sel"},void 0,!1,void 0,this),Ie(Gt,{width:2,fg:t.muted,text:""},void 0,!1,void 0,this),Ie(Gt,{width:v,fg:t.muted,text:"name"},void 0,!1,void 0,this),Ie(Gt,{width:$t,fg:t.muted,text:""},void 0,!1,void 0,this),Ie(Gt,{width:Tr,fg:t.muted,text:"state"},void 0,!1,void 0,this),h?Ie(Gt,{width:Ur,fg:t.muted,text:"health"},void 0,!1,void 0,this):null,m?Ie(Gt,{width:$c,fg:t.muted,text:"port"},void 0,!1,void 0,this):null,p?Ie(Gt,{width:sn,fg:t.muted,text:"pid"},void 0,!1,void 0,this):null]},void 0,!0,void 0,this),r.map((b,g)=>{let _=n?.services[b],R=tr(_),S=_?.health??"UNKNOWN",y=f.has(b),x=g===o,w=y?"[x]":"[ ]",k=x?"\u203A":" ";return Ie("box",{height:d.rowH,flexDirection:"row",overflow:"hidden",backgroundColor:x?t.highlight:y?t.element:void 0,onMouseDown:()=>{if(u.current)return;if(x){a(b);return}c(g)},children:[Ie("box",{width:2,flexShrink:0,children:Ie("text",{fg:t.primary,children:k},void 0,!1,void 0,this)},void 0,!1,void 0,this),Ie("box",{width:4,flexShrink:0,onMouseDown:()=>{u.current=!0,l?.(b),setTimeout(()=>{u.current=!1},0)},children:Ie("text",{fg:IM(t,y,x),children:w},void 0,!1,void 0,this)},void 0,!1,void 0,this),Ie("box",{width:2,flexShrink:0,children:Ie("text",{fg:et(t,R),children:hr(R)},void 0,!1,void 0,this)},void 0,!1,void 0,this),Ie(Gt,{width:v,fg:x?t.primary:Ze(b,t),text:oe(b,v)},void 0,!1,void 0,this),Ie(Gt,{width:$t,fg:t.muted,text:""},void 0,!1,void 0,this),Ie(Gt,{width:Tr,fg:et(t,R),text:R},void 0,!1,void 0,this),h?Ie(Gt,{width:Ur,fg:et(t,S),text:S},void 0,!1,void 0,this):null,m?Ie(Gt,{width:$c,fg:t.text,text:go(_)},void 0,!1,void 0,this):null,p?Ie(Gt,{width:sn,fg:t.text,text:_?.pid?String(_.pid):""},void 0,!1,void 0,this):null]},b,!0,void 0,this)})]},void 0,!0,void 0,this)}function Ml(e){let{palette:t,checked:r}=e,n=r.length;if(n>0)return Ie(ut,{palette:t,items:[{text:`${n} selected`,tone:"primary"}],hints:[{key:"space",label:"unmark"},{key:"n",label:"start these"},{key:"x",label:"stop these"}]},void 0,!1,void 0,this);return null}function IM(e,t,r){if(t)return e.success;if(r)return e.primary;return e.muted}function Gt(e){return Ie("box",{width:e.width,flexShrink:0,overflow:"hidden",children:Ie("text",{fg:e.fg,wrapMode:"none",children:e.text},void 0,!1,void 0,this)},void 0,!1,void 0,this)}var Ef=I(()=>{Mt();Ot();Lt();ke();tt()});import{jsxDEV as fe}from"@opentui/react/jsx-dev-runtime";function mk(e){let{palette:t,cfg:r,snap:n,logs:o,names:i,selected:s,selectedLog:a=-1,checked:c,width:l,followTick:d,logService:u,logSources:f,errorOnly:p,showSystemLogs:m,onOpen:h,onSelectIndex:v,onToggle:b,onFilterService:g,onToggleErrors:_,onShowErrors:R,wrapMode:S="clip",view:y,follow:x=!0,onLeaveLatest:w,onPickLog:k,viewStart:D=0,viewTotal:O,newer:F=0,onJumpLatest:Z,facets:W,leftover:C,search:j="",regex:B=!1}=e,z=ye();if(!r||i.length===0)return fe(ze,{palette:t,title:"No services configured",body:"Add services to .devctl/config.yaml or finish setup.",hint:"open the setup screen with /setup"},void 0,!1,void 0,this);let de=l<li,Ee=jc(n),Ne=cl(n,i),ne=i.filter((ge)=>n?.services[ge]?.state==="FAILED").length,H=Vb(o),ae=Ee?Lb(C,n?.session_id):void 0,Q=Hc(l,i,de),nt=Math.max(24,de?l-4:l-Q-4),ft=m?o:o.filter((ge)=>!ai(ge.source)),ie=yo(o,{service:u,errorOnly:p,systemLogs:m,search:j,regex:B}),Le=y??ie,Me=O??ie.length,Ke=Math.min(Me,D+Le.length),gt=Me===0?"empty":`${D+1}\u2013${Ke} of ${Me}`,Xe=u===""?"all":u,Ct=[...Ne.running>0?[{text:`${Ne.running}/${i.length}`,tone:"success"}]:[],...ne>0?[{text:`${ne} failed`,tone:"error"}]:[],...c.length>0?[{text:`${c.length} selected`,tone:"primary"}]:[],...H>0?[{text:`${H} errors`,tone:"error",onMouseDown:R}]:[]];return fe("box",{flexGrow:1,flexDirection:de?"column":"row",overflow:"hidden",children:[fe("box",{flexGrow:de?1:0,flexShrink:0,minWidth:de?void 0:Q,width:de?void 0:Q,border:!0,borderStyle:"rounded",borderColor:Ee?t.border:t.borderActive,backgroundColor:t.panel,title:"services",titleColor:t.primary,flexDirection:"column",overflow:"hidden",children:[Ct.length>0?fe(ut,{palette:t,items:Ct},void 0,!1,void 0,this):null,fe(Ml,{palette:t,checked:c},void 0,!1,void 0,this),fe("box",{flexGrow:1,paddingLeft:z.pad,paddingRight:z.pad,overflow:"hidden",children:fe(Il,{palette:t,names:i,snap:n,selected:s,checked:c,width:Nc(Q,z.pad),onOpen:h,onSelectIndex:v,onToggle:b},void 0,!1,void 0,this)},void 0,!1,void 0,this),fe(FM,{palette:t,names:i,snap:n,width:Q,onOpen:h},void 0,!1,void 0,this),ae?fe(HM,{palette:t,leftover:ae,width:Q},void 0,!1,void 0,this):null]},void 0,!0,void 0,this),fe("box",{position:"relative",flexGrow:2,minWidth:de?void 0:32,minHeight:de?8:void 0,border:!0,borderStyle:"rounded",borderColor:t.border,backgroundColor:t.panel,title:`logs \xB7 ${Xe} \xB7 ${gt}`,titleColor:t.primary,overflow:"hidden",flexDirection:"column",children:[fe(wi,{palette:t,logs:ft,names:f??i,service:u,errorOnly:p,width:nt,onService:g,onToggleErrors:_,facets:W},void 0,!1,void 0,this),Me>Le.length?fe(js,{palette:t,start:D,count:Le.length,total:Me},void 0,!1,void 0,this):null,ie.length===0?fe(ze,{palette:t,title:Ee?"Waiting for logs":o.length===0?"No events yet":"No events in this filter",body:Ee?"Start services to stream output here. Later starts keep earlier logs.":o.length===0?"New lines appear as services write output.":"Click All or another service chip, or turn off ERROR+.",hint:"\u2190 \u2192 cycle service e errors g latest"},void 0,!1,void 0,this):fe(Si,{palette:t,logs:Le,width:nt,followTick:d,focused:!1,wrapMode:S,selected:a,follow:x,onLeaveLatest:w,onPick:k,viewStart:D,search:j,regex:B},d,!1,void 0,this),!x?fe(xi,{palette:t,width:nt,newer:F,bottom:0,onJump:Z},void 0,!1,void 0,this):null]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)}function OM(e){if(!e)return!1;return e.state===dt||e.health===Oe||e.state===fr||e.restarts>0||e.last_error!==""}function fk(e){if(e.state===dt)return 0;if(e.health===Oe)return 1;if(e.state===fr)return 2;return 3}function $M(e){if(e.last_error!=="")return e.last_error;if(e.restarts>0)return`restarted ${e.restarts}x, no error recorded`;return e.health===Oe?"failing health check":e.state.toLowerCase()}function FM(e){let{palette:t,names:r,snap:n,width:o,onOpen:i}=e,s=r.map((d)=>({name:d,rt:n?.services[d]})).filter((d)=>OM(d.rt)).sort((d,u)=>fk(d.rt)-fk(u.rt));if(s.length===0)return null;let a=s.slice(0,MM),c=s.length-a.length,l=Math.max(8,o-Tf-4);return fe("box",{flexDirection:"column",flexShrink:0,paddingLeft:1,paddingRight:1,overflow:"hidden",children:[fe("box",{height:1,overflow:"hidden",children:fe("text",{fg:t.error,wrapMode:"none",children:`issues (${s.length})`},void 0,!1,void 0,this)},void 0,!1,void 0,this),a.map(({name:d,rt:u})=>{let f=tr(u);return fe("box",{height:1,flexDirection:"row",overflow:"hidden",onMouseDown:()=>i(d),children:[fe("box",{width:2,flexShrink:0,children:fe("text",{fg:et(t,f),children:hr(f)},void 0,!1,void 0,this)},void 0,!1,void 0,this),fe("box",{width:Tf,flexShrink:0,overflow:"hidden",children:fe("text",{fg:Ze(d,t),wrapMode:"none",children:oe(d,Tf)},void 0,!1,void 0,this)},void 0,!1,void 0,this),fe("box",{width:l,flexShrink:0,overflow:"hidden",children:fe("text",{fg:t.muted,wrapMode:"none",children:Re($M(u),l)},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},d,!0,void 0,this)}),c>0?fe("text",{fg:t.muted,wrapMode:"none",children:`+${c} more \u2014 open a service to see details`},void 0,!1,void 0,this):null]},void 0,!0,void 0,this)}function HM(e){let{palette:t,leftover:r,width:n}=e,o=r.processes.slice(0,NM),i=r.processes.length-o.length,s=Math.max(8,n-Af-4);return fe("box",{flexDirection:"column",flexShrink:0,paddingLeft:1,paddingRight:1,overflow:"hidden",children:[fe("box",{height:1,overflow:"hidden",children:fe("text",{fg:t.warning,wrapMode:"none",children:"last session"},void 0,!1,void 0,this)},void 0,!1,void 0,this),fe("text",{fg:t.muted,wrapMode:"none",children:Re(`${r.session_id} ${r.profile||"(none)"}`,Math.max(8,n-4))},void 0,!1,void 0,this),o.map((a)=>fe("box",{height:1,flexDirection:"row",overflow:"hidden",children:[fe("box",{width:Af,flexShrink:0,overflow:"hidden",children:fe("text",{fg:t.text,wrapMode:"none",children:oe(a.name,Af)},void 0,!1,void 0,this)},void 0,!1,void 0,this),fe("box",{width:s,flexShrink:0,overflow:"hidden",children:fe("text",{fg:t.muted,wrapMode:"none",children:Re(`pid ${a.pid} leftover`,s)},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},`${a.name}-${a.pid}`,!0,void 0,this)),i>0?fe("text",{fg:t.muted,wrapMode:"none",children:`+${i} more leftover pids`},void 0,!1,void 0,this):null]},void 0,!0,void 0,this)}var MM=4,Tf=14,NM=4,Af=14;var hk=I(()=>{At();Yt();Mt();vr();Ot();gr();Lt();dl();ke();tt();Rf();Ef()});import jM from"cli-spinners";import{useEffect as BM,useState as UM}from"react";import{jsxDEV as re}from"@opentui/react/jsx-dev-runtime";function gk(e){let{palette:t,report:r,loading:n,progress:o,error:i,selected:s,onPick:a,onReload:c}=e,l=fi(s,Df);if(n)return re(WM,{palette:t,progress:o},void 0,!1,void 0,this);if(i)return re(ew,{palette:t,title:"Doctor failed",body:i},void 0,!1,void 0,this);if(!r)return re(ze,{palette:t,title:"Doctor",body:"Diagnostics have not run yet.",hint:"opening this screen runs doctor"},void 0,!1,void 0,this);let d=r.checks.filter((v)=>v.severity==="ok").length,u=r.checks.filter((v)=>v.severity==="warn").length,f=r.checks.filter((v)=>v.severity==="error").length,p=r.checks.length,m=si(p>0?d/p:0),h=f>0?t.error:u>0?t.warning:t.success;return re(kt,{palette:t,title:`doctor ${r.issues} issue(s)`,children:[re("box",{height:4,flexDirection:"column",flexShrink:0,alignItems:"center",justifyContent:"center",overflow:"hidden",border:!0,borderStyle:"rounded",borderColor:t.primary,backgroundColor:t.element,onMouseDown:c,children:[re("text",{wrapMode:"none",children:[re("span",{fg:t.inverse,bg:t.primary,children:" r "},void 0,!1,void 0,this),re("span",{fg:t.primary,children:" RUN DOCTOR AGAIN"},void 0,!1,void 0,this)]},void 0,!0,void 0,this),re("text",{fg:t.muted,wrapMode:"none",children:"results stay cached until you refresh"},void 0,!1,void 0,this)]},void 0,!0,void 0,this),re("box",{height:1,flexShrink:0},void 0,!1,void 0,this),re("box",{height:1,flexShrink:0,overflow:"hidden",children:re("text",{wrapMode:"none",children:[re("span",{fg:h,children:`[${m}]`},void 0,!1,void 0,this),re("span",{fg:t.success,children:` ${d} ok`},void 0,!1,void 0,this),u>0?re("span",{fg:t.warning,children:` \xB7 ${u} warn`},void 0,!1,void 0,this):null,f>0?re("span",{fg:t.error,children:` \xB7 ${f} error`},void 0,!1,void 0,this):null,u===0&&f===0?re("span",{fg:t.muted,children:" \xB7 all clear"},void 0,!1,void 0,this):null]},void 0,!0,void 0,this)},void 0,!1,void 0,this),re("box",{height:1,flexShrink:0},void 0,!1,void 0,this),re("scrollbox",{ref:l,focused:!1,stickyScroll:!1,scrollX:!1,style:Ue(t),children:re("box",{flexDirection:"column",overflow:"hidden",gap:1,children:r.checks.map((v,b)=>re(GM,{palette:t,check:v,index:b,active:b===s,onPick:a},`${v.name}-${b}`,!1,void 0,this))},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function WM({palette:e,progress:t}){let[r,n]=UM(0);return BM(()=>{let o=setInterval(()=>n((i)=>(i+1)%Lf.frames.length),Lf.interval);return()=>clearInterval(o)},[]),re("box",{flexGrow:1,alignItems:"center",justifyContent:"center",overflow:"hidden",children:re("box",{width:"80%",maxWidth:52,height:10,flexShrink:1,border:!0,borderStyle:"rounded",borderColor:e.borderActive,backgroundColor:e.panel,flexDirection:"column",alignItems:"center",justifyContent:"center",overflow:"hidden",children:[re("text",{wrapMode:"none",children:[re("span",{fg:e.primary,children:`${Lf.frames[r]} `},void 0,!1,void 0,this),re("span",{fg:e.text,children:"devctl doctor"},void 0,!1,void 0,this)]},void 0,!0,void 0,this),re("box",{height:1,flexShrink:0},void 0,!1,void 0,this),re("text",{fg:e.text,wrapMode:"none",children:`Checking ${t.active}`},void 0,!1,void 0,this),re("box",{height:1,flexShrink:0},void 0,!1,void 0,this),t.checks.slice(-4).map((o,i)=>re("text",{wrapMode:"none",children:[re("span",{fg:et(e,o.severity==="error"?"ERROR":o.severity==="warn"?"WARN":"OK"),children:`${hr(o.severity==="error"?"ERROR":o.severity==="warn"?"WARN":"OK")} `},void 0,!1,void 0,this),re("span",{fg:e.muted,children:o.name},void 0,!1,void 0,this)]},`${o.name}-${i}`,!0,void 0,this)),t.checks.length===0?re("text",{fg:e.muted,children:"Waiting for the first result\u2026"},void 0,!1,void 0,this):null]},void 0,!0,void 0,this)},void 0,!1,void 0,this)}function GM(e){let{palette:t,check:r,index:n,active:o,onPick:i}=e,s=r.severity==="error"?"ERROR":r.severity==="warn"?"WARN":"OK";if(r.severity==="ok")return re("box",{id:`${Df}-${n}`,flexDirection:"row",flexShrink:0,paddingLeft:1,overflow:"hidden",backgroundColor:o?t.highlight:void 0,onMouseDown:()=>i(n),children:re("text",{wrapMode:"none",children:[re("span",{fg:t.primary,children:o?"\u203A ":" "},void 0,!1,void 0,this),re("span",{fg:et(t,s),children:hr(s)},void 0,!1,void 0,this),re("span",{fg:o?t.primary:t.text,children:` ${r.name}`},void 0,!1,void 0,this),re("span",{fg:t.muted,children:r.message?` \xB7 ${r.message}`:""},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this);let c=r.severity==="error"?t.error:t.warning;return re("box",{id:`${Df}-${n}`,border:!0,borderStyle:"rounded",borderColor:o?t.borderActive:c,title:r.name,titleColor:c,flexDirection:"column",flexShrink:0,paddingLeft:1,paddingRight:1,overflow:"hidden",backgroundColor:o?t.highlight:void 0,onMouseDown:()=>i(n),children:[re("text",{wrapMode:"word",children:[re("span",{fg:et(t,s),children:`${hr(s)} `},void 0,!1,void 0,this),re("span",{fg:t.text,children:r.message},void 0,!1,void 0,this)]},void 0,!0,void 0,this),r.hint?re("text",{fg:t.info,wrapMode:"word",children:`\u2192 ${r.hint}`},void 0,!1,void 0,this):null,o&&r.action?.kind==="free-port"?re("text",{fg:t.primary,wrapMode:"none",children:`enter stop ${r.action.holder.command} (pid ${r.action.holder.pid})`},void 0,!1,void 0,this):null]},void 0,!0,void 0,this)}var Df="doctor-row",Lf;var vk=I(()=>{Yt();Ot();ke();tt();Lf=jM.bouncingBall});import{jsxDEV as We,Fragment as KM}from"@opentui/react/jsx-dev-runtime";function bk(e){let{palette:t,logs:r,names:n,logSources:o,service:i,errorOnly:s,showSystemLogs:a,search:c,searchFocused:l,followTick:d,width:u,onSearch:f,onService:p,onToggleErrors:m,onSelect:h,wrapMode:v="focus",selected:b=-1,follow:g=!0,newer:_=0,viewStart:R=0,viewTotal:S,onLeaveLatest:y,onJumpLatest:x,view:w,fullscreen:k=!1,facets:D,split:O=!1,splitFocus:F=0,serviceB:Z="",viewB:W,selectedB:C=-1,followB:j=!0,newerB:B=0,viewStartB:z=0,viewTotalB:de,onServiceB:Ee,onSelectB:Ne,onLeaveLatestB:ne,onFocusPane:H}=e,ae=ye(),Q=Ep(u,ae.pad,k),nt=a?r:r.filter((ge)=>!ai(ge.source)),ft=yo(r,{service:i,services:e.services,errorOnly:s,search:c,regex:e.regex,source:e.source,systemLogs:a}),ie=w??ft,Le=S??ft.length,Me=Math.min(Le,R+ie.length),Ke=Le===0?"empty":`${R+1}\u2013${Me} of ${Le}`,gt=e.services??[],Xe=gt.length>0?gt.join(","):i===""?"all services":i,Ct=[...g?[]:[{text:_>0?`${O?"left ":""}pinned \xB7 +${_} new`:`${O?"left ":""}pinned`,tone:"warning"}],...O&&!j?[{text:B>0?`right pinned \xB7 +${B} new`:"right pinned",tone:"warning"}]:[],...e.source?[{text:`src ${e.source}`,tone:"info"}]:[],...e.regex?[{text:"regex",tone:"accent"}]:[],...c===""?[]:[{text:`search ${c}`,tone:"accent"}],...O?[{text:F===0?"left pane":"right pane",tone:"accent"}]:[]];if(O){let ge=Math.max(20,Math.floor(u/2)),vt=Math.max(20,u-ge),at=W??[],yt=de??at.length;return We("box",{flexGrow:1,flexDirection:"column",overflow:"hidden",children:[l?We("box",{height:1,paddingLeft:1,backgroundColor:t.highlight,overflow:"hidden",children:We("input",{focused:!0,value:c,placeholder:"search messages or service names",onInput:f,backgroundColor:t.highlight,focusedBackgroundColor:t.highlight,textColor:t.text,cursorColor:t.primary},void 0,!1,void 0,this)},void 0,!1,void 0,this):We(ut,{palette:t,items:Ct},void 0,!1,void 0,this),We("box",{flexGrow:1,flexDirection:"row",overflow:"hidden",children:[We(yk,{palette:t,names:o??n,logs:r,service:i,errorOnly:s,showSystemLogs:a,search:c,regex:e.regex,source:e.source,width:ge,shown:ie,shownTotal:Le,selected:b,follow:g,newer:_,viewStart:R,wrapMode:v,showTimestamps:e.showTimestamps!==!1,showMeta:e.showMeta!==!1,followTick:d,facets:D,focused:F===0,onService:p,onToggleErrors:m,onSelect:h,onLeaveLatest:y,onFocus:()=>H?.(0)},void 0,!1,void 0,this),We(yk,{palette:t,names:o??n,logs:r,service:Z,errorOnly:s,showSystemLogs:a,search:c,regex:e.regex,source:e.source,width:vt,shown:at,shownTotal:yt,selected:C,follow:j,newer:B,viewStart:z,wrapMode:v,showTimestamps:e.showTimestamps!==!1,showMeta:e.showMeta!==!1,followTick:d,facets:D,focused:F===1,onService:Ee??p,onToggleErrors:m,onSelect:Ne,onLeaveLatest:ne,onFocus:()=>H?.(1)},void 0,!1,void 0,this)]},void 0,!0,void 0,this),!g||!j?We(xi,{palette:t,width:u,newer:Math.max(_,B),onJump:x},void 0,!1,void 0,this):null]},void 0,!0,void 0,this)}return We("box",{flexGrow:1,flexDirection:"column",overflow:"hidden",children:[k?null:We(KM,{children:[Ct.length>0?We(ut,{palette:t,items:Ct},void 0,!1,void 0,this):null,We(wi,{palette:t,logs:nt,names:o??n,service:i,errorOnly:s,width:u,onService:p,onToggleErrors:m,facets:D},void 0,!1,void 0,this)]},void 0,!0,void 0,this),l?We("box",{height:1,paddingLeft:1,backgroundColor:t.highlight,overflow:"hidden",children:We("input",{focused:!0,value:c,placeholder:"search messages or service names",onInput:f,backgroundColor:t.highlight,focusedBackgroundColor:t.highlight,textColor:t.text,cursorColor:t.primary},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,w&&Le>ie.length?We(js,{palette:t,start:R,count:ie.length,total:Le},void 0,!1,void 0,this):null,We("box",{flexGrow:1,border:!k,borderStyle:"rounded",borderColor:t.border,title:k?void 0:`logs ${Xe} \xB7 ${Ke}`,titleColor:t.primary,padding:k?0:ae.pad,overflow:"hidden",children:ie.length===0?We(ze,{palette:t,title:r.length===0?"No log events":"No events in this filter",body:r.length===0?"Start services to stream logs.":"Pick All, another service, or clear search / ERROR+.",hint:`\u2190 \u2192 cycle filters e errors i internal ${It("l")} clear`},void 0,!1,void 0,this):We(Si,{palette:t,logs:ie,width:Q,followTick:d,focused:!1,wrapMode:v,selected:b,follow:g,showTimestamps:e.showTimestamps!==!1,showMeta:e.showMeta!==!1,onPick:h,onLeaveLatest:y,viewStart:R,search:c,regex:e.regex===!0},d,!1,void 0,this)},void 0,!1,void 0,this),!g?We(xi,{palette:t,width:u,newer:_,onJump:x},void 0,!1,void 0,this):null]},void 0,!0,void 0,this)}function yk(e){let{palette:t,width:r,service:n,shown:o,shownTotal:i,viewStart:s,focused:a}=e,c=e.showSystemLogs?e.logs:e.logs.filter((u)=>!ai(u.source)),l=n===""?"all services":n,d=i===0?"empty":`${s+1}\u2013${Math.min(i,s+o.length)} of ${i}`;return We("box",{width:r,flexGrow:0,flexShrink:0,flexDirection:"column",overflow:"hidden",onMouseDown:e.onFocus,children:[We(wi,{palette:t,logs:c,names:e.names,service:n,errorOnly:e.errorOnly,width:r,onService:(u)=>{e.onFocus(),e.onService(u)},onToggleErrors:()=>{e.onFocus(),e.onToggleErrors()},facets:e.facets},void 0,!1,void 0,this),We("box",{flexGrow:1,border:!0,borderStyle:"rounded",borderColor:a?t.borderActive:t.border,title:`logs ${l} \xB7 ${d}`,titleColor:a?t.primary:t.muted,overflow:"hidden",children:o.length===0?We(ze,{palette:t,title:"No events in this pane",body:"Click the pane, then \u2190 \u2192 to pick a service.",hint:"\\\\ split | focus"},void 0,!1,void 0,this):We(Si,{palette:t,logs:o,width:Ep(r,0,!1),followTick:e.followTick,focused:!1,wrapMode:e.wrapMode,selected:e.selected,follow:e.follow,showTimestamps:e.showTimestamps,showMeta:e.showMeta,onPick:(u)=>{e.onFocus(),e.onSelect?.(u)},onLeaveLatest:e.onLeaveLatest,viewStart:s,search:e.search,regex:e.regex===!0},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}var xk=I(()=>{Rf();Yt();Mt();gr();ke();Rr()});import{jsxDEV as Ge}from"@opentui/react/jsx-dev-runtime";function Sk(e){let{palette:t,cfg:r,snap:n,profile:o,selected:i,onPick:s}=e,a=ye(),c=fi(i,wk),l=Object.keys(r?.profiles??{}).sort();if(l.length===0)return Ge(ze,{palette:t,title:"No profiles",body:"Add profiles.<name>.services in config.yaml."},void 0,!1,void 0,this);return Ge(kt,{palette:t,title:"profiles",children:[Ge("box",{height:1,flexShrink:0,overflow:"hidden",children:Ge("text",{fg:t.muted,wrapMode:"none",children:[`${l.length} profile${l.length===1?"":"s"} \xB7 current: `,Ge("span",{fg:t.success,children:o||"(none)"},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this),Ge("box",{height:1,flexShrink:0},void 0,!1,void 0,this),Ge("scrollbox",{ref:c,focused:!1,stickyScroll:!1,scrollX:!1,style:Ue(t),children:Ge("box",{flexDirection:"column",overflow:"hidden",gap:a.gap||1,children:l.map((d,u)=>{let f=d===o,p=u===i,m=r?.profiles[d]?.services??[],h=Object.keys(r?.profiles[d]?.environment??{}).length,v=p?t.borderActive:f?t.success:t.border,b=p?t.primary:f?t.success:t.muted;return Ge("box",{id:`${wk}-${u}`,border:!0,borderStyle:"rounded",borderColor:v,title:d,titleColor:b,flexDirection:"column",flexShrink:0,paddingLeft:1,paddingRight:1,overflow:"hidden",backgroundColor:p?t.highlight:void 0,onMouseDown:()=>s(u),children:[Ge("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,children:Ge("text",{wrapMode:"none",children:[f?Ge("span",{fg:t.success,children:"\u25CF current profile"},void 0,!1,void 0,this):Ge("span",{fg:t.muted,children:"\u25CB not active"},void 0,!1,void 0,this),Ge("span",{fg:t.muted,children:` \xB7 ${m.length} service${m.length===1?"":"s"}`},void 0,!1,void 0,this),h>0?Ge("span",{fg:t.muted,children:` \xB7 ${h} env var${h===1?"":"s"}`},void 0,!1,void 0,this):null]},void 0,!0,void 0,this)},void 0,!1,void 0,this),Ge("box",{flexDirection:"row",overflow:"hidden",flexShrink:0,children:m.length===0?Ge("text",{fg:t.muted,wrapMode:"none",children:"(no services)"},void 0,!1,void 0,this):Ge("text",{wrapMode:"word",children:m.map((g,_)=>{let R=tr(n?.services[g]);return Ge("span",{children:[Ge("span",{fg:et(t,R),children:hr(R)},void 0,!1,void 0,this),Ge("span",{fg:Ze(g,t),children:` ${g}`},void 0,!1,void 0,this),_<m.length-1?Ge("span",{fg:t.muted,children:" "},void 0,!1,void 0,this):null]},g,!0,void 0,this)})},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},d,!0,void 0,this)})},void 0,!1,void 0,this)},void 0,!1,void 0,this),Ge(Ut,{palette:t,backgroundColor:t.element,edge:"top",children:Ge(it,{palette:t,hints:[{key:"space",label:"set current"},{key:"enter",label:"set and start"}]},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}var wk="profile-row";var kk=I(()=>{Yt();Mt();Lt();ke();tt()});import{jsxDEV as V,Fragment as Rk}from"@opentui/react/jsx-dev-runtime";function qM(e){let t=e.toLowerCase();if(t==="iap")return"info";if(t==="service"||t==="service_account")return"primary";return"idle"}function VM(e,t,r){if(r||t>=500)return e.error;if(t===0||t>=400)return e.warning;return e.success}function zM(e,t){if(t.startsWith("sa:"))return{label:"SA",color:e.primary};if(t!=="")return{label:"USR",color:e.info};return{label:"\u2014",color:e.muted}}function QM(e){let{palette:t,req:r,cols:n}=e,o=VM(t,r.status,r.error),i=r.status>0?String(r.status):"ERR",s=zM(t,r.identity),a=r.route||Ek,c=r.error?`${r.path} \u2014 ${Re(r.error,XM)}`:r.path;return V("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,children:[V("box",{width:If,flexShrink:0,overflow:"hidden",children:V("text",{fg:t.muted,children:r.timestamp.slice(11,19)},void 0,!1,void 0,this)},void 0,!1,void 0,this),n.showMethod?V("box",{width:Ol,flexShrink:0,overflow:"hidden",children:V("text",{fg:t.text,wrapMode:"none",children:oe(r.method,Ol)},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,V("box",{width:$l,flexShrink:0,overflow:"hidden",children:V("text",{fg:o,wrapMode:"none",children:oe(i,$l)},void 0,!1,void 0,this)},void 0,!1,void 0,this),n.showDur?V("box",{width:Fl,flexShrink:0,overflow:"hidden",children:V("text",{fg:t.muted,wrapMode:"none",children:oe(`${r.durationMs}ms`,Fl)},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,V("box",{width:Nl,flexShrink:0,overflow:"hidden",children:V("text",{fg:s.color,wrapMode:"none",children:oe(s.label,Nl)},void 0,!1,void 0,this)},void 0,!1,void 0,this),V("box",{width:n.routeCol,flexShrink:0,overflow:"hidden",children:V("text",{fg:r.route?t.info:t.muted,wrapMode:"none",children:oe(a,n.routeCol)},void 0,!1,void 0,this)},void 0,!1,void 0,this),V("box",{flexGrow:1,overflow:"hidden",children:V("text",{fg:r.error?t.error:t.text,wrapMode:"none",children:c},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function ZM(e){let{palette:t,cols:r}=e;return V("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,backgroundColor:t.element,children:[V("box",{width:If,flexShrink:0,overflow:"hidden",children:V("text",{fg:t.muted,children:oe("TIME",If)},void 0,!1,void 0,this)},void 0,!1,void 0,this),r.showMethod?V("box",{width:Ol,flexShrink:0,overflow:"hidden",children:V("text",{fg:t.muted,children:oe("METHOD",Ol)},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,V("box",{width:$l,flexShrink:0,overflow:"hidden",children:V("text",{fg:t.muted,children:oe("ST",$l)},void 0,!1,void 0,this)},void 0,!1,void 0,this),r.showDur?V("box",{width:Fl,flexShrink:0,overflow:"hidden",children:V("text",{fg:t.muted,children:oe("DUR",Fl)},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,V("box",{width:Nl,flexShrink:0,overflow:"hidden",children:V("text",{fg:t.muted,children:oe("WHO",Nl)},void 0,!1,void 0,this)},void 0,!1,void 0,this),V("box",{width:r.routeCol,flexShrink:0,overflow:"hidden",children:V("text",{fg:t.muted,children:oe("ROUTE",r.routeCol)},void 0,!1,void 0,this)},void 0,!1,void 0,this),V("box",{flexGrow:1,overflow:"hidden",children:V("text",{fg:t.muted,children:"PATH"},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function eO(e){let{palette:t,name:r,auth:n,identity:o,upstream:i,match:s,onMouseDown:a}=e;return V("box",{flexDirection:"column",flexShrink:0,overflow:"hidden",onMouseDown:a,children:[V("text",{fg:t.text,wrapMode:"none",children:r},void 0,!1,void 0,this),V("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,children:[V(Be,{palette:t,label:n||"no auth",tone:qM(n)},void 0,!1,void 0,this),o?V(Be,{palette:t,label:o,tone:"idle"},void 0,!1,void 0,this):null]},void 0,!0,void 0,this),s?V("text",{fg:t.muted,wrapMode:"word",children:`match: ${s}`},void 0,!1,void 0,this):null,V("text",{fg:t.muted,wrapMode:"word",children:`\u2192 ${i}`},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function Tk(e){let{palette:t,cfg:r,snap:n,width:o,onSelectRoute:i}=e,s=n?.proxy.routes??[],a=new Map((r?.proxy.routes??[]).map((S)=>[S.name,S])),c=new Map((r?.proxy.routes??[]).map((S)=>[S.name,S.match])),l=n?.proxy.recentRequests??[],d=n?.proxy.requestTotal??0,u=n?.proxy.requestErrors??0,f=o<li,p=Math.max(Ck,Math.min(tO,Math.floor(o*0.36))),m=f?o-Pk:o-p-Pk,h=Math.max(20,m-2),v=h>=_k,b=h>=rO,g=h>=_k?nO:oO,_=l.reduce((S,y)=>Math.min(g,Math.max(S,(y.route||Ek).length+JM)),YM),R={showMethod:v,showDur:b,routeCol:_};return V(kt,{palette:t,title:"proxy",children:[V(ut,{palette:t,items:[{text:n?.proxy.running?"RUNNING":"STOPPED",tone:n?.proxy.running?"success":"idle"},...n?.proxy.address?[{text:n.proxy.address,tone:"info"}]:[],{text:`${s.length} routes`,tone:s.length>0?"primary":"idle"},...d>0?[{text:`${d} requests`,tone:"idle"},...u>0?[{text:`${u} errors`,tone:"warning"}]:[]]:[]]},void 0,!1,void 0,this),V("box",{flexGrow:1,flexDirection:f?"column":"row",overflow:"hidden",children:[V("box",{flexGrow:f?1:0,flexBasis:f?0:void 0,flexShrink:0,minWidth:f?void 0:Ck,width:f?void 0:p,minHeight:f?8:void 0,border:!0,borderStyle:"rounded",borderColor:t.border,title:"routes",titleColor:t.primary,paddingLeft:1,paddingRight:1,flexDirection:"column",overflow:"hidden",children:s.length===0?V(ze,{palette:t,title:"No proxy routes",body:"Add routes under proxy.routes or .devctl/proxy/routes.yaml."},void 0,!1,void 0,this):V(Rk,{children:[i?V("text",{fg:t.muted,wrapMode:"word",children:"click a route for full details"},void 0,!1,void 0,this):null,V("scrollbox",{focused:!1,stickyScroll:!1,scrollX:!1,style:Ue(t),children:V("box",{flexDirection:"column",overflow:"hidden",gap:1,children:s.map((S)=>{let y=c.get(S.name),x=y&&(y.host||y.path)?`${y.host||"*"}${y.path?y.path:""}`:void 0,w=a.get(S.name);return V(eO,{palette:t,name:S.name,auth:S.auth,identity:S.identity,upstream:S.upstream,match:x,onMouseDown:i?()=>i({name:S.name,authType:w?.auth.type||S.auth,identityType:w?.auth.identity.type??"",serviceAccount:w?.auth.identity.service_account||w?.auth.service_account||"",audience:w?.auth.audience??"",clientId:w?.auth.client_id??"",clientSecret:Ba(w?.auth.client_secret??"")??((w?.auth.client_secret??"").trim()?"inline":""),matchHost:w?.match.host??"",matchPath:w?.match.path??"",upstream:S.upstream}):void 0},S.name,!1,void 0,this)})},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this),V("box",{flexGrow:1,flexBasis:0,minWidth:f?void 0:30,minHeight:f?10:void 0,border:!0,borderStyle:"rounded",borderColor:t.borderActive,title:"requests",titleColor:t.primary,flexDirection:"column",overflow:"hidden",children:l.length===0?V("box",{paddingLeft:1,paddingRight:1,children:V("text",{fg:t.muted,wrapMode:"word",children:n?.proxy.running?"No requests seen yet. Send one through the proxy to see it show up here \u2014 no need to restart or press r.":"Start the proxy, then send it a request to see live traffic here."},void 0,!1,void 0,this)},void 0,!1,void 0,this):V(Rk,{children:[V(ZM,{palette:t,cols:R},void 0,!1,void 0,this),V("scrollbox",{focused:!1,stickyScroll:!1,scrollX:!1,style:Ue(t),children:V("box",{flexDirection:"column",overflow:"hidden",children:l.map((S)=>V(QM,{palette:t,req:S,cols:R},S.requestId,!1,void 0,this))},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this),V(Ut,{palette:t,backgroundColor:t.element,edge:"top",children:V(it,{palette:t,hints:[{key:"n",label:"start proxy"},{key:"x",label:"stop proxy"}]},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}var If=9,Ol=8,$l=4,Fl=8,Nl=4,YM=8,JM=2,XM=64,Ek="(none)",Ck=26,tO=56,Pk=2,_k=46,rO=54,nO=20,oO=12;var Ak=I(()=>{Yt();vr();Ot();ke()});import{jsxDEV as pe}from"@opentui/react/jsx-dev-runtime";function Ik(e){let{palette:t,name:r}=e;return pe("box",{flexGrow:1,border:!0,borderStyle:"rounded",borderColor:t.borderActive,backgroundColor:t.panel,title:r,titleColor:Ze(r,t),flexDirection:"column",overflow:"hidden",children:pe(Mf,{...e,compact:!1},void 0,!1,void 0,this)},void 0,!1,void 0,this)}function Mf(e){let{palette:t,cfg:r,snap:n,name:o,reveal:i,width:s,compact:a=!0,envScrollRef:c}=e,l=ye(),d=r?.services[o];if(!d)return pe(ze,{palette:t,title:"Unknown service",body:`No service named ${o}.`,hint:"j/k move"},void 0,!1,void 0,this);let u=n?.services[o],f=tr(u),p=u?.health??"UNKNOWN",m=go(u)||Es(d,u),h=Ab(d,i,r?.secrets.extra_markers??[],r?.secrets.extra_patterns??[],e.resolvedEnv),v=e.envStatus==="error"?"error":e.envStatus==="resolved"?"success":e.envStatus==="loading"?"info":"muted",b=e.envStatus==="resolved"?"resolved":e.envStatus==="loading"?"resolving":e.envStatus==="error"?"config fallback":"config",g=s>=sO,_=[{label:"health",value:Wc(d)},{label:"workdir",value:d.working_dir||"."},{label:"restart",value:Kc(d)},{label:"caps",value:d.capabilities.join(", ")||"\u2014"},{label:"runtime",value:d.container?`${d.container.runtime||"docker"} \xB7 ${d.container.image}`:"host"},...d.watch.enabled?[{label:"watch",value:d.watch.paths.join(" ")||"on"}]:[]],R=[{label:"identity",value:Gc(d,u)},{label:"depends",value:d.dependencies.map(Ra).join(", ")||"\u2014"},{label:"ports",value:Es(d,u)},{label:"tries",value:String(u?.restarts??0),tone:u?.restarts?"warning":"muted"}],S=Math.max(Co+8,s-2),y=g?Math.floor(S/2):S;return pe("box",{padding:l.pad,flexGrow:1,flexDirection:"column",overflow:"hidden",children:[pe(ut,{palette:t,items:[{text:f,tone:Dk(f)},{text:p,tone:Dk(p)},{text:u?.pid?`pid ${u.pid}`:"pid \u2014",tone:u?.pid?"info":"idle"},{text:m==="\u2014"?"port \u2014":`port ${m}`,tone:m==="\u2014"?"idle":"info"}]},void 0,!1,void 0,this),d.description?pe("box",{height:1,overflow:"hidden",children:pe("text",{fg:t.muted,wrapMode:"none",children:Re(d.description,Math.max(8,s-4))},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,u?.last_error?pe("box",{height:1,overflow:"hidden",children:pe("text",{fg:t.error,wrapMode:"none",children:Re(u.last_error,iO)},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,pe(Of,{palette:t,item:{label:"command",value:Uc(d)},valueWidth:Math.max(8,S-Co)},void 0,!1,void 0,this),pe(aO,{palette:t,left:_,right:R,wide:g,colWidth:y},void 0,!1,void 0,this),pe(cO,{palette:t,entries:h,reveal:i,width:s,focused:!a,scrollRef:c,source:b,sourceTone:v,error:e.envError,onSelect:e.onSelectEnv},void 0,!1,void 0,this),a?null:pe(it,{palette:t,hints:[{key:"n",label:"start"},{key:"x",label:"stop"},{key:"R",label:"restart"},{key:"o",label:"config"},{key:"l",label:"logs"},{key:"/reveal",label:"secrets"},{key:"esc",label:"back"}]},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function aO(e){let{palette:t,left:r,right:n,wide:o,colWidth:i}=e,s=Math.max(4,i-Co);if(!o)return pe("box",{width:i,flexShrink:0,flexDirection:"column",overflow:"hidden",children:[...r,...n].map((a)=>pe(Of,{palette:t,item:a,valueWidth:s},a.label,!1,void 0,this))},void 0,!1,void 0,this);return pe("box",{height:r.length,flexShrink:0,flexDirection:"row",overflow:"hidden",children:[pe(Lk,{palette:t,items:r,width:i},void 0,!1,void 0,this),pe(Lk,{palette:t,items:n,width:i},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function Lk(e){let t=Math.max(4,e.width-Co);return pe("box",{width:e.width,flexShrink:0,flexDirection:"column",overflow:"hidden",children:e.items.map((r)=>pe(Of,{palette:e.palette,item:r,valueWidth:t},r.label,!1,void 0,this))},void 0,!1,void 0,this)}function Of(e){let{palette:t,item:r,valueWidth:n}=e,o=r.tone==="warning"?t.warning:r.tone==="muted"?t.muted:t.text;return pe("box",{height:1,width:Co+n,flexDirection:"row",overflow:"hidden",children:[pe("box",{width:Co,flexShrink:0,overflow:"hidden",children:pe("text",{fg:t.muted,wrapMode:"none",children:oe(r.label,Co)},void 0,!1,void 0,this)},void 0,!1,void 0,this),pe("box",{width:n,flexShrink:0,overflow:"hidden",children:pe("text",{fg:o,wrapMode:"none",children:oe(r.value,n)},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function cO(e){let{palette:t,entries:r,reveal:n,width:o,focused:i,scrollRef:s,source:a,sourceTone:c,error:l,onSelect:d}=e;return pe("box",{flexGrow:1,flexDirection:"column",overflow:"hidden",children:[pe(ut,{palette:t,items:[{text:`env ${r.length}`,tone:r.length>0?"info":"idle"},{text:a,tone:c},{text:n?"secrets shown":"redacted",tone:n?"warning":"muted"}],hints:i?[{key:"j/k",label:"scroll"}]:[{key:"/reveal",label:"secrets"}]},void 0,!1,void 0,this),l?pe("box",{height:1,overflow:"hidden",children:pe("text",{fg:t.warning,wrapMode:"none",children:Re(l,Math.max(8,o-4))},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,r.length===0?pe("text",{fg:t.muted,children:"no service env overrides"},void 0,!1,void 0,this):pe("box",{flexGrow:1,height:"100%",overflow:"hidden",children:[d?pe("text",{fg:t.muted,wrapMode:"word",children:"click a variable for its full value"},void 0,!1,void 0,this):null,pe("scrollbox",{ref:s,focused:i,stickyScroll:!1,scrollX:!1,style:{rootOptions:{flexGrow:1,height:"100%",overflow:"hidden",backgroundColor:t.panel},viewportOptions:{backgroundColor:t.panel},contentOptions:{backgroundColor:t.panel},scrollbarOptions:{trackOptions:{foregroundColor:t.primary,backgroundColor:t.element}}},children:pe("box",{flexDirection:"column",overflow:"hidden",children:r.map((u)=>{let f=u.required?t.warning:u.fromConfig?t.accent:t.muted,p=u.value===""&&u.required?"(required)":u.value;return pe("box",{flexDirection:"row",overflow:"hidden",onMouseDown:d?()=>d(u):void 0,children:pe("text",{wrapMode:"word",children:[pe("span",{fg:f,children:u.required?`${u.key}*`:u.key},void 0,!1,void 0,this),pe("span",{fg:u.value===""?t.muted:t.text,children:` ${p}`},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},u.key,!1,void 0,this)})},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)}function Dk(e){switch(e.toUpperCase()){case"HEALTHY":case"RUNNING":return"success";case"UNHEALTHY":case"WARNING":case"WARN":case"STARTING":case"RESTARTING":return"warning";case"FAILED":case"ERROR":return"error";default:return"muted"}}var iO=72,sO=56,Co=9;var $f=I(()=>{ht();Yt();Mt();Ot();Lt();ke();tt()});import{jsxDEV as Hn}from"@opentui/react/jsx-dev-runtime";function Mk(e){let{palette:t,cfg:r,names:n,snap:o,selected:i,checked:s,width:a,reveal:c,onOpen:l,onSelectIndex:d,onToggle:u,resolvedEnv:f,envStatus:p,envError:m,onSelectEnv:h}=e;if(n.length===0)return Hn(ze,{palette:t,title:"No services",body:"This configuration does not define any services."},void 0,!1,void 0,this);let v=a<li,b=n[i]??"",g=Hc(a,n,v),_=4,R=v?Math.max(Ff,a-_):Math.max(Ff,a-g-_);return Hn("box",{flexGrow:1,flexDirection:v?"column":"row",overflow:"hidden",children:[Hn("box",{flexGrow:v?1:0,flexShrink:0,minWidth:v?void 0:Oc,width:v?void 0:g,border:!0,borderStyle:"rounded",borderColor:t.borderActive,backgroundColor:t.panel,title:"services",titleColor:t.primary,flexDirection:"column",overflow:"hidden",children:[Hn(Ml,{palette:t,checked:s},void 0,!1,void 0,this),Hn("box",{flexGrow:1,overflow:"hidden",children:Hn(Il,{palette:t,names:n,snap:o,selected:i,checked:s,width:Nc(g),onOpen:l,onSelectIndex:d,onToggle:u},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this),Hn("box",{flexGrow:2,minWidth:v?void 0:Ff,minHeight:v?lO:void 0,border:!0,borderStyle:"rounded",borderColor:t.border,backgroundColor:t.panel,title:b||"detail",titleColor:b?Ze(b,t):t.primary,overflow:"hidden",flexDirection:"column",children:Hn(Mf,{palette:t,cfg:r,snap:o,name:b,reveal:c,width:R,resolvedEnv:f,envStatus:p,envError:m,onSelectEnv:h},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}var Ff=36,lO=12;var Ok=I(()=>{Yt();vr();Lt();tt();$f();Ef()});import{jsxDEV as we}from"@opentui/react/jsx-dev-runtime";function uO(e){switch(e){case"cycle":return"\u2194";case"toggle":return"\u23FB";case"page":return"\u25B8";case"action":return"!";default:return"\xB7"}}function Hk(e){let{palette:t,items:r,selected:n,locked:o,width:i,onPick:s,onActivate:a}=e,c=ye(),l=vp(r,n),d=Ln(r,l);return we("box",{flexGrow:1,border:!0,borderStyle:"rounded",borderColor:t.borderActive,backgroundColor:t.panel,title:"settings",titleColor:t.primary,flexDirection:"column",overflow:"hidden",children:[we("box",{height:2,paddingLeft:c.pad,flexShrink:0,flexDirection:"column",overflow:"hidden",children:[we("text",{fg:t.primary,wrapMode:"none",children:"Preferences"},void 0,!1,void 0,this),we("text",{fg:t.muted,wrapMode:"none",children:o?"DEVCTL_TUI_CONFIG is set \u2014 changes stay in this session.":"Changes save to your user tui.json unless noted."},void 0,!1,void 0,this)]},void 0,!0,void 0,this),we("box",{flexGrow:1,paddingLeft:c.pad,paddingRight:c.pad,overflow:"hidden",flexShrink:1,children:we(pO,{palette:t,items:r,active:l,scale:c,width:i,onPick:s,onActivate:a},void 0,!1,void 0,this)},void 0,!1,void 0,this),we(mO,{palette:t,item:d,scale:c},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function pO(e){let{palette:t,items:r,active:n,scale:o,width:i,onPick:s,onActivate:a}=e,c=fi(n,Nk),l=Math.max(8,i-$k-Fk-10),d=0,u=Sb(r);return we("scrollbox",{ref:c,focused:!1,stickyScroll:!1,scrollX:!1,style:Ue(t),children:we("box",{flexDirection:"column",overflow:"hidden",children:u.map((f,p)=>{let m=d;d+=f.items.length;let h=n>=m&&n<m+f.items.length;return we("box",{flexDirection:"column",flexShrink:0,marginBottom:p<u.length-1?Math.max(1,o.gap):0,overflow:"hidden",children:[we("box",{height:1,overflow:"hidden",children:we("text",{fg:h?t.primary:t.muted,wrapMode:"none",children:f.group},void 0,!1,void 0,this)},void 0,!1,void 0,this),f.group==="MCP"?we("box",{height:1,overflow:"hidden",children:we("text",{fg:t.muted,wrapMode:"none",children:"dedicated page \u2014 also /mcp or /agent"},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,f.items.map((v,b)=>{let g=m+b;return we(fO,{index:g,palette:t,item:v,active:g===n,rowH:o.rowH,valueWidth:l,onPick:()=>s(g),onActivate:()=>a(v)},v.id,!1,void 0,this)})]},f.group,!0,void 0,this)})},void 0,!1,void 0,this)},void 0,!1,void 0,this)}function fO(e){let{palette:t,index:r,item:n,active:o,rowH:i,valueWidth:s,onPick:a,onActivate:c}=e;return we("box",{id:`${Nk}-${r}`,height:i,flexDirection:"row",overflow:"hidden",flexShrink:0,backgroundColor:o?t.highlight:void 0,onMouseDown:()=>{if(o){c();return}a()},children:[we("box",{width:2,flexShrink:0,overflow:"hidden",children:we("text",{fg:t.primary,children:o?"\u203A":" "},void 0,!1,void 0,this)},void 0,!1,void 0,this),we("box",{width:Fk,flexShrink:0,overflow:"hidden",children:we("text",{fg:o?t.primary:t.muted,children:uO(n.kind)},void 0,!1,void 0,this)},void 0,!1,void 0,this),we("box",{width:$k,flexShrink:0,overflow:"hidden",children:we("text",{fg:o?t.primary:t.text,wrapMode:"none",children:n.name},void 0,!1,void 0,this)},void 0,!1,void 0,this),we("box",{flexGrow:1,overflow:"hidden",children:we("text",{fg:hO(t,n,o),wrapMode:"none",children:Re(n.value,s)},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function mO(e){let{palette:t,item:r,scale:n}=e,o=r?.id==="font",i=r?.kind==="page"?`${r.name} \u2192 dedicated page`:r?.name??"";return we("box",{height:dO,flexShrink:0,border:!0,borderStyle:"rounded",borderColor:t.border,title:"selected",titleColor:t.muted,backgroundColor:t.element,paddingLeft:n.pad,paddingRight:n.pad,flexDirection:"column",overflow:"hidden",children:[we("box",{height:1,overflow:"hidden",children:we("text",{fg:t.primary,wrapMode:"none",children:o?`${xb(n.steps)} ${n.label}`:i},void 0,!1,void 0,this)},void 0,!1,void 0,this),we("box",{height:1,overflow:"hidden",children:we("text",{fg:t.text,wrapMode:"word",children:r?.detail??""},void 0,!1,void 0,this)},void 0,!1,void 0,this),we("box",{height:1,overflow:"hidden",children:we("text",{fg:t.muted,wrapMode:"none",children:r?r.kind==="info"?"read-only":r.hint:""},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function hO(e,t,r){if(t.id==="mouse")return t.value==="on"?e.success:e.text;if(t.kind==="page")return e.info;if(t.kind==="info")return e.text;return r?e.primary:e.text}var $k=14,Fk=2,dO=5,Nk="settings-row";var jk=I(()=>{Mt();Ot();ke();Er()});import{jsxDEV as Ae}from"@opentui/react/jsx-dev-runtime";function Bk(e){let{palette:t,cfg:r,google:n,bootError:o,bootErrorMissing:i=!1,step:s=0}=e,a=ye();if(o&&!r)return i?Ae(ze,{palette:t,title:"No configuration found",body:"Would you like to run setup?",hint:"[Enter] 9-step setup [Esc] Exit"},void 0,!1,void 0,this):Ae(ze,{palette:t,title:"Configuration error",body:o,hint:"Fix .devctl/config.yaml, then restart devctl [Esc] Exit"},void 0,!1,void 0,this);let c=e.issues,l=gO(r,n,c),d=l.filter((g)=>g.ok).length,u=l.length,f=u>0?Math.round(d/u*100):0,p=20,m=Math.round(f/100*p),h="\u2588".repeat(m)+"\u2591".repeat(Math.max(0,p-m)),v=f===100?t.success:f===0?t.muted:t.primary,b=l[s];return Ae(kt,{palette:t,title:"setup",scroll:!0,children:[Ae("box",{flexDirection:"row",overflow:"hidden",flexShrink:0,children:Ae("text",{fg:t.primary,children:"Developer onboarding"},void 0,!1,void 0,this)},void 0,!1,void 0,this),Ae("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,children:Ae("text",{wrapMode:"none",children:[Ae("span",{fg:v,children:`[${h}]`},void 0,!1,void 0,this),Ae("span",{fg:t.text,children:` ${d}/${u} ready`},void 0,!1,void 0,this),Ae("span",{fg:t.muted,children:` (${f}%)`},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this),Ae("box",{height:1,flexShrink:0},void 0,!1,void 0,this),l.map((g,_)=>{let R=_===s;return Ae("box",{height:a.rowH,flexDirection:"row",overflow:"hidden",backgroundColor:R?t.highlight:void 0,paddingLeft:1,children:[Ae("box",{width:2,flexShrink:0,children:Ae("text",{fg:et(t,g.ok?"OK":"WARN"),children:hr(g.ok?"OK":"WARN")},void 0,!1,void 0,this)},void 0,!1,void 0,this),Ae("box",{width:3,flexShrink:0,children:Ae("text",{fg:R?t.primary:t.muted,children:String(_+1).padStart(2," ")},void 0,!1,void 0,this)},void 0,!1,void 0,this),Ae("box",{width:20,flexShrink:0,overflow:"hidden",children:Ae("text",{fg:R?t.primary:t.text,children:g.name},void 0,!1,void 0,this)},void 0,!1,void 0,this),Ae("box",{flexGrow:1,overflow:"hidden",children:Ae("text",{fg:R?t.text:t.muted,wrapMode:"none",children:g.detail},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},g.name,!0,void 0,this)}),Ae("box",{height:1,flexShrink:0},void 0,!1,void 0,this),b?Ae("box",{border:!0,borderStyle:"rounded",borderColor:b.ok?t.success:t.warning,title:b.name,titleColor:b.ok?t.success:t.warning,flexDirection:"column",paddingLeft:1,paddingRight:1,flexShrink:0,overflow:"hidden",children:Ae("text",{fg:b.ok?t.text:t.warning,wrapMode:"word",children:b.detail},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,Ae("box",{height:1,flexShrink:0},void 0,!1,void 0,this),Ae("box",{flexDirection:"row",overflow:"hidden",flexShrink:0,children:Ae(Be,{palette:t,label:"never hard-coded",tone:"idle"},void 0,!1,void 0,this)},void 0,!1,void 0,this),Ae("text",{fg:t.muted,wrapMode:"word",children:"Service accounts and IAP audiences stay in configuration. Run `devctl setup` for the interactive CLI wizard."},void 0,!1,void 0,this),Ae(it,{palette:t,hints:[{key:"j/k",label:"steps"},{key:"enter",label:r?"start":"begin setup"},{key:"esc",label:"exit"},{key:"o",label:"profiles"}]},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function gO(e,t,r=[]){let n=Object.values(e?.services??{}).flatMap((s)=>s.ports.filter((a)=>!a.auto)),o=(e?.proxy.routes??[]).some((s)=>s.auth.type.toLowerCase()==="iap"),i=Tl(e,void 0,t).project;return[{name:ln[0],ok:Boolean(e?.repoRoot),detail:e?.repoRoot||"unknown"},{name:ln[1],ok:Boolean(e?.project.name),detail:e?.project.name||"set project.name"},{name:ln[2],ok:Boolean(i),detail:i||"optional for local-only"},{name:ln[3],ok:t?.adcAvailable===!0,detail:t?.adcAvailable?"ADC available":"needed only for cloud identity / IAP"},{name:ln[4],ok:!0,detail:"read from configuration; never hard-coded"},{name:ln[5],ok:!o||(e?.proxy.routes??[]).every((s)=>s.auth.type.toLowerCase()!=="iap"||s.auth.audience!==""),detail:o?"IAP routes present":"no IAP routes"},{name:ln[6],ok:n.length===0||Boolean(e),detail:`${n.length} fixed ports from configuration`},{name:ln[7],ok:Object.keys(e?.profiles??{}).length>0,detail:`${Object.keys(e?.profiles??{}).length} profile(s)`},{name:ln[8],ok:r.length===0,detail:r.length===0?"configuration is valid":r[0]??"invalid"}]}var ln;var Uk=I(()=>{Yt();Mt();ke();tt();ln=["Repository","Environment","Google project","Authentication","Service accounts","IAP","Ports","Profiles","Validation"]});import{useEffect as vO,useState as yO}from"react";import{jsxDEV as ee}from"@opentui/react/jsx-dev-runtime";function ki(e){let{palette:t,title:r,tone:n="muted",children:o}=e,i=n==="success"?t.success:n==="warning"?t.warning:n==="error"?t.error:t.border;return ee("box",{border:!0,borderStyle:"rounded",borderColor:i,title:r,titleColor:i,flexDirection:"column",flexShrink:0,paddingLeft:1,paddingRight:1,marginBottom:1,overflow:"hidden",children:o},void 0,!1,void 0,this)}function Nf(e,t){if(t.color)return t.color;switch(t.tone){case"success":return e.success;case"warning":return e.warning;case"error":return e.error;case"primary":return e.primary;case"muted":return e.muted;default:return e.text}}function Us(e){let{palette:t,cells:r,header:n,gap:o=0,stripe:i}=e;return ee("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,backgroundColor:i?t.element:void 0,children:r.map((s,a)=>ee("box",{flexDirection:"row",flexShrink:0,overflow:"hidden",children:[a>0&&o>0?ee("box",{width:o,flexShrink:0},void 0,!1,void 0,this):null,ee("box",{width:s.width,flexShrink:0,overflow:"hidden",children:ee("text",{fg:n?t.primary:Nf(t,s),wrapMode:"none",children:oe(s.text,s.width)},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},`${s.text}-${a}`,!0,void 0,this))},void 0,!1,void 0,this)}function Wk(e){let{palette:t,width:r}=e;return ee("box",{height:1,flexShrink:0,overflow:"hidden",children:ee("text",{fg:t.border,wrapMode:"none",children:oe("\u2500".repeat(Math.max(1,r)),r)},void 0,!1,void 0,this)},void 0,!1,void 0,this)}function Bs(e){let{palette:t,facts:r,width:n}=e,o=Gp(r,n),i=[{text:"what",width:o.what},{text:"reading",width:o.reading}];if(o.meter>0)i.push({text:"used",width:o.meter});return i.push({text:"meaning",width:o.meaning}),ee("box",{flexDirection:"column",overflow:"hidden",flexShrink:0,children:[ee(Us,{palette:t,header:!0,gap:pi,cells:i},void 0,!1,void 0,this),ee(Wk,{palette:t,width:n},void 0,!1,void 0,this),r.map((s,a)=>{let c=s.meaning===""?[""]:Xc(s.meaning,Math.max(1,o.meaning)),l=s.meter?jx(s.meter):o.meter>0?"":void 0,d=[{text:s.what,width:o.what,tone:"text"},{text:s.reading,width:o.reading,tone:s.tone??"text"}];if(o.meter>0)d.push({text:l??"",width:o.meter,tone:s.meter?s.tone??"text":"muted"});return d.push({text:c[0]??"",width:o.meaning,tone:"muted"}),ee("box",{flexDirection:"column",overflow:"hidden",flexShrink:0,children:[ee(Us,{palette:t,gap:pi,stripe:a%2===1,cells:d},void 0,!1,void 0,this),c.slice(1).map((u,f)=>{let p=[{text:"",width:o.what},{text:"",width:o.reading}];if(o.meter>0)p.push({text:"",width:o.meter});return p.push({text:u,width:o.meaning,tone:"muted"}),ee(Us,{palette:t,gap:pi,stripe:a%2===1,cells:p},`${s.what}-more-${f}`,!1,void 0,this)})]},`${s.what}-${s.reading}-${a}`,!0,void 0,this)})]},void 0,!0,void 0,this)}function Gk(e){let{palette:t,cfg:r,snap:n,width:o,onRefresh:i}=e,s=ye(),[,a]=yO(0);if(vO(()=>{let B=setInterval(()=>{a((z)=>{let de=z+1;if(i&&de%xO===0)i();return de})},bO);return()=>clearInterval(B)},[i]),!r)return ee(ze,{palette:t,title:"No configuration loaded",body:"Add services to .devctl/config.yaml or finish setup."},void 0,!1,void 0,this);let c=Object.keys(r.services).sort(),l=c.some((B)=>Bx(r.services[B])),d=Ix(c,n),u=zx(d,l),f=l&&d.live>0?d.healthy/d.live:d.total>0?d.live/d.total:0,p=si(f,wO),m=d.failed>0?"error":l&&d.live>0&&d.healthy<d.live?"warning":"success",h=Fx(o,s.pad),v=n?.session_id?Jl(n.session_id):void 0,b=v?ks(Date.now()-v.getTime()):"just started",g=n?.logs.total??0,_=n?.logs.errors??0,R=$x(n?.logs.counts??{}),S=n?.proxy.routes??[],y=n?.credentials,x=(y?.entries??[]).filter((B)=>B.valid).length,w=n?.system,k=w?w.memAvailableKB??w.memFreeKB:0,D=w?Wx(w.loadAvg1,w.cpuCount):void 0,O=w?Gx(k,w.memTotalKB):void 0,F=[];if(w&&D&&O){F.push(D,O,{what:"Computer",reading:Ux(w.platform),meaning:`on for ${ks(w.hostUptimeSec*SO)}`,tone:"text"});let B=n?.stats_series;if(B&&B.cpu.length>1)F.push({what:"Load trend",reading:Wp(B.cpu),meaning:`${Math.round(B.interval_ms/1000)}s samples`,tone:"text"});if(B&&B.mem.length>1)F.push({what:"RAM trend",reading:Wp(B.mem),meaning:"used memory over the same window",tone:"text"})}let Z=d.failed>0||D?.tone==="error"||O?.tone==="error"?"error":m==="warning"||D?.tone==="warning"||O?.tone==="warning"?"warning":"success",W=[{what:"This session",reading:b,meaning:"how long this run has been going",tone:"text"},{what:"Profile",reading:n?.profile||"none",meaning:"which group of services is selected",tone:n?.profile?"text":"muted"},{what:"This window",reading:n?.detached?"closed":"watching",meaning:n?.detached?"services keep running without this screen":"you are watching the services from here",tone:n?.detached?"warning":"text"},{what:"Project folder",reading:"",meaning:r.repoRoot,tone:"muted"}],C=[{what:"Lines kept",reading:String(g),meaning:"log lines still in memory",tone:"text"},{what:"Error lines",reading:String(_),meaning:_>0?"open Logs and press e to see only errors":"no ERROR or FATAL lines",tone:_>0?"error":"muted"},...R.map(([B,z])=>({what:B,reading:String(z),meaning:"lines from this service",tone:"text"}))],j=[{what:"Web proxy",reading:n?.proxy.running?"on":"off",meaning:n?.proxy.running?`${S.length} apps${n.proxy.address?` at ${n.proxy.address}`:""}`:"not forwarding local URLs",tone:n?.proxy.running?"success":"muted"},{what:"Agent tools",reading:n?.mcp?.running?"on":"off",meaning:n?.mcp?.address?`Cursor talks to devctl at ${n.mcp.address}`:"turn on from Settings \u2192 MCP",tone:n?.mcp?.running?"success":"muted"},{what:"Google account",reading:n?.identity.user?"signed in":"not signed in",meaning:[n?.identity.user,n?.identity.project].filter(Boolean).join(" \xB7 ")||"no Google user for this session",tone:n?.identity.user?"success":"muted"},{what:"Saved tokens",reading:y?`${x} of ${y.entries.length} good`:"none",meaning:y?Vx(y.backend):"nothing stored yet",tone:y&&x<y.entries.length?"warning":y?"success":"muted"}];return ee(kt,{palette:t,title:"stats",scroll:!0,children:[ee(ki,{palette:t,title:`Your services ${d.live} of ${d.total} started`,tone:Z,children:[ee(Bs,{palette:t,facts:u,width:h},void 0,!1,void 0,this),ee("box",{height:1,flexShrink:0},void 0,!1,void 0,this),ee("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,children:ee("text",{wrapMode:"none",children:[ee("span",{fg:t.muted,children:oe(l?"Ready":"Started",Gp(u,h).what)},void 0,!1,void 0,this),ee("span",{children:" ".repeat(pi)},void 0,!1,void 0,this),ee("span",{fg:Nf(t,{text:"",width:1,tone:m}),children:`[${p}]`},void 0,!1,void 0,this),ee("span",{fg:Nf(t,{text:"",width:1,tone:m}),children:l?` ${d.healthy} of ${d.live||d.total} passing checks`:` ${d.live} of ${d.total} up`},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this),w?ee(ki,{palette:t,title:"This computer",tone:D?.tone==="error"||O?.tone==="error"?"error":D?.tone==="warning"||O?.tone==="warning"?"warning":"success",children:ee(Bs,{palette:t,facts:F,width:h},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,ee(ki,{palette:t,title:"This session",children:ee(Bs,{palette:t,facts:W,width:h},void 0,!1,void 0,this)},void 0,!1,void 0,this),ee(ki,{palette:t,title:"Each service",tone:d.failed>0?"error":l&&d.live>0&&d.healthy<d.live?"warning":"success",children:d.total===0?ee("text",{fg:t.muted,children:"No services in this project yet."},void 0,!1,void 0,this):ee("box",{flexDirection:"column",overflow:"hidden",flexShrink:0,children:[ee(kO,{palette:t,names:c,snap:n,width:h},void 0,!1,void 0,this),ee("box",{height:1,flexShrink:0},void 0,!1,void 0,this),ee("text",{fg:t.muted,wrapMode:"none",children:oe("CPU is % of one core. RAM is what that process is holding now.",h)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this),ee(ki,{palette:t,title:"Log lines",tone:_>0?"warning":"muted",children:ee(Bs,{palette:t,facts:C,width:h},void 0,!1,void 0,this)},void 0,!1,void 0,this),ee(ki,{palette:t,title:"Other parts",children:ee(Bs,{palette:t,facts:j,width:h},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function kO(e){let{palette:t,names:r,snap:n,width:o}=e,i=Nx(o),s=[{text:"service",width:i.name},{text:"status",width:Tr}];if(i.health)s.push({text:"check",width:Ur});if(i.cpu)s.push({text:"cpu",width:_s});if(i.mem)s.push({text:"ram",width:Rs});if(i.up)s.push({text:"up",width:Ps});if(i.rst)s.push({text:"restarts",width:ll});if(i.pid)s.push({text:"pid",width:sn});return ee("box",{flexDirection:"column",overflow:"hidden",flexShrink:0,children:[ee(Us,{palette:t,header:!0,gap:$t,cells:s},void 0,!1,void 0,this),ee(Wk,{palette:t,width:o},void 0,!1,void 0,this),r.map((a,c)=>ee(RO,{palette:t,name:a,rt:n?.services[a],cols:i,stripe:c%2===1},a,!1,void 0,this))]},void 0,!0,void 0,this)}function _O(e){if(e===void 0)return"muted";if(e>PO)return"error";if(e>CO)return"warning";return"text"}function RO(e){let{palette:t,name:r,rt:n,cols:o,stripe:i}=e,s=Kx(n),a=qx(n),c=n?.restarts??0,l=[{text:r,width:o.name,color:Ze(r,t)},{text:s,width:Tr,color:et(t,n?.state??"STOPPED")}];if(o.health)l.push({text:a,width:Ur,color:et(t,n?.health??"UNKNOWN")});if(o.cpu)l.push({text:n?.cpuPercent!==void 0?Pb(n.cpuPercent):"\u2014",width:_s,tone:_O(n?.cpuPercent)});if(o.mem)l.push({text:n?.memoryKB!==void 0?Cs(n.memoryKB):"\u2014",width:Rs,tone:"text"});if(o.up)l.push({text:Ox(n),width:Ps,tone:"text"});if(o.rst)l.push({text:String(c),width:ll,tone:c>0?"warning":"muted"});if(o.pid)l.push({text:n?.pid?String(n.pid):"\u2014",width:sn,tone:"muted"});return ee("box",{flexDirection:"column",flexShrink:0,overflow:"hidden",children:[ee(Us,{palette:t,gap:$t,stripe:i,cells:l},void 0,!1,void 0,this),n?.last_error?ee("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,children:ee("text",{fg:t.error,wrapMode:"none",children:oe(` ${n.last_error}`,Math.max(24,o.name+$t+Tr+Ur))},void 0,!1,void 0,this)},void 0,!1,void 0,this):null]},void 0,!0,void 0,this)}var bO=1000,xO=5,wO=16,SO=1000,CO=80,PO=100;var Kk=I(()=>{Yt();Mt();Ot();gr();Lt();dl();ke();tt()});import{useRenderer as EO,useTerminalDimensions as TO}from"@opentui/react";import{useCallback as jn,useEffect as AO,useMemo as qk,useRef as Vr,useState as Je}from"react";import{jsxDEV as he,Fragment as LO}from"@opentui/react/jsx-dev-runtime";function Vk({controller:e,tui:t,onQuit:r,onDown:n,onAttached:o,bootError:i,bootErrorMissing:s=!1,terminalBackground:a,workspace:c}){let{saveTuiPreferences:l,resolveTuiOverridePath:d,userTuiConfigPath:u,validateConfigText:f,freePort:p,readTextFile:m,writeTextFile:h,createStarterConfig:v,validate:b}=c,[g,_]=Je(e),[R,S]=Je(i),y=EO(),{width:x,height:w}=TO(),[k,D]=Je(g?"dashboard":"setup"),[O,F]=Je("none"),[Z,W]=Je(""),[C,j]=Je("commands"),[B,z]=Je(0),[de,Ee]=Je(0),[Ne,ne]=Je(0),[H,ae]=Je([]),[Q,nt]=Je(),[ft,ie]=Je(R??""),[Le,Me]=Je({}),[Ke,gt]=Je(),[Xe,Ct]=Je(Tb(g?.cfg)),[ge,vt]=Je(""),[at,yt]=Je(!1),[rr,Qe]=Je(),[Xt,qe]=Je(),[mt,br]=Je(),Kt=Vr(null),xr=Vr(null),nr=Vr(null),qt=Vr(null),or=Vr(null),ir=Vr(null),sr=Vr(null),Vt=Vr(""),[ce,K]=Je("quit"),A=Vr(void 0),ve=Vr(0),[J,Dt]=Je(g?.cfg),ct=g?.previousPersisted,[Ce,Po]=Je(void 0),P=wS({tui:t,controller:g,resolveTuiOverridePath:d,terminalBackground:a,userTuiConfigPath:u,snap:Q,setStatus:ie,saveTuiPreferences:l,setPaletteIndex:z,setOverlay:F,setConfirmKind:K,setScreen:D,setSelected:ne,screen:k}),{themeName:ue,setThemeName:Yr,fontSize:Jr,prefsLocked:wr,palette:se,rootBackground:Bn,settingRows:Un,persistPrefs:Dr,persistTheme:dn,activateSetting:Ws}=P,Ci=lS({controller:g,workspace:c,cfg:J,snap:Q,screen:k,configReloadError:Ce,setSnap:nt,setStatus:ie}),{google:Pi,doctor:le,doctorLoading:bt,doctorError:Pt,doctorProgress:Xr,setDoctorTick:Gs}=Ci,_o=t.keybinds.copy??Zr(),Sr=qk(()=>Object.keys(J?.services??{}).sort(),[J]),un=jn(async()=>{if(!g)return;try{let X=await g.status();if(nt(X),X.profile&&Xe==="")Ct(X.profile);return X}catch(X){ie(L(X));return}},[g,Xe]),Hl=hS({controller:g,tui:t,names:Sr,screen:k,refresh:un,setStatus:ie}),{logs:Gf,setLogs:yC,paused:Kf,errorOnly:jl,setErrorOnly:Bl,showSystemLogs:qf,logSearch:Vf,setLogSearch:zf,logSearchFocused:bC,setLogSearchFocused:xC,logFollow:Yf,logSince:wC,logService:Jf,setLogService:Ul,logServiceB:SC,setLogServiceB:kC,splitLogs:Wl,splitFocus:Gl,setSplitFocus:CC,logSliceB:Xf,logWindowB:Qf,logSelectedB:Zf,logPinnedB:PC,pinLogViewB:_C,logServices:RC,logShowTimestamps:EC,logShowMeta:TC,logSource:AC,logRegex:em,logWrap:tm,logPinned:rm,logSelected:nm,logsFullscreen:Kl,dashboardLogCursor:LC,setDashboardLogCursor:DC,logFacets:om,logSources:im,filteredLogs:sm,filteredLogsB:IC,logWindow:Ks,logSlice:qs,pinLogView:ql,applyLogCursorA:MC,applyLogCursorB:OC,jumpToLatestLogs:am}=Hl,Ro=qk(()=>{if(C==="tasks")return Np(Object.keys(J?.tasks??{}),Z,"tasks","run this task");if(C==="services")return Np(Object.keys(J?.services??{}),Z,"services","exec in this service");return Tx(Z,{services:Object.keys(J?.services??{}),tasks:Object.keys(J?.tasks??{})})},[J,Z,C]);AO(()=>{Ee(0)},[Z]);let Vl=ex(k,{doctor:le?.checks.length??0,settings:Un.length,profiles:Object.keys(J?.profiles??{}).length,services:Sr.length,logs:Wl&&Gl===1?Xf.length:qs.length,mcp:mw(),config:Object.keys(J?.tasks??{}).length}),cm=k==="logs"?Wl&&Gl===1?Zf:nm:Ne,Ir=Vl<=0?Math.max(0,cm):Math.max(0,Math.min(cm,Vl-1)),$C=k==="detail"?ge:k==="services"?Sr[Ir]??"":"",{inspectorEnv:lm,inspectorEnvStatus:dm,inspectorEnvError:um,resolveEnvironment:FC}=ES({controller:g,cfg:J,envService:$C}),Eo=jn(()=>{if(F("none"),W(""),j("commands"),xC(!1),A.current)clearTimeout(A.current)},[]),zl=jn((X)=>{ae((xt)=>xt.includes(X)?xt.filter((QC)=>QC!==X):[...xt,X])},[]),pm=yS({tui:t,controller:g,cfg:J,persistPrefs:Dr,snap:Q,refresh:un,setStatus:ie}),{mcpPortDraft:NC,mcpPort:HC,toggleMcpTool:jC,toggleMcp:BC,copyMcpSnippet:UC}=pm;iS({controller:g,cfg:J,paused:Kf,logSince:wC,refresh:un,setLogs:yC,setCfg:Dt,setConfigReloadError:Po,setStatus:ie});let Vs=jn((X)=>{vt(X),D("detail")},[]),fm=jn((X)=>{let xt=X.value!==""?X.value:X.required?"(required, not set)":"(empty)";br({title:X.key,body:xt}),F("scroll-text")},[]),Yl=uS({controller:g,workspace:c,cfg:J,snap:Q,refresh:un,setStatus:ie,setOverlay:F}),{plan:zs,planInitiallyRunning:WC,planBusy:GC,lifecycle:mm}=Yl,Qr=PS({workspace:c,renderer:y,setOverlay:F,setStatus:ie,setScreen:D,onAttached:o,setController:_,setCfg:Dt,setBootError:S}),{configEditRef:KC,configEditText:qC,configEditError:VC,setConfigEditError:zC,openConfigBuffer:hm,saveConfigBuffer:YC}=nS({cfg:J,readTextFile:m,setOverlay:F,setStatus:ie,validateConfigText:f,writeTextFile:h,controller:g,setConfirmKind:K,refresh:un}),gm=jn(async()=>{let X=y.getSelection()?.getSelectedText()??"";if(X.trim()===""){ie("Nothing selected \u2014 drag to highlight text");return}try{await ml(X);let xt=X.split(`
2233
+ `).length;ie(xt===1?"Copied selection":`Copied ${xt} selected lines`)}catch(xt){ie(L(xt))}},[y]),{runCommand:Ys}=Zw({setOverlay:F,setQuery:W,setSlashPicker:j,checked:H,setConfirmKind:K,setStatus:ie,persistTheme:dn,setPaletteIndex:z,themeName:ue,setScreen:D,setSelected:ne,screen:k,renderer:y,cfg:J,setScrollText:br,reveal:at,controller:g,refresh:un,profile:Xe,setReveal:yt,resolveEnvironment:FC,openDetail:Vs,copySelection:gm,lastExportPath:Vt,openConfigBuffer:hm,onDown:n??((X)=>r(X)),workspace:c,logView:Hl,diagnostics:Ci,lifecycleActions:Yl}),vm=jn(()=>{let X=ui(Ro,de);if(C==="tasks"){if(!X){ie(Ro.length===0?"no tasks configured":"pick a task");return}j("commands");let xt=Wr("run");if(xt)Ys(xt,[X.name]);return}if(C==="services"){if(!X){ie(Ro.length===0?"no services configured":"pick a service");return}j("commands"),W(`exec ${X.name} -- `);return}if(!X){ie(Z.trim()===""?"pick a command from the list":`unknown command /${Z}`),Eo();return}Ys(X,Ex(X,Z))},[Eo,Ro,Z,Ys,de,C]),ym=jn((X)=>{dn(X),Eo()},[Eo,dn]);Mw({tui:t,controller:g,cfg:J,snap:Q,ui:{screen:k,overlay:O,onQuit:r,onDown:n??((X)=>r(X)),closeOverlay:Eo,confirmKind:ce,confirmDetail:Le,portTarget:Ke,profile:Xe,listCursor:Ir,names:Sr,listCount:Vl,openDetail:Vs,copySelection:gm,height:w,paletteIndex:B,applyTheme:ym,runCommand:Ys,saveConfigBuffer:YC,filtered:Ro,slashIndex:de,submitSlash:vm,advanceWizard:()=>void Qr.advanceWizard(),bootErrorMissing:s,bootError:R,createStarterConfig:v,startWizard:Qr.startWizard,toggleChecked:zl,checked:H,detailName:ge,refresh:un,openConfigBuffer:hm,setOverlay:F,setConfirmKind:K,setConfirmDetail:Me,setPortTarget:gt,setLogDetail:Qe,logDetail:rr,setLogSearch:zf,setProfile:Ct,setStatus:ie,setPaletteIndex:z,setSlashIndex:Ee,setQuery:W,setSlashPicker:j,setScreen:D,setSelected:ne,setChecked:ae,setConfigEditError:zC,freePort:p},logView:Hl,lifecycleActions:Yl,mcp:pm,preferences:P,diagnostics:Ci,refs:{interruptArmedAt:ve,leaderTimer:A,logDetailsScrollRef:qt,scrollTextScrollRef:ir,routeDetailsScrollRef:or,planScrollRef:sr,helpScrollRef:xr,configScrollRef:Kt,detailScrollRef:nr}});let bm=cx(ce,Xe,Le),JC=new Set(zs?.waves.flat()??[]),XC=zs===void 0||mm==="stop"?"":Object.entries(Q?.services??{}).find(([X,xt])=>JC.has(X)&&xt.state==="FAILED")?.[0]??"";return he(bp.Provider,{value:ws(Jr),children:he("box",{flexDirection:"column",width:x,height:w,backgroundColor:Bn,overflow:"hidden",children:[Kl?null:he(LO,{children:[he(Xx,{palette:se,cfg:J,snap:Q,google:Pi,profile:Xe,reveal:at,width:x},void 0,!1,void 0,this),he(Qx,{palette:se,screen:k,width:x,onSelect:D},void 0,!1,void 0,this),Ce?he("box",{height:1,paddingLeft:1,backgroundColor:se.panel,overflow:"hidden",children:he("text",{fg:se.error,wrapMode:"none",children:`\u26A0 configuration reload failed: ${Ce}`},void 0,!1,void 0,this)},void 0,!1,void 0,this):null]},void 0,!0,void 0,this),he("box",{flexGrow:1,overflow:"hidden",children:[k==="dashboard"?he(mk,{palette:se,cfg:J,snap:Q,logs:Gf,names:Sr,selected:Ir,checked:H,width:x,followTick:Yf,logService:Jf,errorOnly:jl,showSystemLogs:qf,onOpen:Vs,onSelectIndex:ne,selectedLog:LC,onToggle:zl,logSources:im,onFilterService:Ul,onToggleErrors:()=>Bl((X)=>!X),onShowErrors:()=>{Ul(""),Bl(!0)},wrapMode:tm,view:qs,follow:!rm,onLeaveLatest:ql,onPickLog:(X)=>{let xt=qs[X];if(!xt)return;DC(X),Qe(xt),ql()},viewStart:Ks.start,viewTotal:sm.length,newer:Ks.newer,onJumpLatest:am,facets:om,leftover:ct,search:Vf,regex:em},void 0,!1,void 0,this):null,k==="services"?he(Mk,{palette:se,cfg:J,names:Sr,snap:Q,selected:Ir,checked:H,width:x,reveal:at,onOpen:Vs,onSelectIndex:ne,onToggle:zl,resolvedEnv:lm,envStatus:dm,envError:um,onSelectEnv:fm},void 0,!1,void 0,this):null,k==="detail"?he(Ik,{palette:se,cfg:J,snap:Q,name:ge,reveal:at,width:x,envScrollRef:nr,resolvedEnv:lm,envStatus:dm,envError:um,onSelectEnv:fm},void 0,!1,void 0,this):null,k==="logs"?he(bk,{palette:se,logs:Gf,names:Sr,logSources:im,service:Jf,errorOnly:jl,showSystemLogs:qf,search:Vf,searchFocused:bC,followTick:Yf,width:x,onSearch:zf,onService:Ul,onToggleErrors:()=>Bl((X)=>!X),source:AC,regex:em,services:RC,showTimestamps:EC,showMeta:TC,view:qs,wrapMode:tm,selected:nm,follow:!rm,newer:Ks.newer,viewStart:Ks.start,viewTotal:sm.length,onSelect:MC,fullscreen:Kl,onLeaveLatest:ql,onJumpLatest:am,facets:om,split:Wl,splitFocus:Gl,serviceB:SC,viewB:Xf,selectedB:Zf,followB:!PC,newerB:Qf.newer,viewStartB:Qf.start,viewTotalB:IC.length,onServiceB:kC,onSelectB:OC,onLeaveLatestB:_C,onFocusPane:CC},void 0,!1,void 0,this):null,k==="auth"?he(ZS,{palette:se,cfg:J,google:Pi,identity:Q?.identity},void 0,!1,void 0,this):null,k==="credentials"?he(sk,{palette:se,credentials:Q?.credentials},void 0,!1,void 0,this):null,k==="proxy"?he(Tk,{palette:se,cfg:J,snap:Q,width:x,onSelectRoute:(X)=>{qe(X),F("route-details")}},void 0,!1,void 0,this):null,k==="mcp"?he(hw,{palette:se,snap:Q,port:HC,portDraft:NC,selected:Ir,onPick:ne,onToggle:()=>{BC()},onCopy:UC,onToggleTool:(X)=>{jC(X)}},void 0,!1,void 0,this):null,k==="doctor"?he(gk,{palette:se,report:le,loading:bt,progress:Xr,error:Pt,selected:Ir,onPick:ne,onReload:()=>{Gs((X)=>X+1),ie("Re-running doctor")}},void 0,!1,void 0,this):null,k==="stats"?he(Gk,{palette:se,cfg:J,snap:Q,width:x,onRefresh:un},void 0,!1,void 0,this):null,k==="config"?he(ok,{palette:se,cfg:J,width:x,selectedTask:Ir,scrollRef:Kt},void 0,!1,void 0,this):null,k==="profiles"?he(Sk,{palette:se,cfg:J,snap:Q,profile:Xe,selected:Ir,onPick:ne},void 0,!1,void 0,this):null,k==="setup"?he(Bk,{issues:J?b(J):["configuration not loaded"],palette:se,cfg:J,google:Pi,bootError:R,bootErrorMissing:s,step:Ir},void 0,!1,void 0,this):null,k==="settings"?he(Hk,{palette:se,items:Un,selected:Ir,locked:wr,width:x,onPick:ne,onActivate:Ws},void 0,!1,void 0,this):null]},void 0,!0,void 0,this),O==="slash"?he(qS,{palette:se,items:Ro,query:Z,selected:de,title:C==="tasks"?"tasks":C==="services"?"services":"commands",onQuery:W,onSubmit:vm},void 0,!1,void 0,this):null,O==="themes"?he(XS,{palette:se,themeName:ue,selected:B%St.length,termW:x,termH:w,onIndex:z,onPreview:Yr,onPick:ym},void 0,!1,void 0,this):null,O==="help"?he(bw,{palette:se,termW:x,termH:w,copyKey:_o,scrollRef:xr},void 0,!1,void 0,this):null,O==="leader"?he(OS,{palette:se,termW:x,termH:w},void 0,!1,void 0,this):null,O==="confirm"?he(DS,{palette:se,title:bm.title,body:bm.body,kind:ce,termW:x,termH:w},void 0,!1,void 0,this):null,O==="setup-wizard"?he(kS,{palette:se,termW:x,termH:w,step:Qr.step,answers:Qr.answers,repo:Qr.repo,draft:Qr.draft,authStatus:Qr.authStatus,onDraft:Qr.setDraft,onSubmit:()=>void Qr.advanceWizard()},void 0,!1,void 0,this):null,O==="log-details"?he(FS,{palette:se,event:rr,termW:x,termH:w,scrollRef:qt},void 0,!1,void 0,this):null,O==="route-details"?he(US,{palette:se,route:Xt,termW:x,termH:w,scrollRef:or},void 0,!1,void 0,this):null,O==="scroll-text"&&mt?he(GS,{palette:se,title:mt.title,body:mt.body,termW:x,termH:w,scrollRef:ir},void 0,!1,void 0,this):null,O==="config-edit"&&J?he(AS,{palette:se,path:J.configPath,initialValue:qC,error:VC,termW:x,termH:w,textareaRef:KC},void 0,!1,void 0,this):null,O==="plan"&&zs?he(jS,{palette:se,plan:zs,snap:Q,busy:GC,failed:XC,kind:mm,termW:x,termH:w,scrollRef:sr,initiallyRunning:WC,onDismiss:Eo},void 0,!1,void 0,this):null,Kl?null:he(Zx,{palette:se,screen:k,overlay:O,status:ft,paused:Kf,errorOnly:jl,width:x,copyKey:_o},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this)}var zk=I(()=>{te();Yt();qp();Mt();vr();wo();In();Lt();Ow();eS();oS();sS();dS();pS();gS();bS();SS();_S();TS();LS();IS();ff();$S();NS();BS();WS();KS();bf();VS();QS();ek();ik();ak();hk();vk();xk();$s();kk();Ak();$f();Ok();jk();Uk();Kk();Er();tt();Rr()});var Yk={};rP(Yk,{renderApp:()=>jf,runTui:()=>Hf,runTuiWithController:()=>MO});import{createCliRenderer as DO}from"@opentui/core";import{createRoot as IO}from"@opentui/react";import{jsxDEV as OO}from"@opentui/react/jsx-dev-runtime";async function MO(e,t){let r=e.loadTuiConfig(t.cfg.repoRoot,t.cfg.ui.keymap);await jf(e,t,r)}async function Hf(e,t){let r,n,o=!1;try{r=await e.openTui("",t)}catch(s){n=L(s),o=ba(s,qn)}let i=e.loadTuiConfig(r?.cfg.repoRoot??process.cwd(),r?.cfg.ui.keymap);await jf(e,r,i,n,o)}async function jf(e,t,r=e.loadTuiConfig(t?.cfg.repoRoot??process.cwd(),t?.cfg.ui.keymap),n,o=!1){Js();let i=Sm(),s=await DO({exitOnCtrlC:!1,useMouse:r.mouse,enableMouseMovement:r.mouse,useKittyKeyboard:{}}),a=IO(s),c=null;try{c=(await s.getPalette({timeout:120,size:16})).defaultBackground}catch{}await new Promise((l)=>{let d=(f)=>{let p=()=>{a.unmount(),i(),s.destroy(),l(),process.exit(0)};if(!t){p();return}t.close({detach:f,shutdownSupervisor:!0}).finally(p)},u=(f)=>{let p=()=>{a.unmount(),i(),s.destroy(),l(),process.exit(0)};if(!t){p();return}t.shutdown({stopServices:!f}).then(()=>t?.close({detach:!0})).finally(p)};a.render(OO(Vk,{workspace:db(e),controller:t,tui:r,onQuit:d,onDown:u,onAttached:(f)=>{t=f},bootError:n,bootErrorMissing:o,terminalBackground:c},void 0,!1,void 0,this))})}var Bf=I(()=>{ub();te();zk()});if(process.env.METADATA_SERVER_DETECTION===void 0)process.env.METADATA_SERVER_DETECTION="bios-only";if(process.env.GCE_METADATA_TIMEOUT===void 0)process.env.GCE_METADATA_TIMEOUT="0";import{existsSync as ia,readFileSync as ra}from"fs";import{dirname as IP,isAbsolute as Dm,join as oa,resolve as MP}from"path";Xs();Xs();import{spawnSync as dP}from"child_process";import{randomBytes as Zl}from"crypto";import{copyFileSync as uP,existsSync as pn,mkdirSync as pP,readdirSync as fP,readFileSync as ed,renameSync as td,statSync as mP,unlinkSync as Xl,writeFileSync as hP}from"fs";import{homedir as km}from"os";import{basename as gP,dirname as Ql,isAbsolute as vP,join as _t,resolve as yP}from"path";var bP=448,xP=384;function _i(e,t){let r=e.startsWith("~/")?_t(km(),e.slice(2)):e;return vP(r)?r:yP(t,r)}function cr(){let e=process.env.DEVCTL_HOME;if(e&&e!=="")return e;return _t(km(),".devctl")}function Mr(e){pP(e,{recursive:!0,mode:bP})}function To(e){let t=_t(cr(),"state",ar(e)),r=_t(cr(),"sessions",ar(e));if(!pn(t)&&pn(r)){Mr(Ql(t));try{td(r,t)}catch{Mr(t);for(let n of["state.json","devctl.lock","devctl.sock"]){let o=_t(r,n);if(pn(o))uP(o,_t(t,n))}}}return Mr(t),t}function Qs(){let e=_t(cr(),"logs");return Mr(e),e}function Zs(){let e=_t(cr(),"exports");return Mr(e),e}function kr(){let e=_t(cr(),"credentials");return Mr(e),e}function ea(e,t=process.platform){if(t==="win32")return`\\\\.\\pipe\\devctl-${ar(e)}`;return _t(To(e),"devctl.sock")}function wP(e){return _t(To(e),"devctl.lock")}function Cm(e){return _t(To(e),"state.json")}function Ri(e){return _t(To(e),"bootstrap.log")}var SP=5,Pm="bootstrap-";function _m(e){let t=Ri(e);if(!pn(t))return;let r=To(e),n=_t(r,`${Pm}${rd()}.log`);try{td(t,n)}catch{return}kP(r)}function kP(e){let t;try{t=fP(e).filter((n)=>n.startsWith(Pm)&&n.endsWith(".log"))}catch{return}let r=t.map((n)=>{let o=_t(e,n);try{return{path:o,mtime:mP(o).mtimeMs}}catch{return}}).filter((n)=>n!==void 0).sort((n,o)=>o.mtime-n.mtime);for(let n=SP;n<r.length;n++)try{Xl(r[n].path)}catch{}}function Cr(e,t){Mr(Ql(e));let r=_t(Ql(e),`.${gP(e)}.tmp-${process.pid}-${Zl(4).toString("hex")}`);hP(r,t,{mode:xP}),td(r,e)}function rd(e=new Date){return`${e.toISOString().slice(0,19).replace(/:/g,"-")+"Z"}-${Zl(3).toString("hex")}`}function fn(e){return nd(Cm(e))}function nd(e){if(!pn(e))return;try{let t=JSON.parse(ed(e,"utf8"));if(Array.isArray(t.processes)&&typeof t.repo_root==="string")return t;return}catch{return}}function Rm(e,t){Cr(Cm(e),`${JSON.stringify(t,null,2)}
2234
+ `)}function Em(e,t){let r=wP(e);if(pn(r)){let n=CP(r);if(n&&Qt(n.pid))throw Error(`supervisor already running (pid ${n.pid})`);try{Xl(r)}catch{}}return Cr(r,JSON.stringify({pid:process.pid,socket:t})),{release:()=>{try{Xl(r)}catch{}}}}function CP(e){if(!pn(e))return;try{let t=JSON.parse(ed(e,"utf8"));if(typeof t.pid==="number"&&typeof t.socket==="string")return{pid:t.pid,socket:t.socket}}catch{return}return}function Qt(e){if(e<=0)return!1;if(process.platform==="win32")return PP(e);try{return process.kill(e,0),!0}catch{return!1}}function od(e){try{let t=dP("cmd.exe",["/d","/c",`tasklist /FO CSV /NH /FI "PID eq ${e}"`],{encoding:"buffer",windowsHide:!0,timeout:5000});return _P(t.stdout)}catch{return""}}function PP(e){try{return process.kill(e,0),!0}catch{}let t=od(e).toLowerCase();if(t===""||t.includes("no tasks")||t.includes("no matching"))return!1;return t.includes(String(e))}function _P(e){if(!e)return"";if(typeof e==="string")return e;if(e.length>=2&&e[0]===255&&e[1]===254)return e.toString("utf16le");if(e.length>=4&&e[1]===0&&e[3]===0)return e.toString("utf16le");return e.toString("utf8")}function id(){return Zl(24).toString("hex")}function RP(e){return _t(To(e),"mcp-token")}function Tm(e){let t=RP(e);if(pn(t)){let n=ed(t,"utf8").trim();if(n!=="")return n}let r=id();return Cr(t,r),r}ta();ta();var OP="OPENCODE_TUI_CONFIG";function sa(){return oa(cr(),"tui.json")}function aa(e=""){for(let t of[Am,OP]){let r=process.env[t];if(!r||r==="")continue;let n=Dm(r)?r:MP(e||process.cwd(),r);if(ia(n))return n}return}function $P(e,t){if(!t||Object.keys(t).length===0)return e;return{...e,keybinds:{...e.keybinds,...t}}}function Do(e,t){let r=$P(Lm(),t),n=aa(e);if(n)return cd(r,na(ra(n,"utf8")),n);let o=FP(e),i=o?cd(r,na(ra(o,"utf8")),o):r,s=sa();if(!ia(s)||o===s)return i;return cd(i,na(ra(s,"utf8")),s)}function Im(e){let t=sa(),r={};if(ia(t)){let o=na(ra(t,"utf8"));if(Wn(o))r=o}let n={...r};if(e.theme!==void 0)n.theme=e.theme;if(e.mouse!==void 0)n.mouse=e.mouse;if(e.leader_timeout!==void 0)n.leader_timeout=e.leader_timeout;if(e.font_size!==void 0)n.font_size=e.font_size;if(e.log_timestamps!==void 0)n.log_timestamps=e.log_timestamps;if(e.log_metadata!==void 0)n.log_metadata=e.log_metadata;if(e.mcp_enabled!==void 0)n.mcp_enabled=e.mcp_enabled;if(e.mcp_port===null)delete n.mcp_port;else if(e.mcp_port!==void 0)n.mcp_port=e.mcp_port;if(e.mcp_disabled_tools!==void 0)n.mcp_disabled_tools=e.mcp_disabled_tools;return Cr(t,`${JSON.stringify(n,null,2)}
2235
+ `),t}function FP(e){let t=aa(e);if(t)return t;let r=[e||process.cwd(),oa(e||process.cwd(),".devctl"),cr()],n=["tui.json","tui.jsonc"];for(let o of r)for(let i of n){let s=oa(o,i);if(ia(s))return s}return}function cd(e,t,r){let n=Wn(t)?t:{},o={...e.keybinds};if(Wn(n.keybinds)){for(let[c,l]of Object.entries(n.keybinds))if(typeof l==="string")o[c]=l}let i={...e.cursor};if(Wn(n.cursor)){if(HP(n.cursor.style))i.style=n.cursor.style;if(typeof n.cursor.blinking==="boolean")i.blinking=n.cursor.blinking}let s={...e.scroll_acceleration};if(Wn(n.scroll_acceleration)&&typeof n.scroll_acceleration.enabled==="boolean")s.enabled=n.scroll_acceleration.enabled;let a={...e.attention,sounds:{...e.attention.sounds}};if(Wn(n.attention)){if(typeof n.attention.enabled==="boolean")a.enabled=n.attention.enabled;if(typeof n.attention.notifications==="boolean")a.notifications=n.attention.notifications;if(typeof n.attention.sound==="boolean")a.sound=n.attention.sound;if(typeof n.attention.volume==="number")a.volume=n.attention.volume;if(typeof n.attention.sound_pack==="string")a.sound_pack=n.attention.sound_pack;if(Wn(n.attention.sounds)){for(let[c,l]of Object.entries(n.attention.sounds))if(typeof l==="string")a.sounds[c]=NP(l,r)}}return{theme:typeof n.theme==="string"?n.theme:e.theme,leader_timeout:typeof n.leader_timeout==="number"?n.leader_timeout:e.leader_timeout,font_size:typeof n.font_size==="number"&&Number.isFinite(n.font_size)?Math.round(n.font_size):e.font_size,keybinds:o,scroll_speed:typeof n.scroll_speed==="number"?n.scroll_speed:e.scroll_speed,scroll_acceleration:s,diff_style:n.diff_style==="stacked"?"stacked":"auto",cursor:i,mouse:typeof n.mouse==="boolean"?n.mouse:e.mouse,attention:a,log_timestamps:typeof n.log_timestamps==="boolean"?n.log_timestamps:e.log_timestamps,log_metadata:typeof n.log_metadata==="boolean"?n.log_metadata:e.log_metadata,mcp_enabled:typeof n.mcp_enabled==="boolean"?n.mcp_enabled:e.mcp_enabled,mcp_port:typeof n.mcp_port==="number"&&Number.isInteger(n.mcp_port)?n.mcp_port:e.mcp_port,mcp_disabled_tools:Array.isArray(n.mcp_disabled_tools)?n.mcp_disabled_tools.filter((c)=>typeof c==="string"):e.mcp_disabled_tools,path:r}}function NP(e,t){if(e.startsWith("file://")||Dm(e))return e;return oa(IP(t),e)}function HP(e){return e==="block"||e==="underline"||e==="line"||e==="default"}function Wn(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}function na(e){let t=e.replace(/\/\*[\s\S]*?\*\//g,"").replace(/^\s*\/\/.*$/gm,"");return JSON.parse(t)}import{existsSync as NT,readFileSync as HT,writeFileSync as jT}from"fs";import{spawn as Sd}from"child_process";import{createWriteStream as m_,existsSync as Oi,mkdirSync as h_,readdirSync as Pd,readFileSync as g_,rmSync as v_,statSync as Cd,writeFileSync as y_}from"fs";import{dirname as rh,join as No}from"path";ga();ga();var b_=50000,_d="session-";function th(e){return Buffer.from(JSON.stringify(e),"utf8").toString("base64url")}function x_(e){try{let t=JSON.parse(Buffer.from(e,"base64url").toString("utf8"));if(typeof t==="object"&&t!==null&&typeof t.session==="string"&&typeof t.seq==="number")return t}catch{}return}function kd(e,t){let r={...e};return r[t]=void 0,r}class Rd{events=[];eventStart=0;nextSeq=1;max;bus;detector;persistDir;persist;sessionID;streams=new Map;lastWrite=new Map;parsers=[];constructor(e,t,r,n,o,i,s=0,a=0){this.max=e>0?e:b_,this.bus=t,this.detector=r,this.sessionID=i;let c=o===""||o.startsWith("~/")?Qs():o;if(this.persist=n&&i!=="",this.persistDir=this.persist?No(c,`${_d}${i}`):"",this.persist)h_(this.persistDir,{recursive:!0,mode:448}),k_(c,s,a)}sessionDir(){return this.persistDir}setParsers(e){this.parsers=e}append(e){let t=gd(e.message),r=this.parseLine(t),n=(c)=>this.detector?this.detector.redactText(c):c,o=r.raw!==void 0,i=n(t),s=r.message!==void 0?n(gd(r.message)):void 0,a={...e,timestamp:e.timestamp||new Date().toISOString(),level:e.level||r.level||yd(t),request_id:e.request_id||r.request_id||bd(t),message:o&&s!==void 0?s:i,raw:o?i:void 0,seq:this.nextSeq++};if(this.events.length<this.max)this.events.push(a);else this.events[this.eventStart]=a,this.eventStart=(this.eventStart+1)%this.max;if(this.bus?.publish(Se(Io,a.service,{event:a,level:a.level})),this.persist){let c=`${a.timestamp} ${a.service} ${a.level} ${a.raw??a.message}
2236
+ `,l=S_(a.service),d=this.streamFor(l);this.lastWrite.set(l,new Promise((u)=>{d.write(c,()=>u())}))}return a}async flush(){await Promise.all([...this.lastWrite.values()])}parseLine(e){let t={};for(let r of this.parsers)try{let n=r.parse(e);if(n)t={...t,...n}}catch{}return t}streamFor(e){let t=this.streams.get(e);if(t===void 0)t=m_(No(this.persistDir,`${e}.log`),{flags:"a",mode:384}),t.on("error",()=>{}),this.streams.set(e,t);return t}async close(){await this.flush(),await Promise.all([...this.streams.values()].map((e)=>new Promise((t)=>{e.end(()=>t())}))),this.streams.clear(),this.lastWrite.clear()}query(e){let t=wd(e),r=[];return this.forEachEvent((n)=>{if(t(n))r.push(n)}),r}queryPage(e,t={}){let r=Zm(t.limit),n=t.cursor?x_(t.cursor):void 0,o=n!==void 0&&n.session!==this.sessionID,i=o?void 0:n,s=i?t.direction??"backward":"backward",a=wd(e),c=[];this.forEachEvent((m)=>{if(a(m))c.push(m)});let l;if(!i)l=c.slice(Math.max(0,c.length-r));else if(s==="forward")l=c.filter((m)=>m.seq>i.seq).slice(0,r);else{let m=c.filter((h)=>h.seq<i.seq);l=m.slice(Math.max(0,m.length-r))}let d=l[0]?.seq,u=l[l.length-1]?.seq,f=d!==void 0&&c.some((m)=>m.seq<d),p=u!==void 0&&c.some((m)=>m.seq>u);return{events:l,prevCursor:th({session:this.sessionID,seq:d??i?.seq??0}),nextCursor:th({session:this.sessionID,seq:u??i?.seq??this.nextSeq-1}),hasNext:p,hasPrev:f,sessionChanged:o}}queryFacets(e){let t=kd(e,"services"),r=kd(e,"level"),n=kd(e,"source"),o=xd(e),i=0,s={},a={},c={};return this.forEachEvent((l)=>{if(!o(l))return;if(Fo(e,l))i+=1;if(Fo(t,l))s[l.service]=(s[l.service]??0)+1;if(Fo(r,l))a[l.level]=(a[l.level]??0)+1;if(Fo(n,l))c[l.source]=(c[l.source]??0)+1}),{total:i,byService:s,byLevel:a,bySource:c}}snapshot(){let e={},t=0;return this.forEachEvent((r)=>{if(e[r.service]=(e[r.service]??0)+1,r.level===Kn||r.level===Oo)t+=1}),{total:this.events.length,errors:t,counts:e}}forEachEvent(e){let t=this.events.length;for(let r=0;r<t;r+=1){let n=this.events[(this.eventStart+r)%t];if(n)e(n)}}exportTo(e,t){Ed(e,this.query(t))}}function nh(e){return{append:(t)=>{e.append(t)},query:async(t)=>e.query(t),queryPage:async(t,r)=>e.queryPage(t,r),queryFacets:async(t)=>e.queryFacets(t),snapshot:()=>e.snapshot(),exportTo:async(t,r)=>{e.exportTo(t,r)},setParsers:(t)=>{e.setParsers(t)},setSecrets:(t,r)=>{},close:()=>e.close()}}function w_(e=new Date){let t=e.toISOString().replace(/[:.]/g,"-");return No(Zs(),`devctl-logs-${t}.log`)}function oh(e=""){if(e==="")return w_();return _i(e,process.cwd())}function Ed(e,t){Mr(rh(e));let r=t.map((n)=>`${n.timestamp} ${n.service} ${n.level} ${n.raw??n.message}`);y_(e,`${r.join(`
2165
2237
  `)}
2166
- `,{mode:384})}function rh(e){let t=Ii(e)&&kd(e).isDirectory()?e:Zm(e);if(Mr(t),process.platform==="darwin"){let r=Ii(e)&&kd(e).isFile()?["-R",e]:[t];wd("open",r,{detached:!0,stdio:"ignore"}).unref();return}if(process.platform==="win32"){wd("explorer",[t],{detached:!0,stdio:"ignore"}).unref();return}wd("xdg-open",[t],{detached:!0,stdio:"ignore"}).unref()}function nh(e=Js()){if(!Ii(e))return[];return Cd(e).filter((t)=>t.startsWith(Pd)).sort().reverse()}function oh(e,t=Js()){let r=qn(t,e);if(!Ii(r))return[];let n=[];for(let i of Cd(r)){if(!i.endsWith(".log"))continue;let s=lR(qn(r,i),"utf8");for(let a of s.split(`
2167
- `)){if(a.trim()==="")continue;let c=a.split(" "),l=c.slice(3).join(" "),d=gd(l);n.push({timestamp:c[0]??"",service:c[1]??i.replace(/\.log$/,""),source:"history",level:c[2]??"INFO",message:d?.message??l,raw:d?l:void 0,pid:0,seq:0})}}let o=n.sort((i,s)=>i.timestamp.localeCompare(s.timestamp));return o.forEach((i,s)=>{i.seq=s+1}),o}function yR(e){let t=e.replace(/[^A-Za-z0-9._-]+/g,"_");return t===""?"service":t}function xR(e,t,r){if(!Ii(e))return;let n=Cd(e).filter((i)=>i.startsWith(Pd)).map((i)=>{let s=qn(e,i),a=kd(s);return{path:s,mtime:a.mtimeMs}}).sort((i,s)=>s.mtime-i.mtime),o=t>0?Date.now()-t*86400000:0;n.forEach((i,s)=>{let a=o>0&&i.mtime<o,c=r>0&&s>=r;if(a||c)dR(i.path,{recursive:!0,force:!0})})}te();var{spawn:Ad}=globalThis.Bun;import{createServer as uh}from"net";var bR=65535,Td=1;async function ph(e,t,r={}){let n={};for(let[i,s]of Object.entries(r))for(let a of Object.values(s))n[a]=i;let o={};for(let[i,s]of Object.entries(e.services)){if(t.length>0&&!t.includes(i))continue;let a={};for(let c of s.ports){let l=r[i]?.[c.name],d=l&&l>=Td?l:c.value;if(l===void 0&&c.auto)d=await wR();if(d<Td||d>bR)throw new Ve(Q,`invalid port ${d} on service ${i}`,{service:i});if(n[d]&&n[d]!==i)throw new Ve(Q,`duplicate port ${d} used by ${n[d]} and ${i}`,{service:i});if(l!==d&&!c.auto&&!await jo(d))throw await SR(i,c.name,d);n[d]=i,a[c.name]=d}o[i]=a}return o}function wR(){return new Promise((e,t)=>{let r=uh();r.listen(0,"127.0.0.1",()=>{let n=r.address();r.close(()=>{if(n&&typeof n==="object")e(n.port);else t(Pe(Q,"unable to allocate dynamic port",Error("no address")))})}),r.on("error",(n)=>t(Pe(Q,"unable to allocate dynamic port",n)))})}async function ya(e){let t=e.ports.filter((n)=>!n.auto&&n.value>=Td);if(t.length===0)return;let r={};for(let n of t){if(await jo(n.value))return;r[n.name]=n.value}return r}function jo(e){return new Promise((t)=>{let r=uh();r.listen(e,"127.0.0.1",()=>{r.close(()=>t(!0))}),r.on("error",()=>t(!1))})}async function SR(e,t,r){let n=await Yn(r);return kR(e,t,r,n)}function kR(e,t,r,n){let o=t===""?`${e} port`:`${e} ports.${t}`;if(n&&n.pid===process.pid)return Te(He,`${e} blocked: this TUI already holds port ${r}`,`Stop the proxy from the Proxy screen, or change ${o} in .devctl.`,e);if(n)return Te(He,`${e} blocked: ${n.command} (pid ${n.pid}) is using port ${r}`,`Open Doctor and press enter to free port ${r}, or change ${o} in .devctl.`,e);return Te(He,`${e} blocked: port ${r} is already in use`,`Something is listening on ${r}. Open Doctor to free it, or change ${o} in .devctl.`,e)}function CR(e,t){let r=e.split(`
2168
- `).filter((n)=>n.trim()!==""&&!n.startsWith("COMMAND"));for(let n of r){let o=n.trim().split(/\s+/),i=o[0]??"",s=Number(o[1]);if(i!==""&&Number.isInteger(s)&&s>0)return{port:t,pid:s,command:i}}return}async function Yn(e){if(process.platform==="win32")return ER(e);let t=await PR(e);if(t)return t;return RR(e)}async function PR(e){try{let t=Ad({cmd:["lsof","-nP",`-iTCP:${e}`,"-sTCP:LISTEN"],stdout:"pipe",stderr:"ignore"}),r=await new Response(t.stdout).text();return await t.exited,CR(r,e)}catch{return}}async function RR(e){let t;try{let o=Ad({cmd:["fuser","-n","tcp",String(e)],stdout:"pipe",stderr:"pipe"});t=`${await new Response(o.stdout).text()} ${await new Response(o.stderr).text()}`,await o.exited}catch{return}let r=t.match(/(\d+)/),n=r?Number(r[1]):0;if(!Number.isInteger(n)||n<=0)return;return{port:e,pid:n,command:"process"}}function _R(e,t){for(let r of e.split(`
2169
- `)){if(!r.includes("LISTENING")&&!r.includes("LISTEN"))continue;if(!r.includes(`:${t}`)&&!r.includes(` ${t} `))continue;let n=r.trim().match(/(\d+)\s*$/),o=n?Number(n[1]):0;if(Number.isInteger(o)&&o>0)return{port:t,pid:o,command:"process"}}return}async function ER(e){let t=Ad({cmd:["netstat","-ano"],stdout:"pipe",stderr:"ignore"}),r=t.stdout?await new Response(t.stdout).text():"";return await t.exited,_R(r,e)}var TR=400;async function xa(e){if(e.pid===process.pid)throw Te(He,`port ${e.port} is held by this TUI`,"stop the proxy from the proxy screen, or quit the TUI");try{process.kill(e.pid,"SIGTERM")}catch(t){throw Pe(He,`could not stop pid ${e.pid} on port ${e.port}`,t)}await AR(TR);try{process.kill(e.pid,0),process.kill(e.pid,"SIGKILL")}catch{return}}function AR(e){return new Promise((t)=>{setTimeout(t,e)})}Da();te();import{readdirSync as U_,readFileSync as W_}from"fs";import{basename as Cg,dirname as G_,extname as K_,join as to,resolve as q_}from"path";import{parse as V_}from"yaml";ht();function N(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}function Bd(e){return N(e)?new Set(Object.keys(e)):new Set}function G(e){if(typeof e==="string")return e;if(typeof e==="number"||typeof e==="boolean")return String(e);return""}function Re(e){if(typeof e==="number")return e;if(typeof e==="string"){let t=Number(e);return Number.isNaN(t)?0:t}return 0}function ot(e){return e===!0}function Rt(e){if(!Array.isArray(e))return[];return e.map((t)=>G(t))}function Ud(e){if(!Array.isArray(e))return[];return e.map((t)=>N(t)?{service:G(t.service),condition:G(t.condition)||"service_started"}:G(t))}function Sn(e){if(!N(e))return{};let t={};for(let[r,n]of Object.entries(e))t[r]=G(n);return t}function Pr(e){if(typeof e==="string")return{args:e.split(/\s+/).filter((t)=>t!==""),shell:!1};if(Array.isArray(e))return{args:e.map((t)=>G(t)),shell:!1};return Bo()}function Wd(e){if(e===void 0||e===null)return[];if(typeof e==="string"||typeof e==="number")return[jd(e,"http")];if(Array.isArray(e))return e.map((t,r)=>jd(t,`port_${r}`));if(N(e))return Object.entries(e).map(([t,r])=>jd(r,t));return[]}function jd(e,t){if(typeof e==="string"&&e.toLowerCase()==="auto")return{name:t,value:0,auto:!0};return{name:t,value:Re(e),auto:!1}}function kh(e){if(!N(e))return Id();let t={},r=[],n={};for(let[o,i]of Object.entries(e))if(o==="required")r=Rt(i);else if(o==="defaults")n=Sn(i);else t[o]=G(i);return{vars:t,required:r,defaults:n}}function jR(e){if(!N(e))return Od();return{type:G(e.type),mode:G(e.mode),service_account:G(e.service_account),config:N(e.config)?{...e.config}:{}}}function BR(e){if(!N(e))return Md();return{type:G(e.type),url:G(e.url),address:G(e.address),command:Pr(e.command),interval_seconds:Re(e.interval_seconds),timeout_seconds:Re(e.timeout_seconds),start_period_seconds:Re(e.start_period_seconds),unhealthy_threshold:e.unhealthy_threshold===void 0?3:Re(e.unhealthy_threshold),healthy_reset_threshold:e.healthy_reset_threshold===void 0?10:Re(e.healthy_reset_threshold)}}function UR(e){if(!N(e))return{policy:"",max_retries:0,backoff_seconds:0};return{enabled:e.enabled===void 0?void 0:ot(e.enabled),policy:G(e.policy),max_retries:Re(e.max_retries),backoff_seconds:Re(e.backoff_seconds)}}function WR(e){if(!N(e))return{wait_for_healthy:!1,timeout_seconds:0};return{wait_for_healthy:ot(e.wait_for_healthy),timeout_seconds:Re(e.timeout_seconds)}}function Ni(e){if(!N(e))return vn();let t=N(e.logs)?e.logs:{};return{extends:G(e.extends),description:G(e.description),command:Pr(e.command),shell:ot(e.shell),working_dir:G(e.working_dir),dependencies:Ud(e.dependencies),ports:Wd(e.ports),environment:kh(e.environment),health:BR(e.health),identity:jR(e.identity),logs:{stdout:ot(t.stdout),stderr:ot(t.stderr)},restart:UR(e.restart),startup:WR(e.startup),capabilities:Rt(e.capabilities),proxy:qd(e.proxy),expose:Kd(e.expose),container:Gd(e.container),watch:GR(e.watch),hooks:KR(e.hooks)}}function GR(e){if(!N(e))return Ld();return{enabled:ot(e.enabled),paths:Rt(e.paths),debounce_ms:ka(Re(e.debounce_ms)),ignore:e.ignore===void 0?[...Mi]:Rt(e.ignore)}}function KR(e){let t=N(e)?e:{};return{pre_start:Pr(t.pre_start),post_start:Pr(t.post_start)}}function Ch(e){let t=N(e)?e:{};return{command:Pr(t.command),shell:ot(t.shell),working_dir:G(t.working_dir),dependencies:Rt(t.dependencies),environment:kh(t.environment)}}function Gd(e){if(!N(e))return;let t={};if(N(e.ports))for(let[r,n]of Object.entries(e.ports))t[r]=Re(n);return{image:G(e.image),runtime:G(e.runtime),ports:t,env:Sn(e.env),volumes:Rt(e.volumes)}}function Kd(e){if(e===!0)return{enabled:!0,host:"",port:""};if(e===!1)return{enabled:!1,host:"",port:""};if(N(e))return{enabled:e.enabled!==void 0?ot(e.enabled):!0,host:G(e.host),port:G(e.port)};return Dd()}function qd(e){if(e===void 0||e===null)return[];if(Array.isArray(e))return e.map((t)=>Qn(t));if(N(e))return[Qn(e)];return[]}function Ia(e){if(!N(e))return{services:[],environment:{}};return{services:Rt(e.services),environment:Sn(e.environment)}}function qR(e){if(typeof e==="string")return{type:e,service_account:""};if(!N(e))return{type:"",service_account:""};return{type:G(e.type),service_account:G(e.service_account)}}function VR(e){if(!N(e))return Oi();return{type:G(e.type),identity:qR(e.identity),audience:G(e.audience),service_account:G(e.service_account),client_id:G(e.client_id),client_secret:G(e.client_secret)}}function Qn(e){if(!N(e))return{name:"",match:{host:"",path:""},upstream:{url:""},auth:Oi()};let t=N(e.match)?e.match:{},r=N(e.upstream)?e.upstream:{};return{name:G(e.name),match:{host:G(t.host),path:G(t.path)},upstream:{url:G(r.url),service:G(r.service),port:G(r.port)},auth:VR(e.auth)}}te();import{existsSync as zR,statSync as Rh}from"fs";import{basename as Ph,dirname as Ma,isAbsolute as YR,join as Oa,resolve as _h}from"path";function Zn(e,t){if(t!=="")return JR(t);let r=e===""?process.cwd():e;r=YR(r)?r:_h(r);for(;;){let n=Oa(r,Nt,lr);if(Hi(n))return{repoRoot:r,configPath:n};let o=Oa(r,"devctl.yaml");if(Hi(o))return{repoRoot:r,configPath:o};let i=Ma(r);if(i===r)break;r=i}throw Te(Vn,"no devctl configuration found","run `devctl setup` or create a .devctl/config.yaml in the repository root")}function JR(e){let t=_h(e),r;try{r=Rh(t)}catch(o){throw Pe(Vn,"config path not found",o)}if(r.isDirectory()){if(Ph(t)===Nt)return{repoRoot:Ma(t),configPath:Oa(t,lr)};return{repoRoot:t,configPath:Oa(t,lr)}}let n=Ma(t);if(Ph(n)===Nt)n=Ma(n);return{repoRoot:n,configPath:t}}function Hi(e){return zR(e)&&!Rh(e).isDirectory()}ht();function Na(){return{services:{},templates:{},provenance:{}}}function rn(e,t,r,n,o=""){if(N(t)&&Object.keys(t).length>0){for(let[i,s]of Object.entries(t))rn(e,s,r,n,o===""?i:`${o}.${i}`);return}if(o==="")return;(e[o]??=[]).push({source:r,layer:n})}var XR=["environment","restart","startup","health","logs","identity","container","watch","hooks"];function $a(e,t,r){let n=Bd(r);if(N(r))for(let i of XR){let s=r[i];if(N(s))for(let a of Object.keys(s))n.add(`${i}.${a}`)}let o=e[t];e[t]=o?new Set([...o,...n]):n}function Eh(e,t,r=Na(),n={source:"unknown",layer:"unknown"}){if(rn(r.provenance,t,n.source,n.layer),t.version!==void 0)e.version=Re(t.version);if(N(t.project)&&t.project.name!==void 0)e.project.name=G(t.project.name);if(N(t.google)){if(t.google.project_id!==void 0)e.google.project_id=G(t.google.project_id);if(t.google.region!==void 0)e.google.region=G(t.google.region)}if(N(t.profiles))for(let[o,i]of Object.entries(t.profiles)){let s=e.profiles[o];e.profiles[o]=s?ZR(s,i):Ia(i)}if(N(t.templates))for(let[o,i]of Object.entries(t.templates)){let s=e.templates[o];e.templates[o]=s?Fa(s,i):Ni(i),$a(r.templates,o,i)}if(N(t.services))for(let[o,i]of Object.entries(t.services)){let s=e.services[o];e.services[o]=s?Fa(s,i):Ni(i),$a(r.services,o,i)}if(N(t.tasks))for(let[o,i]of Object.entries(t.tasks))e.tasks[o]=e.tasks[o]?e_(e.tasks[o],i):Ch(i);if(N(t.proxy))QR(e.proxy,t.proxy);if(N(t.logs)){if(t.logs.max_memory_events!==void 0)e.logs.max_memory_events=Re(t.logs.max_memory_events);if(N(t.logs.persistence)){let o=t.logs.persistence;if(o.enabled!==void 0)e.logs.persistence.enabled=ot(o.enabled);if(o.directory!==void 0)e.logs.persistence.directory=G(o.directory);if(o.retention_days!==void 0)e.logs.persistence.retention_days=Re(o.retention_days);if(o.max_session_logs!==void 0)e.logs.persistence.max_session_logs=Re(o.max_session_logs)}}if(N(t.auth)&&t.auth.refresh_threshold_seconds!==void 0)e.auth.refresh_threshold_seconds=Re(t.auth.refresh_threshold_seconds);if(N(t.shutdown)){if(t.shutdown.stop_services_on_exit!==void 0)e.shutdown.stop_services_on_exit=ot(t.shutdown.stop_services_on_exit);if(t.shutdown.grace_seconds!==void 0)e.shutdown.grace_seconds=Re(t.shutdown.grace_seconds)}if(N(t.ui)){if(t.ui.theme!==void 0)e.ui.theme=G(t.ui.theme);if(t.ui.keymap!==void 0)e.ui.keymap=Sn(t.ui.keymap)}if(N(t.secrets)){if(t.secrets.extra_markers!==void 0)e.secrets.extra_markers=Rt(t.secrets.extra_markers);if(t.secrets.extra_patterns!==void 0)e.secrets.extra_patterns=Rt(t.secrets.extra_patterns)}if(N(t.doctor)&&Array.isArray(t.doctor.tools))e.doctor.tools=t.doctor.tools.filter(N).map((o)=>({name:G(o.name),command:G(o.command)}));if(Array.isArray(t.plugins))e.plugins=t.plugins.filter(N).map((o)=>({path:G(o.path)})).filter((o)=>o.path!=="");if(N(t.environment)){if(Array.isArray(t.environment.sources))e.environment.sources=Rt(t.environment.sources);if(N(t.environment.secrets))e.environment.secrets={...e.environment.secrets,...Sn(t.environment.secrets)}}}function QR(e,t){if(t.enabled!==void 0)e.enabled=ot(t.enabled);if(t.gateway!==void 0)e.gateway=ot(t.gateway);if(N(t.listen)){if(t.listen.host!==void 0)e.listen.host=G(t.listen.host);if(t.listen.port!==void 0)e.listen.port=Re(t.listen.port)}if(N(t.token_endpoint)){let r=t.token_endpoint;if(r.enabled!==void 0)e.token_endpoint.enabled=ot(r.enabled);if(r.host!==void 0)e.token_endpoint.host=G(r.host);if(r.port!==void 0)e.token_endpoint.port=Re(r.port)}if(Array.isArray(t.routes))e.routes=t.routes.map((r)=>Qn(r))}function ZR(e,t){if(!N(t))return e;return{services:t.services!==void 0?Rt(t.services):e.services,environment:t.environment!==void 0?{...e.environment,...Sn(t.environment)}:e.environment}}function e_(e,t){if(!N(t))return e;return{command:t.command!==void 0?Pr(t.command):e.command,shell:t.shell!==void 0?ot(t.shell):e.shell,working_dir:t.working_dir!==void 0?G(t.working_dir):e.working_dir,dependencies:t.dependencies!==void 0?Rt(t.dependencies):e.dependencies,environment:Th(e.environment,t.environment)}}function Fa(e,t){if(!N(t))return e;let r=Bd(t),n={...e,environment:Th(e.environment,t.environment),health:n_(e.health,t.health),identity:o_(e.identity,t.identity),logs:i_(e.logs,t.logs),restart:s_(e.restart,t.restart),startup:a_(e.startup,t.startup),container:c_(e.container,t.container),watch:t_(e.watch,t.watch),hooks:r_(e.hooks,t.hooks)};if(r.has("extends"))n.extends=G(t.extends);if(r.has("description"))n.description=G(t.description);if(r.has("command"))n.command=Pr(t.command);if(r.has("shell"))n.shell=ot(t.shell);if(r.has("working_dir"))n.working_dir=G(t.working_dir);if(r.has("dependencies"))n.dependencies=Ud(t.dependencies);if(r.has("ports"))n.ports=Wd(t.ports);if(r.has("capabilities"))n.capabilities=Rt(t.capabilities);if(r.has("proxy"))n.proxy=qd(t.proxy);if(r.has("expose"))n.expose=Kd(t.expose);return n}function t_(e,t){if(!N(t))return e;return{enabled:t.enabled!==void 0?ot(t.enabled):e.enabled,paths:t.paths!==void 0?Rt(t.paths):e.paths,debounce_ms:t.debounce_ms!==void 0?ka(Re(t.debounce_ms)):e.debounce_ms,ignore:t.ignore!==void 0?Rt(t.ignore):e.ignore}}function r_(e,t){if(!N(t))return e;return{pre_start:t.pre_start!==void 0?Pr(t.pre_start):e.pre_start,post_start:t.post_start!==void 0?Pr(t.post_start):e.post_start}}function Th(e,t){if(!N(t))return e;let r={...e.vars},n={...e.defaults},o=e.required;for(let[i,s]of Object.entries(t))if(i==="required")o=Rt(s);else if(i==="defaults")Object.assign(n,Sn(s));else r[i]=G(s);return{vars:r,required:o,defaults:n}}function n_(e,t){if(!N(t))return e;return{type:t.type!==void 0?G(t.type):e.type,url:t.url!==void 0?G(t.url):e.url,address:t.address!==void 0?G(t.address):e.address,command:t.command!==void 0?Pr(t.command):e.command,interval_seconds:t.interval_seconds!==void 0?Re(t.interval_seconds):e.interval_seconds,timeout_seconds:t.timeout_seconds!==void 0?Re(t.timeout_seconds):e.timeout_seconds,start_period_seconds:t.start_period_seconds!==void 0?Re(t.start_period_seconds):e.start_period_seconds,unhealthy_threshold:t.unhealthy_threshold!==void 0?Re(t.unhealthy_threshold):e.unhealthy_threshold,healthy_reset_threshold:t.healthy_reset_threshold!==void 0?Re(t.healthy_reset_threshold):e.healthy_reset_threshold}}function o_(e,t){if(!N(t))return e;return{type:t.type!==void 0?G(t.type):e.type,mode:t.mode!==void 0?G(t.mode):e.mode,service_account:t.service_account!==void 0?G(t.service_account):e.service_account,config:t.config!==void 0&&N(t.config)?{...e.config??{},...t.config}:e.config}}function i_(e,t){if(!N(t))return e;return{stdout:t.stdout!==void 0?ot(t.stdout):e.stdout,stderr:t.stderr!==void 0?ot(t.stderr):e.stderr}}function s_(e,t){if(!N(t))return e;return{enabled:t.enabled!==void 0?ot(t.enabled):e.enabled,policy:t.policy!==void 0?G(t.policy):e.policy,max_retries:t.max_retries!==void 0?Re(t.max_retries):e.max_retries,backoff_seconds:t.backoff_seconds!==void 0?Re(t.backoff_seconds):e.backoff_seconds}}function a_(e,t){if(!N(t))return e;return{wait_for_healthy:t.wait_for_healthy!==void 0?ot(t.wait_for_healthy):e.wait_for_healthy,timeout_seconds:t.timeout_seconds!==void 0?Re(t.timeout_seconds):e.timeout_seconds}}function c_(e,t){if(!N(t))return e;let r=Gd(t);if(!r)return e;return{image:t.image!==void 0?r.image:e?.image??"",runtime:t.runtime!==void 0?r.runtime:e?.runtime??"",ports:t.ports!==void 0?{...e?.ports??{},...r.ports}:e?.ports??{},env:t.env!==void 0?{...e?.env??{},...r.env}:e?.env??{},volumes:t.volumes!==void 0?r.volumes:e?.volumes??[]}}function Ah(e,t){let r=Object.keys(e.services).sort();for(let n of r){let o=e.services[n]?.proxy??[];if(o.length===0)continue;o.forEach((i,s)=>{let c={name:o.length===1?n:`${n}-${s+1}`,match:i.match,upstream:i.upstream,auth:i.auth},l=e.proxy.routes.length;if(e.proxy.routes.push(c),t)rn(t,c,`synthesized from services.${n}.proxy`,"synthesized",`proxy.routes.${l}`)})}l_(e,t)}function l_(e,t){if(!e.proxy.enabled)return;let r=new Set(e.proxy.routes.map((n)=>n.name));for(let n of Object.keys(e.services).sort()){let o=e.services[n];if(!o)continue;if(!(o.expose.enabled!==!1&&(o.expose.enabled===!0||e.proxy.gateway&&Xn(o.ports,"http")!==void 0))||r.has(n))continue;let a={name:n,match:{host:o.expose.host||`${n}.local`,path:""},upstream:{url:"",service:n,port:o.expose.port||"http"},auth:Oi()},c=e.proxy.routes.length;if(e.proxy.routes.push(a),r.add(n),t){let l=o.expose.enabled===!0?`services.${n}.expose`:"proxy.gateway";rn(t,a,`synthesized from ${l}`,"synthesized",`proxy.routes.${c}`)}}}function Lh(e,t=Na()){if(Object.keys(e.templates).length===0)return;let r={};for(let[n,o]of Object.entries(e.services))r[n]=Dh(e,o,t.services[n]??new Set,t,{});e.services=r}function Dh(e,t,r,n,o){if(t.extends==="")return t;if(o[t.extends])throw Error(`template cycle involving "${t.extends}"`);let i=e.templates[t.extends]??vn();if(!e.templates[t.extends])throw Error(`service extends unknown template "${t.extends}"`);let s={...o,[t.extends]:!0},a=n.templates[t.extends]??new Set,c=Dh(e,i,a,n,s),l=d_(c,t,r);return l.extends=t.extends,l}function d_(e,t,r){let n={...e};if(r.has("extends"))n.extends=t.extends;if(r.has("description"))n.description=t.description;if(r.has("command"))n.command=t.command;if(r.has("shell"))n.shell=t.shell;if(r.has("working_dir"))n.working_dir=t.working_dir;if(r.has("dependencies"))n.dependencies=t.dependencies;if(r.has("ports"))n.ports=t.ports;if(r.has("capabilities"))n.capabilities=t.capabilities;if(r.has("proxy"))n.proxy=t.proxy;if(r.has("container")){let o=t.container,i=e.container;n.container=o&&i?{image:r.has("container.image")?o.image:i.image,runtime:r.has("container.runtime")?o.runtime:i.runtime,ports:r.has("container.ports")?o.ports:i.ports,env:r.has("container.env")?o.env:i.env,volumes:r.has("container.volumes")?o.volumes:i.volumes}:o??i}if(r.has("watch"))n.watch=t.watch;return n.hooks={pre_start:r.has("hooks.pre_start")?t.hooks.pre_start:e.hooks.pre_start,post_start:r.has("hooks.post_start")?t.hooks.post_start:e.hooks.post_start},n.health={type:r.has("health.type")?t.health.type:e.health.type,url:r.has("health.url")?t.health.url:e.health.url,address:r.has("health.address")?t.health.address:e.health.address,command:r.has("health.command")?t.health.command:e.health.command,interval_seconds:r.has("health.interval_seconds")?t.health.interval_seconds:e.health.interval_seconds,timeout_seconds:r.has("health.timeout_seconds")?t.health.timeout_seconds:e.health.timeout_seconds,start_period_seconds:r.has("health.start_period_seconds")?t.health.start_period_seconds:e.health.start_period_seconds,unhealthy_threshold:r.has("health.unhealthy_threshold")?t.health.unhealthy_threshold:e.health.unhealthy_threshold,healthy_reset_threshold:r.has("health.healthy_reset_threshold")?t.health.healthy_reset_threshold:e.health.healthy_reset_threshold},n.identity={type:r.has("identity.type")?t.identity.type:e.identity.type,mode:r.has("identity.mode")?t.identity.mode:e.identity.mode,service_account:r.has("identity.service_account")?t.identity.service_account:e.identity.service_account,config:r.has("identity.config")?t.identity.config:e.identity.config},n.logs={stdout:r.has("logs.stdout")?t.logs.stdout:e.logs.stdout,stderr:r.has("logs.stderr")?t.logs.stderr:e.logs.stderr},n.restart={enabled:r.has("restart.enabled")?t.restart.enabled:e.restart.enabled,policy:r.has("restart.policy")?t.restart.policy:e.restart.policy,max_retries:r.has("restart.max_retries")?t.restart.max_retries:e.restart.max_retries,backoff_seconds:r.has("restart.backoff_seconds")?t.restart.backoff_seconds:e.restart.backoff_seconds},n.startup={wait_for_healthy:r.has("startup.wait_for_healthy")?t.startup.wait_for_healthy:e.startup.wait_for_healthy,timeout_seconds:r.has("startup.timeout_seconds")?t.startup.timeout_seconds:e.startup.timeout_seconds},n.environment={vars:{...e.environment.vars,...t.environment.vars},defaults:{...e.environment.defaults,...t.environment.defaults},required:r.has("environment.required")?t.environment.required:e.environment.required},n}te();ht();function Ih(e){if(e.version===0)throw I(Q,"version is required");if(e.version===Uo)return e;throw I(Q,`unsupported config version ${e.version} (expected ${Uo}); no migration is available`)}var Mh=["version","project","google","profiles","templates","services","tasks","proxy","logs","auth","shutdown","ui","secrets","doctor","plugins","environment"],Oh=["extends","description","command","shell","working_dir","dependencies","ports","environment","health","identity","logs","restart","startup","capabilities","proxy","expose","container","watch","hooks"],$h=["type","url","address","command","interval_seconds","timeout_seconds","start_period_seconds","unhealthy_threshold","healthy_reset_threshold"],Fh=["service","condition"],Nh=["type","mode","service_account","config"],Hh=["enabled","policy","max_retries","backoff_seconds"],jh=["wait_for_healthy","timeout_seconds"],Bh=["enabled","host","port"],Uh=["enabled","gateway","listen","token_endpoint","routes"],Wh=["host","port"],Vd=["name","match","upstream","auth"],zd=["host","path"],Yd=["url","service","port"],Jd=["type","identity","audience","service_account","client_id","client_secret"],Gh=["max_memory_events","persistence"],Kh=["enabled","directory","retention_days","max_session_logs"],qh=["refresh_threshold_seconds"],Vh=["stop_services_on_exit","grace_seconds"],zh=["theme","keymap"],Yh=["name"],Jh=["project_id","region"],Xh=["services","environment"],Xd=["required","defaults"],Qh=["stdout","stderr"],Zh=["extra_markers","extra_patterns"],eg=["tools"],tg=["name","command"],rg=["enabled","host","port"],ng=["path"],og=["sources","secrets"],ig=["image","runtime","ports","env","volumes"],sg=["enabled","paths","debounce_ms","ignore"],ag=["pre_start","post_start"],cg=["command","shell","working_dir","dependencies","environment"],lg=["google","google_api","iap","network","service_identity","local_http"],dg=["|","||","&&",";",">",">>","<","&"];var u_=2;function kn(e,t){if(Array.isArray(e))return e.flatMap((o,i)=>kn(o,ug(t,String(i))));if(!y_(e))return[];let r=f_(t),n=[];for(let o of Object.keys(e)){let i=ug(t,o);if(r.length>0&&!r.includes(o)&&!p_(t))n.push(i);n.push(...kn(e[o],i))}return n}function p_(e){if(e==="services"||e==="profiles"||e==="templates"||e==="tasks")return!0;if(e.endsWith(".environment")||e.endsWith(".defaults")||e.endsWith(".keymap"))return!0;if(e.endsWith(".container.ports")||e.endsWith(".container.env"))return!0;if(e.endsWith(".identity.config")||e.includes(".identity.config."))return!0;return e.includes(".environment.")&&!e.endsWith(".environment")}function f_(e){switch(e){case"":return Mh;case"project":return Yh;case"google":return Jh;case"proxy":return Uh;case"proxy.listen":return Wh;case"proxy.token_endpoint":return rg;case"logs":return Gh;case"logs.persistence":return Kh;case"auth":return qh;case"shutdown":return Vh;case"ui":return zh;case"secrets":return Zh;case"doctor":return eg;case"environment":return og;default:return m_(e)}}function m_(e){if(e==="services"||e==="profiles"||e==="templates"||e==="tasks")return[];if(e.startsWith("services.")||e.startsWith("templates.")){if(e.includes(".dependencies."))return Fh;return h_(e)}if(e.startsWith("profiles.")){if(e.split(".").length===2)return Xh}if(e.startsWith("tasks.")){let t=e.split(".");if(t.length===2)return cg;if(t[2]==="environment")return Xd}if(e.includes("routes"))return v_(e);if(e.startsWith("doctor.tools")&&e.split(".").length>=3)return tg;if(e==="plugins")return[];if(e.startsWith("plugins.")&&e.split(".").length===2)return ng;return[]}function h_(e){let t=e.split(".");if(t.length===2)return Oh;if(t.length>=3)switch(t[2]){case"health":return $h;case"identity":return Nh;case"restart":return Hh;case"startup":return jh;case"logs":return Qh;case"environment":return Xd;case"proxy":return g_(t);case"expose":return Bh;case"container":return ig;case"watch":return sg;case"hooks":return ag;default:return[]}return[]}function g_(e){let t=e.slice(3),r=t[0]!==void 0&&/^\d+$/.test(t[0])?1:0,n=t[r]??"";if(n==="match")return zd;if(n==="upstream")return Yd;if(n==="auth")return Jd;return Vd}function v_(e){if(e==="proxy.routes")return[];if(e.endsWith(".match"))return zd;if(e.endsWith(".upstream"))return Yd;if(e.endsWith(".auth"))return Jd;if(e.includes("proxy.routes")&&e.split(".").length===u_+1)return Vd;return[]}function ug(e,t){if(e==="")return t;return`${e}.${t}`}function y_(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}function Wo(e){return`unknown fields: ${e.join(", ")}`}ht();function Cn(e){let t=e.trim().toLowerCase();if(t==="")return!0;if(t==="localhost"||t==="::1")return!0;if(pg(t))return!0;return x_(t)}function pg(e){let t=e.split(".");if(t.length!==4)return!1;let r=t.map((n)=>Number(n));if(r.some((n)=>!Number.isInteger(n)||n<0||n>255))return!1;return r[0]===127}function x_(e){if(!e.startsWith("::ffff:"))return!1;return pg(e.slice(7))}import{existsSync as S_}from"fs";import{isAbsolute as k_,resolve as C_}from"path";import{fileURLToPath as P_}from"url";ht();function fg(e,t){if(t){if(t.http!==void 0)return t.http;let n=Object.values(t)[0];if(n!==void 0)return n}let r=xh(e.ports);if(r&&!r.auto)return r.value;return}function b_(e,t,r){let n=e,o="";for(;;){let i=n.indexOf("${");if(i<0)return o+n;o+=n.slice(0,i);let s=n.slice(i).indexOf("}");if(s<0)throw Error(`unclosed environment reference in "${e}"`);let a=n.slice(i+2,i+s);o+=w_(a,t,r),n=n.slice(i+s+1)}}function w_(e,t,r){let n=e.split(".");if(n.length<3||n[0]!=="services")throw Error(`unsupported reference \${${e}}`);let o=n[1]??"",i=t.services[o];if(!i)throw Error(`reference \${${e}}: unknown service`);let s=r[o];if(n[2]==="host"||n[2]==="url"){let a=t.proxy.enabled?t.proxy.routes.find((l)=>l.upstream.service===o):void 0;if(n[2]==="host")return a?a.match.host||"127.0.0.1":"127.0.0.1";if(a)return`http://${a.match.host||"127.0.0.1"}:${t.proxy.listen.port}${a.match.path}`;let c=fg(i,s);if(c===void 0)throw Error(`unresolvable reference \${${e}}`);return`http://127.0.0.1:${c}`}if(n[2]==="port"){let a=fg(i,s);if(a!==void 0)return String(a)}if(n[2]==="ports"){let a=n[3];if(!a)throw Error(`reference \${${e}}: missing port name`);if(s&&s[a]!==void 0)return String(s[a]);let c=Xn(i.ports,a);if(c&&!c.auto)return String(c.value);let l=Number.parseInt(a,10);if(!Number.isNaN(l)&&l>=0&&l<i.ports.length){let d=i.ports[l];if(d&&!d.auto)return String(d.value)}}throw Error(`unresolvable reference \${${e}}`)}function Qd(e,t,r){let n={};for(let[o,i]of Object.entries(e))n[o]=b_(i,t,r);return n}function mg(e){let t=[],r=e;for(;;){let n=r.indexOf("${");if(n<0)return t;let o=r.slice(n).indexOf("}");if(o<0)return t;t.push(r.slice(n+2,n+o)),r=r.slice(n+o+1)}}function hg(e,t){let r=e.split(".");if(r.length<2)return!1;if(r[0]!=="services"||r.length<3)return!1;let n=t.services[r[1]??""];if(!n)return!1;if(r[2]==="port"||r[2]==="ports"){if(r.length===3)return n.ports.length>0;if(r.length>=4){let o=r[3]??"";if(!Number.isNaN(Number.parseInt(o,10)))return!0;return Xn(n.ports,o)!==void 0}}return!0}ht();var Ba=65535,Ua=1,wg=["http","tcp","process","command"];function eu(e){let t=[];for(let[r,n]of Object.entries(e.services)){let o=n.health.type;if(o!==""&&!wg.includes(o.toLowerCase()))t.push({service:r,type:o})}return t}var R_=0,xg=1,bg=2;function eo(e){let t=[];if(e.version===0)t.push("version is required");else if(e.version!==Uo)t.push(`unsupported config version ${e.version} (expected ${Uo}); no migration is available`);if(Object.keys(e.services).length===0)t.push("at least one service must be defined");t.push(...E_(e)),t.push(...__(e)),t.push(...D_(e)),t.push(...I_(e)),t.push(...M_(e));for(let[r,n]of e.plugins.entries())if(n.path==="")t.push(`plugins.${r}.path is required`);else try{let o=n.path.startsWith("file:")?P_(n.path):k_(n.path)?n.path:C_(e.repoRoot,n.path);if(!S_(o))t.push(`plugins.${r}.path does not exist: ${n.path}`)}catch{t.push(`plugins.${r}.path is invalid: ${n.path}`)}if(e.logs.max_memory_events<0)t.push("logs.max_memory_events must be >= 0");return t}function __(e){let t=[];for(let[r,n]of Object.entries(e.tasks)){let o=`tasks.${r}`;if(yn(n.command))t.push(`${o}.command is required`);t.push(...ja(o,n.command,n.shell));for(let i of n.dependencies)if(!e.services[i])t.push(`${o}.dependencies: unknown service "${i}"`);t.push(...Sg(`${o}.environment`,n.environment,e))}return t}function E_(e){let t=[],r={};for(let[n,o]of Object.entries(e.services)){let i=`services.${n}`;if(yn(o.command)&&!o.container)t.push(`${i}.command is required`);t.push(...ja(i,o.command,o.shell)),t.push(...ja(`${i}.hooks.pre_start`,o.hooks.pre_start,o.shell)),t.push(...ja(`${i}.hooks.post_start`,o.hooks.post_start,o.shell)),t.push(...H_(i,o.capabilities));for(let c of o.dependencies){let l=zt(c);if(!e.services[l])t.push(`${i}.dependencies: unknown service "${l}"`);if(l===n)t.push(`${i}.dependencies: service cannot depend on itself`);if(!["service_started","service_healthy"].includes(gn(c)))t.push(`${i}.dependencies: condition must be service_started or service_healthy`);if(gn(c)==="service_healthy"&&e.services[l]?.health.type==="")t.push(`${i}.dependencies: service_healthy requires ${l} to define a health check`)}if(o.extends!==""&&!e.templates[o.extends])t.push(`${i}.extends: unknown template "${o.extends}"`);for(let c of o.ports.filter((l)=>!l.auto))if(c.value<Ua||c.value>Ba)t.push(`${i}.ports.${c.name}: invalid port ${c.value}`);else if(r[c.value])t.push(`duplicate port ${c.value} used by ${r[c.value]} and ${n}`);else r[c.value]=n;let s=L_(`${i}.identity`,o.identity,e.plugins.length>0);if(s!=="")t.push(s);if(t.push(...T_(i,o,e.plugins.length>0)),o.health.start_period_seconds<0)t.push(`${i}.health.start_period_seconds must be >= 0`);if(o.health.unhealthy_threshold<1)t.push(`${i}.health.unhealthy_threshold must be >= 1`);if(o.health.healthy_reset_threshold<1)t.push(`${i}.health.healthy_reset_threshold must be >= 1`);let a=Ra(o.restart);if(a!==ba&&a!==wa&&a!==Sa)t.push(`${i}.restart.policy must be never, on_failure, or always`);if(t.push(...Sg(`${i}.environment`,o.environment,e)),o.container){if(o.container.image==="")t.push(`${i}.container.image is required`);if(o.container.runtime!==""&&o.container.runtime!=="docker"&&o.container.runtime!=="podman")t.push(`${i}.container.runtime must be docker or podman`);for(let[c,l]of Object.entries(o.container.ports)){if(!o.ports.some((d)=>d.name===c))t.push(`${i}.container.ports.${c}: no matching service port`);if(l<Ua||l>Ba)t.push(`${i}.container.ports.${c}: invalid container port ${l}`)}}}return t}function T_(e,t,r){let n=[];if(t.health.type==="")return n;let o=t.health.type.toLowerCase();if(!wg.includes(o)&&!r)n.push(`${e}.health.type must be http, tcp, process, or command`);if(o==="http"&&t.health.url==="")n.push(`${e}.health.url is required for http health checks`);if(o==="tcp"&&t.health.address===""&&t.ports.length===0)n.push(`${e}.health.address is required for tcp health checks without ports`);if(o==="command"&&yn({args:t.health.command.args,shell:!1}))n.push(`${e}.health.command is required for command health checks`);return n}var A_=["","user","service","service_account"];function L_(e,t,r){let n=Fi(t).toLowerCase();if(n===""||n==="user")return"";if(n==="service"||n==="service_account"){if(t.service_account==="")return`${e}.service_account is required for service identity`;if(!t.service_account.includes("@"))return`${e}.service_account must be an email`;return""}if(r)return"";return`${e}.type must be user or service_account`}function tu(e){let t=[];for(let[r,n]of Object.entries(e.services)){let o=Fi(n.identity).toLowerCase();if(!A_.includes(o))t.push({service:r,type:o})}return t}function D_(e){let t={},r=[],n=[],o=(i)=>{let s=t[i]??R_;if(s===bg)return;if(s===xg){r.push(`dependency cycle: ${[...n,i].join(" \u2192 ")}`);return}t[i]=xg,n.push(i);let a=e.services[i];if(a)for(let c of a.dependencies){let l=zt(c);if(e.services[l])o(l)}n.pop(),t[i]=bg};for(let i of Object.keys(e.services))o(i);return r}function Sg(e,t,r){let n=[],o=(i,s)=>{for(let a of mg(s))if(!hg(a,r))n.push(`${e}.${i}: unresolvable reference \${${a}}`)};for(let[i,s]of Object.entries(t.vars))o(i,s);for(let[i,s]of Object.entries(t.defaults))o(i,s);return n}function I_(e){let t=[];for(let[r,n]of Object.entries(e.profiles))for(let o of n.services)if(!e.services[o])t.push(`profiles.${r} references unknown service "${o}"`);return t}function M_(e){let t=[];if(e.proxy.listen.host!==""){if(!j_(e.proxy.listen.host))t.push("proxy.listen.host must be an IP address or localhost");else if(!Cn(e.proxy.listen.host))t.push("proxy.listen.host must be a loopback address")}if(e.proxy.enabled&&e.proxy.listen.port===0)t.push("proxy.listen.port is required when proxy.enabled is true");if(e.proxy.listen.port!==0&&(e.proxy.listen.port<Ua||e.proxy.listen.port>Ba))t.push("proxy.listen.port is invalid");let r={};return e.proxy.routes.forEach((n,o)=>{let i=`proxy.routes[${o}]`;if(n.name==="")t.push(`${i}.name is required`);else if(r[n.name])t.push(`${i}: duplicate route name ${n.name}`);r[n.name]=!0,t.push(...O_(n,i,e)),t.push(...$_(n,i))}),t.push(...N_(e)),t}function O_(e,t,r){let n=[],o=e.upstream.url!=="",i=e.upstream.service??"";if(i===""){if(!o)n.push(`${t}.upstream requires either url or service`);return n}let s=r.services[i];if(!s)return n.push(`${t}.upstream.service references unknown service ${i}`),n;let a=e.upstream.port||"http";if(Xn(s.ports,a)===void 0)n.push(`${t}.upstream: service ${i} has no port named ${a}`);return n}function $_(e,t){let r=[];if(e.auth.type.toLowerCase()==="iap"){if(e.auth.audience.trim()==="")r.push(`${t}.auth.audience is required when auth.type is iap`);if(e.auth.identity.type.trim()==="")r.push(`${t}.auth.identity.type is required when auth.type is iap`)}r.push(...F_(e.auth,t));let n=e.auth.identity.type.toLowerCase();if(n==="service"||n==="service_account"||en({type:n,mode:"",service_account:""})){if((e.auth.identity.service_account||e.auth.service_account)==="")r.push(`${t}.auth.identity.service_account is required`)}return r}function F_(e,t){let r=(e.client_id??"").trim(),n=(e.client_secret??"").trim();if(r===""&&n==="")return[];if(e.type.toLowerCase()!=="iap")return[`${t}.auth.client_id is only valid when auth.type is iap`];let o=[];if(r==="")return o.push(`${t}.auth.client_id is required when client_secret is set`),o;if(n==="")o.push(`${t}.auth.client_secret is required when client_id is set`);else if(Zd(n).length>0&&!yg(n))o.push(`${t}.auth.client_secret must be a literal or a single \${NAME} / \${env.NAME} reference`);let i=e.identity.type.toLowerCase();if(i==="service"||i==="service_account")o.push(`${t}.auth.client_id is only valid with identity.type user`);return o}function N_(e){let t=[],r=e.proxy.token_endpoint;if(!r.enabled)return t;let n=r.host||Ca;if(!Cn(n))t.push("proxy.token_endpoint.host must be a loopback address");if(r.port!==0&&(r.port<Ua||r.port>Ba))t.push("proxy.token_endpoint.port is invalid");return t}function ja(e,t,r){if(t.shell||r)return[];for(let n of t.args)if(dg.includes(n)||n.includes("|")||n.includes(";")||n.includes("&&"))return[`${e}.command contains shell metacharacters; set shell: true to run via a shell`];return[]}function H_(e,t){let r=[];for(let n of t)if(!lg.includes(n))r.push(`${e}.capabilities: unknown capability "${n}"`);return r}function j_(e){if(e==="localhost")return!0;return B_(e)}function B_(e){if(/^(\d{1,3}\.){3}\d{1,3}$/.test(e))return e.split(".").every((r)=>{let n=Number(r);return n>=0&&n<=255});return e.includes(":")}function ji(e,t,r){try{return Bi(e,t,{candidateText:r}),[]}catch(n){if(n instanceof Ve){let o=`${n.kind}: `;return[n.message.startsWith(o)?n.message.slice(o.length):n.message]}return[n instanceof Error?n.message:String(n)]}}function nn(e,t){let{repoRoot:r,configPath:n}=Zn(e,t);return Bi(r,n)}function ro(e,t){try{return nn(e,t)}catch(r){if(!va(r,Vn))throw r;let n=e===""?process.cwd():q_(e),o=Jn();return o.repoRoot=n,o.configPath=to(n,Nt,lr),o}}function Bi(e,t,r){let n=Jn(),o=Na();Pg(t,n,o,r?.candidateText,"main"),n.repoRoot=e,n.configPath=t;let i=G_(t);if(Cg(i)===Nt)Y_(i,n,o);n=z_(n,e,t,o),n=Ih(n);try{Lh(n,o)}catch(a){throw Pe(Q,"template merge failed",a)}Ah(n,o.provenance),n.provenance=o.provenance;let s=eo(n);if(s.length>0)throw I(Q,s.join(`
2170
- `));return n}function z_(e,t,r,n){let o=to(cr(),"config.local.yaml"),i=to(t,Nt,"config.local.yaml");for(let s of[o,i]){if(s===r||!Hi(s))continue;Pg(s,e,n,void 0,s===o?"home_local":"repo_local")}return e}function Pg(e,t,r,n,o="main"){let i=n!==void 0?Rg(n,e):ru(e),s=kn(i,"");if(s.length>0)throw I(Q,Wo(s));Eh(t,i,r,{source:e,layer:o})}function Y_(e,t,r){kg(to(e,"services"),(o,i,s)=>{let a=kn(i,`services.${o}`);if(a.length>0)throw I(Q,Wo(a));let c=t.services[o];t.services[o]=c?Fa(c,i):Ni(i),$a(r.services,o,i),rn(r.provenance,i,s,"modular_service",`services.${o}`)}),kg(to(e,"profiles"),(o,i,s)=>{let a=kn(i,`profiles.${o}`);if(a.length>0)throw I(Q,Wo(a));t.profiles[o]=Ia(i),rn(r.provenance,i,s,"modular_profile",`profiles.${o}`)});let n=to(e,"proxy","routes.yaml");if(Hi(n))J_(n,t,r)}function J_(e,t,r){let n=ru(e);if(N(n.proxy))rn(r.provenance,n.proxy,e,"modular_proxy","proxy");if(Array.isArray(n.routes))rn(r.provenance,n.routes,e,"modular_proxy","proxy.routes");if(N(n.proxy)){let o=kn({proxy:n.proxy},"");if(o.length>0)throw I(Q,Wo(o))}if(Array.isArray(n.routes))n.routes.forEach((o,i)=>{let s=kn(o,`proxy.routes.${i}`);if(s.length>0)throw I(Q,Wo(s))});if(N(n.proxy)){if(Array.isArray(n.proxy.routes))t.proxy.routes.push(...n.proxy.routes.map((o)=>Qn(o)));if(N(n.proxy.listen)){if(typeof n.proxy.listen.host==="string"&&n.proxy.listen.host!=="")t.proxy.listen.host=n.proxy.listen.host;if(typeof n.proxy.listen.port==="number"&&n.proxy.listen.port!==0)t.proxy.listen.port=n.proxy.listen.port}}if(Array.isArray(n.routes))t.proxy.routes.push(...n.routes.map((o)=>Qn(o)))}function kg(e,t){let r;try{r=U_(e)}catch(n){if(X_(n))return;throw n}for(let n of r.sort()){let o=K_(n);if(o!==".yaml"&&o!==".yml")continue;let i=to(e,n),s=ru(i);t(Cg(n,o),s,i)}}function ru(e){let t;try{t=W_(e,"utf8")}catch(r){throw Pe(Q,"unable to read config",r)}return Rg(t,e)}function Rg(e,t){let r;try{r=V_(e)}catch(n){throw Pe(Q,`invalid YAML in ${t}`,n)}if(r===null||r===void 0)return{};if(!N(r))throw I(Q,`unable to decode config: ${t} is not a mapping`);return r}function X_(e){return typeof e==="object"&&e!==null&&"code"in e&&e.code==="ENOENT"}ht();te();jr();import{existsSync as Ka,readFileSync as Z_}from"fs";import{homedir as Lg}from"os";import{join as su}from"path";var{spawn:au}=globalThis.Bun;var eE="https://www.googleapis.com/auth/cloud-platform",Dg=1500,tE=2000;function qo(){let e=globalThis;if(e.window&&typeof e.window.fetch!=="function")e.window.fetch=fetch}function cu(){let e=process.env.GOOGLE_APPLICATION_CREDENTIALS||su(Lg(),".config","gcloud","application_default_credentials.json");if(!Ka(e))return"";try{let t=JSON.parse(Z_(e,"utf8"));return typeof t.quota_project_id==="string"?t.quota_project_id:""}catch{return""}}async function so(e){let t={gcloudInstalled:await lu("gcloud"),adcAvailable:!1,userEmail:"",projectID:"",projectSource:""};if(e!=="")t.projectID=e,t.projectSource="configuration";else{let r=sE("GOOGLE_CLOUD_PROJECT","GCLOUD_PROJECT","GOOGLE_PROJECT");if(r!=="")t.projectID=r,t.projectSource="environment variable";else if(t.gcloudInstalled){let n=await Ag("core/project");if(n!=="")t.projectID=n,t.projectSource="gcloud configuration"}}if(Wi())try{await Fg(rE(t),tE)}catch(r){t.adcAvailable=!1,t.error=Vo(r)}if(t.userEmail===""&&t.gcloudInstalled)t.userEmail=await Ag("core/account");return t}async function rE(e){qo();let{GoogleAuth:t}=await import("google-auth-library"),r=new t({scopes:[eE]});await r.getClient(),e.adcAvailable=!0;let n=await r.getProjectId().catch(()=>"");if(e.projectID===""&&n)e.projectID=n,e.projectSource="application default credentials";let o=await nE(r);if(o!=="")e.userEmail=o}async function nE(e){try{let t=await e.getCredentials();if(t.client_email)return t.client_email}catch{}try{let t=await e.getClient(),r=typeof t.credentials?.id_token==="string"?t.credentials.id_token:"";if(r!==""){let n=oE(r);if(n!=="")return n}}catch{return""}return""}function oE(e){let t=e.split(".");if(t.length<2||!t[1])return"";try{let r=JSON.parse(Buffer.from(t[1],"base64url").toString("utf8"));return typeof r.email==="string"?r.email:""}catch{return""}}async function Ig(e){let t=await so(e);return dr({kind:"user",email:t.userEmail,project:t.projectID,projectSource:t.projectSource,adcAvailable:t.adcAvailable})}async function Mg(){if(await au({cmd:["gcloud","auth","application-default","login"],stdin:"inherit",stdout:"inherit",stderr:"inherit"}).exited!==0)throw Te(hn,"application-default login failed","run `gcloud auth application-default login`")}async function Og(){if(await au({cmd:["gcloud","auth","application-default","revoke","--quiet"],stdout:"ignore",stderr:"pipe"}).exited!==0)throw Te(hn,"failed to revoke application-default credentials","run `gcloud auth application-default revoke`")}function Vo(e){let t=iE(e).toLowerCase();if(io(t,["unauthorized_client"]))return Te(ga,"IAP OAuth client does not match the ADC refresh token","omit client_id to use ADC's default client, or login with an OAuth client that matches auth.client_id");if(io(t,["unauth","invalid_grant","token has been expired","expired"]))return Te(hn,"credential expired or invalid","run `devctl auth login` or `gcloud auth application-default login`");if(io(t,["api has not been used","has not been enabled","access not configured","service_disabled"]))return Te(zn,"required Google API is not enabled","enable the API in the target service account project; devctl will not enable APIs automatically");if(io(t,["permission","forbidden","iam"])){if(io(t,["serviceaccounttoken","iamcredentials","token creator","getaccesstoken"]))return Te(ha,"cannot impersonate service account","ask an administrator to grant roles/iam.serviceAccountTokenCreator on the target service account");return Te(zn,"authorization failure","verify IAM permissions for the current user on this project")}if(t.includes("audience"))return Te(ga,"IAP audience is incorrect","set auth.audience on the proxy route to the IAP OAuth client ID");if(t.includes("iap"))return Pe(ga,"IAP authentication failure",e);if(t.includes("project"))return Te(Q,"wrong or missing Google project","set google.project_id in .devctl/config.yaml");if(io(t,["timeout","network","connection refused","no such host"]))return Pe(lt,"network problem reaching Google Cloud",e);if(io(t,["adc","default credentials","could not find"]))return Te(hn,"application default credentials unavailable","run `gcloud auth application-default login`");return Pe(hn,"Google authentication failed",e)}function iE(e){let t=[],r=new Set,n=(o,i)=>{if(o===null||o===void 0||i>8||r.has(o))return;if(typeof o==="string"){t.push(o);return}if(typeof o!=="object")return;r.add(o);let s=o;for(let a of["message","reason","status","code","error_description","permission","service"])n(s[a],i+1);for(let a of["error","errors","details","response","data","cause","metadata"]){let c=s[a];if(Array.isArray(c))c.forEach((l)=>n(l,i+1));else n(c,i+1)}};return n(e,0),t.length>0?t.join(" "):String(e)}function io(e,t){return t.some((r)=>e.includes(r))}function sE(...e){for(let t of e){let r=process.env[t];if(r)return r}return""}async function lu(e){try{return(await $g(process.platform==="win32"?["where",e]:["which",e],Dg)).code===0}catch{return!1}}function Wi(){let e=process.env.GOOGLE_APPLICATION_CREDENTIALS;if(e&&Ka(e))return!0;if(Ka(su(Lg(),".config","gcloud","application_default_credentials.json")))return!0;let t=process.env.APPDATA;return Boolean(t&&Ka(su(t,"gcloud","application_default_credentials.json")))}async function Ag(e){try{let t=await $g(["gcloud","config","get-value",e],Dg);if(t.code!==0)return"";let r=t.stdout.trim();if(r===""||r==="(unset)")return"";return r}catch{return""}}async function $g(e,t){let r=au({cmd:e,stdout:"pipe",stderr:"ignore",env:{...process.env,CLOUDSDK_CORE_DISABLE_PROMPTS:"1",CLOUDSDK_CORE_DISABLE_USAGE_REPORTING:"true"}}),n=r.stdout?new Response(r.stdout).text():Promise.resolve("");try{let[o,i]=await Fg(Promise.all([n,r.exited]),t);return{code:i??1,stdout:o}}catch{return r.kill(),{code:1,stdout:""}}}function Fg(e,t){return new Promise((r,n)=>{let o=setTimeout(()=>n(Error("timeout")),t);e.then((i)=>{clearTimeout(o),r(i)},(i)=>{clearTimeout(o),n(i)})})}import{createHash as SE}from"crypto";import{join as kE}from"path";import{GoogleAuth as Ki,Impersonated as CE,UserRefreshClient as PE}from"google-auth-library";var{spawn:fu}=globalThis.Bun;import{existsSync as Va,readdirSync as aE,readFileSync as Ng,unlinkSync as cE}from"fs";import{join as Gi}from"path";var mu="keychain-index.json",ao="devctl.credentials",uu=".cred.json";function pu(e){return Gi(kr(),`${Ya(e)}${uu}`)}function za(e){if(e==="file"||process.env.DEVCTL_HOME&&e!=="keychain")return new qa;if(e==="keychain"||process.platform==="darwin"||process.platform==="win32"||process.platform==="linux")return new Hg(new qa);return new qa}class qa{backend="file";async get(e){let t=pE(e);if(!t||t.accessToken==="")return;return t}async set(e,t){Cr(pu(e),`${JSON.stringify({...uE(t),key:e})}
2171
- `)}async delete(e){let t=pu(e);if(Va(t))cE(t)}async list(){let e=kr();if(!Va(e))return[];let t=[];for(let r of aE(e)){if(!r.endsWith(uu))continue;let n=r.slice(0,-uu.length),o=jg(Gi(kr(),r));if(!o)continue;t.push(Bg(o.key??n,o.record))}return t.sort((r,n)=>r.identity.localeCompare(n.identity))}}class Hg{backend="keychain";fallback;constructor(e){this.fallback=e}async get(e){let t=await fE(e);if(t)return t;return this.fallback.get(e)}async set(e,t){if(!await mE(e,t))await this.fallback.set(e,t);lE(e)}async delete(e){await hE(e),await this.fallback.delete(e),dE(e)}async list(){let e=new Set(hu()),t=await this.fallback.list(),r=new Map;for(let n of t)r.set(n.key,n);for(let n of e){let o=await this.get(n);if(o)r.set(n,Bg(n,o))}return[...r.values()].sort((n,o)=>n.identity.localeCompare(o.identity))}}function lE(e){let t=hu();if(!t.includes(e))Cr(Gi(kr(),mu),`${JSON.stringify([...t,e])}
2172
- `)}function dE(e){let t=hu(),r=t.filter((n)=>n!==e);if(r.length!==t.length)Cr(Gi(kr(),mu),`${JSON.stringify(r)}
2173
- `)}function hu(){let e=Gi(kr(),mu);if(!Va(e))return[];try{let t=JSON.parse(Ng(e,"utf8"));return Array.isArray(t)?t.filter((r)=>typeof r==="string"):[]}catch{return[]}}function uE(e){return{...e,accessToken:""}}function pE(e){return jg(pu(e))?.record}function jg(e){if(!Va(e))return;try{let t=JSON.parse(Ng(e,"utf8"));if(typeof t.identity!=="string")return;return{key:typeof t.key==="string"&&t.key!==""?t.key:void 0,record:{identity:t.identity,audience:typeof t.audience==="string"?t.audience:"",scopes:Array.isArray(t.scopes)?t.scopes.filter((r)=>typeof r==="string"):[],accessToken:typeof t.accessToken==="string"?t.accessToken:"",tokenType:typeof t.tokenType==="string"?t.tokenType:"Bearer",expiresAt:typeof t.expiresAt==="string"?t.expiresAt:""}}}catch{return}}function Bg(e,t){let r=Date.parse(t.expiresAt);return{key:e,identity:t.identity,audience:t.audience,scopes:t.scopes,expires_at:t.expiresAt,valid:Number.isFinite(r)&&r-Date.now()>0}}function Ya(e){return e.replace(/[^A-Za-z0-9._-]+/g,"_").slice(0,80)||"token"}async function fE(e){let t=await gE(e);if(!t)return;try{return JSON.parse(t)}catch{return}}async function mE(e,t){let r=JSON.stringify(t);if(process.platform==="darwin")return await zo(["security","delete-generic-password","-s",ao,"-a",e]),await zo(["security","add-generic-password","-s",ao,"-a",e,"-w",r,"-U"])===0;if(process.platform==="linux")return await vE(["secret-tool","store","--label",`devctl ${e}`,"service",ao,"account",e],r)===0;if(process.platform==="win32"){let o=`Add-Type -AssemblyName System.Security; $bytes = [Convert]::FromBase64String('${Buffer.from(r,"utf8").toString("base64")}'); $enc = [System.Security.Cryptography.ProtectedData]::Protect($bytes, $null, 'CurrentUser'); [IO.File]::WriteAllBytes($env:LOCALAPPDATA + '\\devctl-cred-${Ya(e)}.bin', $enc)`;return await zo(["powershell","-NoProfile","-Command",o])===0}return!1}async function hE(e){if(process.platform==="darwin"){await zo(["security","delete-generic-password","-s",ao,"-a",e]);return}if(process.platform==="linux"){await zo(["secret-tool","clear","service",ao,"account",e]);return}if(process.platform==="win32")await zo(["powershell","-NoProfile","-Command",`Remove-Item -ErrorAction SilentlyContinue ($env:LOCALAPPDATA + '\\devctl-cred-${Ya(e)}.bin')`])}async function gE(e){if(process.platform==="darwin"){let t=await du(["security","find-generic-password","-s",ao,"-a",e,"-w"]);return t.code===0&&t.stdout!==""?t.stdout:void 0}if(process.platform==="linux"){let t=await du(["secret-tool","lookup","service",ao,"account",e]);return t.code===0&&t.stdout!==""?t.stdout:void 0}if(process.platform==="win32"){let t=`$p = $env:LOCALAPPDATA + '\\devctl-cred-${Ya(e)}.bin'; if (-not (Test-Path $p)) { exit 1 }; Add-Type -AssemblyName System.Security; $raw = [IO.File]::ReadAllBytes($p); $dec = [System.Security.Cryptography.ProtectedData]::Unprotect($raw, $null, 'CurrentUser'); [Text.Encoding]::UTF8.GetString($dec)`,r=await du(["powershell","-NoProfile","-Command",t]);return r.code===0&&r.stdout!==""?r.stdout:void 0}return}var gu=3000;async function zo(e){try{return await fu({cmd:e,stdout:"ignore",stderr:"ignore",timeout:gu}).exited}catch{return 1}}async function vE(e,t){try{return await fu({cmd:e,stdin:new TextEncoder().encode(t),stdout:"ignore",stderr:"ignore",timeout:gu}).exited}catch{return 1}}async function du(e){try{let t=fu({cmd:e,stdout:"pipe",stderr:"ignore",timeout:gu}),r=t.stdout?await new Response(t.stdout).text():"";return{code:await t.exited,stdout:r.trim()}}catch{return{code:1,stdout:""}}}te();te();var yE=3,xE=200;async function Ug(e,t={}){let r=t.attempts??yE,n=t.backoffMs??xE,o=t.retry??bE,i=Error("retry exhausted");for(let s=0;s<r;s+=1)try{return await e()}catch(a){if(i=a,!o(a)||s===r-1)throw a;await wE(n*(s+1))}throw i}function bE(e){if(e instanceof Ve)return e.kind!==Q&&e.kind!==zn;return!0}function wE(e){return new Promise((t)=>{setTimeout(t,e)})}var Ja={now:()=>new Date,isoNow:()=>new Date().toISOString(),unixMs:()=>Date.now()};var vu=300000,RE=3000000,_E=3,EE=200,Xa="https://www.googleapis.com/auth/cloud-platform",Gg="https://www.googleapis.com/auth/userinfo.email";function Wg(e,t,r,n){let o=n??"",i=`${e}|${t}|${r.join(",")}`;return o===""?i:`${i}|oauth:${o}`}function TE(e,t=process.env){let r=(e.client_id??"").trim();if(r==="")return;let n=(e.client_secret??"").trim(),{value:o,missing:i}=vg(n,t);if(o===""){let s=i[0];throw I(Q,s?`IAP client_secret env ${s} is empty`:`IAP client_id ${r} requires client_secret`)}return{clientId:r,clientSecret:o}}function Qa(e,t=process.env){let r=(e.client_id??"").trim();if(r==="")return;return{clientId:r,resolve:()=>{let n=TE(e,t);if(!n)throw I(Q,`IAP client_id ${r} requires client_secret`);return n}}}function AE(e,t=vu,r=Date.now()){if(e.accessToken==="")return!1;return e.expiresAt.getTime()-r>=t}function LE(e,t=vu,r=Date.now()){if(e.accessToken==="")return!0;let n=e.expiresAt.getTime()-r;return n>0&&n<t}class co{cache=new Map;inflight=new Map;providers;thresholdMs;bus;store;clock;constructor(e,t,r,n,o=Ja){this.thresholdMs=e>0?e:vu,this.providers=[...t],this.bus=r,this.store=n??za(process.env.DEVCTL_CREDENTIAL_BACKEND==="file"?"file":void 0),this.clock=o}replaceProviders(e){this.providers.splice(0,this.providers.length,...e)}isValid(e){return AE(e,this.thresholdMs,this.clock.unixMs())}expiresSoon(e){return LE(e,this.thresholdMs,this.clock.unixMs())}invalidate(e){if(e===void 0||e===""){this.cache.clear(),this.clearStore();return}this.cache.delete(e),this.store.delete(e)}async listStatus(){return this.store.list()}storeBackend(){return this.store.backend}async get(e,t,r,n){let o=Wg(e,t,r,n?.clientId),i=this.cache.get(o)??await this.loadStored(o);if(i&&this.isValid(i))return this.cache.set(o,i),i;return this.refresh(e,t,r,n)}async refresh(e,t,r,n){let o=Wg(e,t,r,n?.clientId),i=this.inflight.get(o);if(i)return i;let s=this.refreshOnce(o,e,t,r,n);this.inflight.set(o,s);try{return await s}finally{this.inflight.delete(o)}}async refreshOnce(e,t,r,n,o){let i=o?.resolve(),s=this.providers.filter((c)=>!c.accepts||c.accepts(t,r,n,i));if(s.length===0)throw I(Fr,`no token provider accepts identity ${t}`);let a=I(Fr,"no token provider available");for(let c of s)try{let l=await Ug(()=>c.fetch(t,r,n,i),{attempts:_E,backoffMs:EE,retry:qE});return this.cache.set(e,l),IE(e,l),await this.store.set(e,ME(l)),this.bus?.publish(Se(Oo,"",{identity:l.identity,audience:l.audience})),l}catch(l){a=l instanceof Error?l:Error(String(l))}throw this.bus?.publish(Se(Gn,"",{identity:t,audience:r,error:A(a)})),a}async loadStored(e){let t=await this.store.get(e);if(!t||t.accessToken==="")return;return OE(t)}async clearStore(){let e=await this.store.list();await Promise.all(e.map((t)=>this.store.delete(t.key)))}}function DE(e){let t=SE("sha256").update(e).digest("hex").slice(0,16);return kE(kr(),`${t}.json`)}function IE(e,t){let r={identity:t.identity,audience:t.audience,expires_at:t.expiresAt.toISOString(),scopes:t.scopes};Cr(DE(e),`${JSON.stringify(r)}
2174
- `)}function ME(e){return{identity:e.identity,audience:e.audience,scopes:e.scopes,accessToken:e.accessToken,tokenType:e.tokenType,expiresAt:e.expiresAt.toISOString()}}function OE(e){return{accessToken:e.accessToken,tokenType:e.tokenType||"Bearer",expiresAt:new Date(e.expiresAt),audience:e.audience,identity:e.identity,scopes:e.scopes}}function Rn(){return[NE(),FE(),$E()]}function $E(){return{name:"user",accepts:(e,t)=>!e.startsWith("sa:")&&t==="",fetch:async(e,t,r)=>{if(e.startsWith("sa:"))throw I(Fr,"user provider cannot mint service-account tokens");return WE(e,t,r)}}}function FE(){return{name:"service_account",accepts:(e,t)=>e.startsWith("sa:")&&t==="",fetch:async(e,t,r)=>{if(!e.startsWith("sa:"))throw I(Fr,"service-account provider cannot mint user tokens");return HE(e,t,r)}}}function NE(){return{name:"iap",accepts:(e,t)=>t!=="",fetch:async(e,t,r,n)=>{if(t==="")throw I(Fr,"IAP audience is required");if(e.startsWith("sa:"))return jE(e,t,r);return BE(e,t,r,n)}}}async function HE(e,t,r){let n=e.slice(3);try{qo();let i=await new Ki({scopes:r.length>0?r:[Xa]}).getClient(),s=new CE({sourceClient:i,targetPrincipal:n,targetScopes:r.length>0?r:[Xa],lifetime:3600}),a=await s.getAccessToken(),c=typeof a==="string"?a:a?.token;if(!c)throw I(Fr,"empty impersonated token");return{accessToken:c,tokenType:"Bearer",expiresAt:qi(c,s),audience:t,identity:e,scopes:r}}catch(o){throw Vo(o)}}async function jE(e,t,r){let n=e.slice(3);try{qo();let i=await new Ki({scopes:[Xa]}).getClient(),s=`https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/${encodeURIComponent(n)}:generateIdToken`,c=(await i.request({url:s,method:"POST",data:{audience:t,includeEmail:!0}})).data.token;if(!c)throw I(Fr,"empty impersonated IAP id token");return{accessToken:c,tokenType:"Bearer",expiresAt:qi(c),audience:t,identity:e,scopes:r}}catch(o){throw Vo(o)}}async function BE(e,t,r,n){try{if(qo(),n)return await UE(e,t,r,n);let s=await(await new Ki({scopes:r.length>0?r:[Gg]}).getIdTokenClient(t)).idTokenProvider.fetchIdToken(t);return{accessToken:s,tokenType:"Bearer",expiresAt:qi(s),audience:t,identity:e,scopes:r}}catch(o){if(o instanceof Ve)throw o;throw Vo(o)}}async function UE(e,t,r,n){let s=(await new Ki({scopes:r.length>0?r:[Gg]}).getClient()).credentials.refresh_token;if(!s)throw Te(hn,"ADC has no refresh token for a custom IAP OAuth client","run `gcloud auth application-default login` with a user account; service-account ADC cannot mint IAP ID tokens for auth.client_id");let c=await new PE({clientId:n.clientId,clientSecret:n.clientSecret,refreshToken:s}).fetchIdToken(t);if(!c)throw I(Fr,"empty IAP id token");return{accessToken:c,tokenType:"Bearer",expiresAt:qi(c),audience:t,identity:e,scopes:r}}async function WE(e,t,r){try{qo();let o=await new Ki({scopes:r.length>0?r:[Xa]}).getClient(),i=await o.getAccessToken();if(!i.token)throw I(Fr,"empty access token");return{accessToken:i.token,tokenType:"Bearer",expiresAt:GE(o,i.token),audience:t,identity:e,scopes:r}}catch(n){throw Vo(n)}}function qi(e,t){if(t?.credentials?.expiry_date)return new Date(t.credentials.expiry_date);return KE(e)??new Date(Date.now()+RE)}function GE(e,t){return qi(t,e)}function KE(e){let t=e.split(".");if(t.length<2||!t[1])return;try{let r=JSON.parse(Buffer.from(t[1],"base64url").toString("utf8"));if(typeof r.exp==="number")return new Date(r.exp*1000)}catch{return}return}function qE(e){if(e instanceof Ve)return e.kind!==Q&&e.kind!==zn;let t=e instanceof Error?e.message.toLowerCase():String(e).toLowerCase();return t.includes("network")||t.includes("econnreset")||t.includes("etimedout")||t.includes("503")||t.includes("429")}te();jr();var zi=4000,VE=8000;function Yo(e){let t=e?.tokens,r=()=>(t??=new co(60000,Rn(),void 0,za("file")),t);return{detectGoogle:so,hasCommand:lu,portAvailable:jo,hasLocalAdc:Wi,adcQuotaProject:cu,mintToken:async(n,o,i)=>{await r().get(n,o,[],i)},probeServiceUsage:async(n,o)=>{let i=await Yi(r().get("user","",[]),zi),s=`https://serviceusage.googleapis.com/v1/projects/${n}/services/${o}`,a=await fetch(s,{headers:{Authorization:`Bearer ${i.accessToken}`},signal:AbortSignal.timeout(zi)});if(!a.ok)return!1;return(await a.json().catch(()=>{return}))?.state==="ENABLED"},containerRuntimeAvailable:async(n)=>{try{return await Bun.spawn({cmd:[n,"info"],stdout:"ignore",stderr:"ignore",stdin:"ignore"}).exited===0}catch{return!1}}}}var Za=Yo();function ec(e=Yo()){return{run:(t,r,n)=>zE(t,e,r,n)}}async function zE(e,t=Za,r,n){let o={checks:[],issues:0},i="Preparing diagnostics",s=()=>r?.({active:i,checks:[...o.checks]}),a=(p)=>{i=p,s()},c=(p)=>{if(o.checks.push(p),p.severity==="error")o.issues+=1;s()},l=[...new Set(Object.values(e.services).flatMap((p)=>p.container?[p.container.runtime||"docker"]:[]))];for(let p of l){a(`${p} container runtime`);let m=await t.hasCommand(p),h=m&&await(t.containerRuntimeAvailable??Za.containerRuntimeAvailable)(p);c(h?{name:`${p} container runtime`,severity:"ok",message:`${p} daemon reachable`}:{name:`${p} container runtime`,severity:"error",message:m?`${p} daemon is not reachable`:`${p} not found`,hint:`install and start ${p}`})}if(a("Google CLI installed"),await t.hasCommand("gcloud"))c({name:"Google CLI installed",severity:"ok",message:"gcloud found"});else c({name:"Google CLI installed",severity:Ui(e)?"error":"warn",message:"gcloud not installed",hint:"install the Google Cloud CLI from https://cloud.google.com/sdk/docs/install"});a("Google environment");let d=await t.detectGoogle(e.google.project_id);if(d.adcAvailable)c({name:"Google authentication available",severity:"ok",message:"Application Default Credentials found"});else c({name:"Google authentication available",severity:Ui(e)?"error":"warn",message:"ADC unavailable",hint:"run `gcloud auth application-default login`"});if(d.projectID!=="")c({name:"Project configured",severity:"ok",message:`${d.projectID} (source: ${d.projectSource})`});else c({name:"Project configured",severity:Ui(e)?"error":"warn",message:"no Google project configured",hint:"set google.project_id in .devctl/config.yaml"});if(Ui(e)||Object.values(e.services).some((p)=>p.capabilities.includes("google")||p.identity.type!=="")){a("Live Google access");let p=!0,m=(h)=>{if(p)c(h)};try{await Yi((async()=>{await JE(e,m,t,d.userEmail),await YE(e,m,t)})(),t.liveDeadlineMs??VE)}catch(h){m({name:"Live Google probes",severity:"warn",message:A(h),hint:"network or timeout \u2014 retry when online"})}finally{p=!1}}for(let p of e.doctor.tools){let m=p.command||p.name;if(a(`${p.name} installed`),await t.hasCommand(m))c({name:`${p.name} installed`,severity:"ok",message:`${m} found`});else c({name:`${p.name} installed`,severity:"error",message:`${m} not found`,hint:`install ${p.name} and ensure it is on PATH`})}if(a("Repository configuration"),n?.repositoryConfigError!==void 0)if(n.repositoryConfigError==="")c({name:"Repository configuration",severity:"ok",message:"valid"});else c({name:"Repository configuration",severity:"error",message:n.repositoryConfigError});else try{let p=eo(e);if(p.length>0)c({name:"Repository configuration",severity:"error",message:p.join("; ")});else c({name:"Repository configuration",severity:"ok",message:"valid"})}catch(p){c({name:"Repository configuration",severity:"error",message:A(p)})}let f={};for(let[p,m]of Object.entries(e.services))for(let h of m.ports){if(h.auto)continue;let v=`Port ${h.value}`;if(a(v),f[h.value])c({name:v,severity:"error",message:`configured on both ${f[h.value]} and ${p}`});else{f[h.value]=p;let x=n?.services?.[p],g=Boolean(m.container&&x&&["STARTING","RUNNING","HEALTHY","UNHEALTHY"].includes(x.state??""));if(Boolean(x&&(x.pid>0||g)&&Object.values(x.ports).includes(h.value)))c({name:v,severity:"ok",message:`in use by running service ${p}`});else if(await t.portAvailable(h.value))c({name:v,severity:"ok",message:"available"});else c(await qg(v,h.value,`services.${p}.ports`))}}if(e.proxy.listen.port>0){let p=`Port ${e.proxy.listen.port}`;if(a(p),n?.proxyRunning)c({name:p,severity:"ok",message:"in use by the running proxy"});else if(await t.portAvailable(e.proxy.listen.port))c({name:p,severity:"ok",message:"proxy listen port available"});else c(await qg(p,e.proxy.listen.port,"proxy.listen.port"))}return i="Diagnostics complete",s(),o}async function YE(e,t,r){let n=e.google.project_id,o=r.hasLocalAdc??Wi;if(n===""||!o())return;let i=r.probeServiceUsage??Za.probeServiceUsage,s=[{name:"IAM Credentials API",service:"iamcredentials.googleapis.com"},{name:"Resource Manager API",service:"cloudresourcemanager.googleapis.com"},{name:"IAP API",service:"iap.googleapis.com"}];(await Promise.allSettled(s.map((c)=>Yi(i(n,c.service),zi)))).forEach((c,l)=>{let d=s[l];if(!d)return;if(c.status==="fulfilled"){t({name:d.name,severity:c.value?"ok":"warn",message:c.value?"enabled":"not enabled or unreachable",hint:c.value?void 0:`enable ${d.service} in the Google Cloud console \u2014 doctor never auto-enables APIs`});return}t({name:d.name,severity:"warn",message:A(c.reason),hint:`enable ${d.service} in the Google Cloud console \u2014 doctor never auto-enables APIs`})})}async function JE(e,t,r,n){let o=Hr(e),i=e.proxy.routes.filter((u)=>u.auth.type.toLowerCase()==="iap"),s=i.filter((u)=>u.auth.audience.trim()!=="");for(let u of i)if(u.auth.audience.trim()==="")t({name:`IAP audience ${u.name}`,severity:"error",message:"missing audience",hint:"set auth.audience to the IAP OAuth client ID"});if(o.length===0&&s.length===0)return;if(!(r.hasLocalAdc??Wi)())return;let c=r.mintToken??Za.mintToken,l=o.map(async(u)=>{try{await Yi(c(`sa:${u}`,""),zi),t({name:`Impersonate ${u}`,severity:"ok",message:"token minted"})}catch(f){let p=(r.adcQuotaProject??cu)(),m=n||"the ADC principal",h=p===""?"unknown":p,v=Kg(`Impersonate ${u}`,f,`grant roles/iam.serviceAccountTokenCreator to ${m} on ${u}; ADC quota project: ${h}`);if(p!==""&&v.message.toLowerCase().includes("api is not enabled"))v.message=`IAM Credentials API is disabled in ADC quota project ${p}`,v.hint=`enable iamcredentials.googleapis.com in ${p}, or set the ADC quota project to ${e.google.project_id}`;else if(v.message.toLowerCase()==="google authentication failed")v.message=`access-token mint failed: ${m} \u2192 ${u}`,v.hint=`verify roles/iam.serviceAccountTokenCreator for ${m} on this service account; ADC quota project: ${h}`;t(v)}}),d=s.map(async(u)=>{let f=oo(u.auth).kind===no?`sa:${oo(u.auth).serviceAccount}`:"user";try{let p=Qa(u.auth);await Yi(c(f,u.auth.audience,p),zi),t({name:`IAP ${u.name}`,severity:"ok",message:"id token minted"})}catch(p){t(Kg(`IAP ${u.name}`,p,"check IAP OAuth client ID and ADC"))}});await Promise.all([...l,...d])}function Kg(e,t,r){let n=A(t),o=n.toLowerCase();if(o.includes("timeout")||o.includes("network")||o.includes("econnrefused")||o.includes("enotfound"))return{name:e,severity:"warn",message:n,hint:"network or timeout \u2014 retry when online"};if(t instanceof Ve&&t.hint!==""){let i=`${t.kind}: `,s=t.message.startsWith(i)?t.message.slice(i.length):t.message;return{name:e,severity:"error",message:s,hint:t.hint}}return{name:e,severity:"error",message:n,hint:r}}function Yi(e,t){return new Promise((r,n)=>{let o=setTimeout(()=>n(Error("timeout")),t);e.then((i)=>{clearTimeout(o),r(i)},(i)=>{clearTimeout(o),n(i)})})}async function qg(e,t,r){let n=await Yn(t);if(!n)return{name:e,severity:"error",message:"already in use",hint:`stop the process using port ${t} or change ${r}`};if(n.pid===process.pid)return{name:e,severity:"error",message:`in use by this TUI (${n.command} pid ${n.pid})`,hint:"stop the proxy from the proxy screen (x), then rerun doctor"};return{name:e,severity:"error",message:`in use by ${n.command} (pid ${n.pid})`,hint:"enter stop that process after a confirmation",action:{kind:"free-port",holder:n}}}function Vg(e){let t=[`${er()} doctor`,""];for(let r of e.checks){let n=r.severity==="error"?"\u2717":r.severity==="warn"?"!":"\u2713";if(t.push(`${n} ${r.name}`),r.severity!=="ok"){if(t.push(` ${r.message}`),r.hint)t.push(` \u2192 ${r.hint}`)}}return t.push("",`${e.issues} issue(s) found.`),t.join(`
2238
+ `,{mode:384})}function ih(e){let t=Oi(e)&&Cd(e).isDirectory()?e:rh(e);if(Mr(t),process.platform==="darwin"){let r=Oi(e)&&Cd(e).isFile()?["-R",e]:[t];Sd("open",r,{detached:!0,stdio:"ignore"}).unref();return}if(process.platform==="win32"){Sd("explorer",[t],{detached:!0,stdio:"ignore"}).unref();return}Sd("xdg-open",[t],{detached:!0,stdio:"ignore"}).unref()}function sh(e=Qs()){if(!Oi(e))return[];return Pd(e).filter((t)=>t.startsWith(_d)).sort().reverse()}function ah(e,t=Qs()){let r=No(t,e);if(!Oi(r))return[];let n=[];for(let i of Pd(r)){if(!i.endsWith(".log"))continue;let s=g_(No(r,i),"utf8");for(let a of s.split(`
2239
+ `)){if(a.trim()==="")continue;let c=a.split(" "),l=c.slice(3).join(" "),d=vd(l);n.push({timestamp:c[0]??"",service:c[1]??i.replace(/\.log$/,""),source:"history",level:c[2]??"INFO",message:d?.message??l,raw:d?l:void 0,pid:0,seq:0})}}let o=n.sort((i,s)=>i.timestamp.localeCompare(s.timestamp));return o.forEach((i,s)=>{i.seq=s+1}),o}function S_(e){let t=e.replace(/[^A-Za-z0-9._-]+/g,"_");return t===""?"service":t}function k_(e,t,r){if(!Oi(e))return;let n=Pd(e).filter((i)=>i.startsWith(_d)).map((i)=>{let s=No(e,i),a=Cd(s);return{path:s,mtime:a.mtimeMs}}).sort((i,s)=>s.mtime-i.mtime),o=t>0?Date.now()-t*86400000:0;n.forEach((i,s)=>{let a=o>0&&i.mtime<o,c=r>0&&s>=r;if(a||c)v_(i.path,{recursive:!0,force:!0})})}te();var{spawn:Ld}=globalThis.Bun;import{createServer as mh}from"net";var C_=65535,Ad=1;async function hh(e,t,r={}){let n={};for(let[i,s]of Object.entries(r))for(let a of Object.values(s))n[a]=i;let o={};for(let[i,s]of Object.entries(e.services)){if(t.length>0&&!t.includes(i))continue;let a={};for(let c of s.ports){let l=r[i]?.[c.name],d=l&&l>=Ad?l:c.value;if(l===void 0&&c.auto)d=await P_();if(d<Ad||d>C_)throw new Ve(Y,`invalid port ${d} on service ${i}`,{service:i});if(n[d]&&n[d]!==i)throw new Ve(Y,`duplicate port ${d} used by ${n[d]} and ${i}`,{service:i});if(l!==d&&!c.auto&&!await jo(d))throw await __(i,c.name,d);n[d]=i,a[c.name]=d}o[i]=a}return o}function P_(){return new Promise((e,t)=>{let r=mh();r.listen(0,"127.0.0.1",()=>{let n=r.address();r.close(()=>{if(n&&typeof n==="object")e(n.port);else t(Pe(Y,"unable to allocate dynamic port",Error("no address")))})}),r.on("error",(n)=>t(Pe(Y,"unable to allocate dynamic port",n)))})}async function xa(e){let t=e.ports.filter((n)=>!n.auto&&n.value>=Ad);if(t.length===0)return;let r={};for(let n of t){if(await jo(n.value))return;r[n.name]=n.value}return r}function jo(e){return new Promise((t)=>{let r=mh();r.listen(e,"127.0.0.1",()=>{r.close(()=>t(!0))}),r.on("error",()=>t(!1))})}async function __(e,t,r){let n=await zn(r);return R_(e,t,r,n)}function R_(e,t,r,n){let o=t===""?`${e} port`:`${e} ports.${t}`;if(n&&n.pid===process.pid)return Te(He,`${e} blocked: this TUI already holds port ${r}`,`Stop the proxy from the Proxy screen, or change ${o} in .devctl.`,e);if(n)return Te(He,`${e} blocked: ${n.command} (pid ${n.pid}) is using port ${r}`,`Open Doctor and press enter to free port ${r}, or change ${o} in .devctl.`,e);return Te(He,`${e} blocked: port ${r} is already in use`,`Something is listening on ${r}. Open Doctor to free it, or change ${o} in .devctl.`,e)}function E_(e,t){let r=e.split(`
2240
+ `).filter((n)=>n.trim()!==""&&!n.startsWith("COMMAND"));for(let n of r){let o=n.trim().split(/\s+/),i=o[0]??"",s=Number(o[1]);if(i!==""&&Number.isInteger(s)&&s>0)return{port:t,pid:s,command:i}}return}async function zn(e){if(process.platform==="win32")return D_(e);let t=await T_(e);if(t)return t;return A_(e)}async function T_(e){try{let t=Ld({cmd:["lsof","-nP",`-iTCP:${e}`,"-sTCP:LISTEN"],stdout:"pipe",stderr:"ignore"}),r=await new Response(t.stdout).text();return await t.exited,E_(r,e)}catch{return}}async function A_(e){let t;try{let o=Ld({cmd:["fuser","-n","tcp",String(e)],stdout:"pipe",stderr:"pipe"});t=`${await new Response(o.stdout).text()} ${await new Response(o.stderr).text()}`,await o.exited}catch{return}let r=t.match(/(\d+)/),n=r?Number(r[1]):0;if(!Number.isInteger(n)||n<=0)return;return{port:e,pid:n,command:"process"}}function L_(e,t){for(let r of e.split(`
2241
+ `)){if(!r.includes("LISTENING")&&!r.includes("LISTEN"))continue;if(!r.includes(`:${t}`)&&!r.includes(` ${t} `))continue;let n=r.trim().match(/(\d+)\s*$/),o=n?Number(n[1]):0;if(Number.isInteger(o)&&o>0)return{port:t,pid:o,command:"process"}}return}async function D_(e){let t=Ld({cmd:["netstat","-ano"],stdout:"pipe",stderr:"ignore"}),r=t.stdout?await new Response(t.stdout).text():"";return await t.exited,L_(r,e)}var I_=400;async function wa(e){if(e.pid===process.pid)throw Te(He,`port ${e.port} is held by this TUI`,"stop the proxy from the proxy screen, or quit the TUI");try{process.kill(e.pid,"SIGTERM")}catch(t){throw Pe(He,`could not stop pid ${e.pid} on port ${e.port}`,t)}await M_(I_);try{process.kill(e.pid,0),process.kill(e.pid,"SIGKILL")}catch{return}}function M_(e){return new Promise((t)=>{setTimeout(t,e)})}Ma();te();import{readdirSync as KR,readFileSync as qR}from"fs";import{basename as Eg,dirname as VR,extname as zR,join as eo,resolve as YR}from"path";import{parse as JR}from"yaml";ht();function N(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}function Ud(e){return N(e)?new Set(Object.keys(e)):new Set}function G(e){if(typeof e==="string")return e;if(typeof e==="number"||typeof e==="boolean")return String(e);return""}function _e(e){if(typeof e==="number")return e;if(typeof e==="string"){let t=Number(e);return Number.isNaN(t)?0:t}return 0}function ot(e){return e===!0}function Rt(e){if(!Array.isArray(e))return[];return e.map((t)=>G(t))}function Wd(e){if(!Array.isArray(e))return[];return e.map((t)=>N(t)?{service:G(t.service),condition:G(t.condition)||"service_started"}:G(t))}function Sn(e){if(!N(e))return{};let t={};for(let[r,n]of Object.entries(e))t[r]=G(n);return t}function Pr(e){if(typeof e==="string")return{args:e.split(/\s+/).filter((t)=>t!==""),shell:!1};if(Array.isArray(e))return{args:e.map((t)=>G(t)),shell:!1};return Bo()}function Gd(e){if(e===void 0||e===null)return[];if(typeof e==="string"||typeof e==="number")return[Bd(e,"http")];if(Array.isArray(e))return e.map((t,r)=>Bd(t,`port_${r}`));if(N(e))return Object.entries(e).map(([t,r])=>Bd(r,t));return[]}function Bd(e,t){if(typeof e==="string"&&e.toLowerCase()==="auto")return{name:t,value:0,auto:!0};return{name:t,value:_e(e),auto:!1}}function _h(e){if(!N(e))return Md();let t={},r=[],n={};for(let[o,i]of Object.entries(e))if(o==="required")r=Rt(i);else if(o==="defaults")n=Sn(i);else t[o]=G(i);return{vars:t,required:r,defaults:n}}function G_(e){if(!N(e))return $d();return{type:G(e.type),mode:G(e.mode),service_account:G(e.service_account),config:N(e.config)?{...e.config}:{}}}function K_(e){if(!N(e))return Od();return{type:G(e.type),url:G(e.url),address:G(e.address),command:Pr(e.command),interval_seconds:_e(e.interval_seconds),timeout_seconds:_e(e.timeout_seconds),start_period_seconds:_e(e.start_period_seconds),unhealthy_threshold:e.unhealthy_threshold===void 0?3:_e(e.unhealthy_threshold),healthy_reset_threshold:e.healthy_reset_threshold===void 0?10:_e(e.healthy_reset_threshold)}}function q_(e){if(!N(e))return{policy:"",max_retries:0,backoff_seconds:0};return{enabled:e.enabled===void 0?void 0:ot(e.enabled),policy:G(e.policy),max_retries:_e(e.max_retries),backoff_seconds:_e(e.backoff_seconds)}}function V_(e){if(!N(e))return{wait_for_healthy:!1,timeout_seconds:0};return{wait_for_healthy:ot(e.wait_for_healthy),timeout_seconds:_e(e.timeout_seconds)}}function ji(e){if(!N(e))return vn();let t=N(e.logs)?e.logs:{};return{extends:G(e.extends),description:G(e.description),command:Pr(e.command),shell:ot(e.shell),working_dir:G(e.working_dir),dependencies:Wd(e.dependencies),ports:Gd(e.ports),environment:_h(e.environment),health:K_(e.health),identity:G_(e.identity),logs:{stdout:ot(t.stdout),stderr:ot(t.stderr)},restart:q_(e.restart),startup:V_(e.startup),capabilities:Rt(e.capabilities),proxy:Vd(e.proxy),expose:qd(e.expose),container:Kd(e.container),watch:z_(e.watch),hooks:Y_(e.hooks)}}function z_(e){if(!N(e))return Dd();return{enabled:ot(e.enabled),paths:Rt(e.paths),debounce_ms:Pa(_e(e.debounce_ms)),ignore:e.ignore===void 0?[...$i]:Rt(e.ignore)}}function Y_(e){let t=N(e)?e:{};return{pre_start:Pr(t.pre_start),post_start:Pr(t.post_start)}}function Rh(e){let t=N(e)?e:{};return{command:Pr(t.command),shell:ot(t.shell),working_dir:G(t.working_dir),dependencies:Rt(t.dependencies),environment:_h(t.environment)}}function Kd(e){if(!N(e))return;let t={};if(N(e.ports))for(let[r,n]of Object.entries(e.ports))t[r]=_e(n);return{image:G(e.image),runtime:G(e.runtime),ports:t,env:Sn(e.env),volumes:Rt(e.volumes)}}function qd(e){if(e===!0)return{enabled:!0,host:"",port:""};if(e===!1)return{enabled:!1,host:"",port:""};if(N(e))return{enabled:e.enabled!==void 0?ot(e.enabled):!0,host:G(e.host),port:G(e.port)};return Id()}function Vd(e){if(e===void 0||e===null)return[];if(Array.isArray(e))return e.map((t)=>Xn(t));if(N(e))return[Xn(e)];return[]}function Oa(e){if(!N(e))return{services:[],environment:{}};return{services:Rt(e.services),environment:Sn(e.environment)}}function J_(e){if(typeof e==="string")return{type:e,service_account:""};if(!N(e))return{type:"",service_account:""};return{type:G(e.type),service_account:G(e.service_account)}}function X_(e){if(!N(e))return Fi();return{type:G(e.type),identity:J_(e.identity),audience:G(e.audience),service_account:G(e.service_account),client_id:G(e.client_id),client_secret:G(e.client_secret),credentials:G(e.credentials)}}function Xn(e){if(!N(e))return{name:"",match:{host:"",path:""},upstream:{url:""},auth:Fi()};let t=N(e.match)?e.match:{},r=N(e.upstream)?e.upstream:{};return{name:G(e.name),match:{host:G(t.host),path:G(t.path)},upstream:{url:G(r.url),service:G(r.service),port:G(r.port)},auth:X_(e.auth)}}te();import{existsSync as Q_,statSync as Th}from"fs";import{basename as Eh,dirname as $a,isAbsolute as Z_,join as Fa,resolve as Ah}from"path";function Qn(e,t){if(t!=="")return eR(t);let r=e===""?process.cwd():e;r=Z_(r)?r:Ah(r);for(;;){let n=Fa(r,Nt,lr);if(Bi(n))return{repoRoot:r,configPath:n};let o=Fa(r,"devctl.yaml");if(Bi(o))return{repoRoot:r,configPath:o};let i=$a(r);if(i===r)break;r=i}throw Te(qn,"no devctl configuration found","run `devctl setup` or create a .devctl/config.yaml in the repository root")}function eR(e){let t=Ah(e),r;try{r=Th(t)}catch(o){throw Pe(qn,"config path not found",o)}if(r.isDirectory()){if(Eh(t)===Nt)return{repoRoot:$a(t),configPath:Fa(t,lr)};return{repoRoot:t,configPath:Fa(t,lr)}}let n=$a(t);if(Eh(n)===Nt)n=$a(n);return{repoRoot:n,configPath:t}}function Bi(e){return Q_(e)&&!Th(e).isDirectory()}ht();function ja(){return{services:{},templates:{},provenance:{}}}function rn(e,t,r,n,o=""){if(N(t)&&Object.keys(t).length>0){for(let[i,s]of Object.entries(t))rn(e,s,r,n,o===""?i:`${o}.${i}`);return}if(o==="")return;(e[o]??=[]).push({source:r,layer:n})}var tR=["environment","restart","startup","health","logs","identity","container","watch","hooks"];function Na(e,t,r){let n=Ud(r);if(N(r))for(let i of tR){let s=r[i];if(N(s))for(let a of Object.keys(s))n.add(`${i}.${a}`)}let o=e[t];e[t]=o?new Set([...o,...n]):n}function Lh(e,t,r=ja(),n={source:"unknown",layer:"unknown"}){if(rn(r.provenance,t,n.source,n.layer),t.version!==void 0)e.version=_e(t.version);if(N(t.project)&&t.project.name!==void 0)e.project.name=G(t.project.name);if(N(t.google)){if(t.google.project_id!==void 0)e.google.project_id=G(t.google.project_id);if(t.google.region!==void 0)e.google.region=G(t.google.region)}if(N(t.profiles))for(let[o,i]of Object.entries(t.profiles)){let s=e.profiles[o];e.profiles[o]=s?rR(s,i):Oa(i)}if(N(t.templates))for(let[o,i]of Object.entries(t.templates)){let s=e.templates[o];e.templates[o]=s?Ha(s,i):ji(i),Na(r.templates,o,i)}if(N(t.services))for(let[o,i]of Object.entries(t.services)){let s=e.services[o];e.services[o]=s?Ha(s,i):ji(i),Na(r.services,o,i)}if(N(t.tasks))for(let[o,i]of Object.entries(t.tasks))e.tasks[o]=e.tasks[o]?nR(e.tasks[o],i):Rh(i);if(N(t.proxy))zd(e.proxy,t.proxy);if(N(t.logs)){if(t.logs.max_memory_events!==void 0)e.logs.max_memory_events=_e(t.logs.max_memory_events);if(N(t.logs.persistence)){let o=t.logs.persistence;if(o.enabled!==void 0)e.logs.persistence.enabled=ot(o.enabled);if(o.directory!==void 0)e.logs.persistence.directory=G(o.directory);if(o.retention_days!==void 0)e.logs.persistence.retention_days=_e(o.retention_days);if(o.max_session_logs!==void 0)e.logs.persistence.max_session_logs=_e(o.max_session_logs)}}if(N(t.auth)&&t.auth.refresh_threshold_seconds!==void 0)e.auth.refresh_threshold_seconds=_e(t.auth.refresh_threshold_seconds);if(N(t.shutdown)){if(t.shutdown.stop_services_on_exit!==void 0)e.shutdown.stop_services_on_exit=ot(t.shutdown.stop_services_on_exit);if(t.shutdown.grace_seconds!==void 0)e.shutdown.grace_seconds=_e(t.shutdown.grace_seconds)}if(N(t.ui)){if(t.ui.theme!==void 0)e.ui.theme=G(t.ui.theme);if(t.ui.keymap!==void 0)e.ui.keymap=Sn(t.ui.keymap)}if(N(t.secrets)){if(t.secrets.extra_markers!==void 0)e.secrets.extra_markers=Rt(t.secrets.extra_markers);if(t.secrets.extra_patterns!==void 0)e.secrets.extra_patterns=Rt(t.secrets.extra_patterns)}if(N(t.doctor)&&Array.isArray(t.doctor.tools))e.doctor.tools=t.doctor.tools.filter(N).map((o)=>({name:G(o.name),command:G(o.command)}));if(Array.isArray(t.plugins))e.plugins=t.plugins.filter(N).map((o)=>({path:G(o.path)})).filter((o)=>o.path!=="");if(N(t.environment)){if(Array.isArray(t.environment.sources))e.environment.sources=Rt(t.environment.sources);if(N(t.environment.secrets))e.environment.secrets={...e.environment.secrets,...Sn(t.environment.secrets)}}}function zd(e,t){if(t.enabled!==void 0)e.enabled=ot(t.enabled);if(t.gateway!==void 0)e.gateway=ot(t.gateway);if(t.credentials!==void 0)e.credentials=G(t.credentials);if(N(t.listen)){if(t.listen.host!==void 0)e.listen.host=G(t.listen.host);if(t.listen.port!==void 0)e.listen.port=_e(t.listen.port)}if(N(t.token_endpoint)){let r=t.token_endpoint;if(r.enabled!==void 0)e.token_endpoint.enabled=ot(r.enabled);if(r.host!==void 0)e.token_endpoint.host=G(r.host);if(r.port!==void 0)e.token_endpoint.port=_e(r.port)}if(Array.isArray(t.routes))e.routes=t.routes.map((r)=>Xn(r))}function rR(e,t){if(!N(t))return e;return{services:t.services!==void 0?Rt(t.services):e.services,environment:t.environment!==void 0?{...e.environment,...Sn(t.environment)}:e.environment}}function nR(e,t){if(!N(t))return e;return{command:t.command!==void 0?Pr(t.command):e.command,shell:t.shell!==void 0?ot(t.shell):e.shell,working_dir:t.working_dir!==void 0?G(t.working_dir):e.working_dir,dependencies:t.dependencies!==void 0?Rt(t.dependencies):e.dependencies,environment:Dh(e.environment,t.environment)}}function Ha(e,t){if(!N(t))return e;let r=Ud(t),n={...e,environment:Dh(e.environment,t.environment),health:sR(e.health,t.health),identity:aR(e.identity,t.identity),logs:cR(e.logs,t.logs),restart:lR(e.restart,t.restart),startup:dR(e.startup,t.startup),container:uR(e.container,t.container),watch:oR(e.watch,t.watch),hooks:iR(e.hooks,t.hooks)};if(r.has("extends"))n.extends=G(t.extends);if(r.has("description"))n.description=G(t.description);if(r.has("command"))n.command=Pr(t.command);if(r.has("shell"))n.shell=ot(t.shell);if(r.has("working_dir"))n.working_dir=G(t.working_dir);if(r.has("dependencies"))n.dependencies=Wd(t.dependencies);if(r.has("ports"))n.ports=Gd(t.ports);if(r.has("capabilities"))n.capabilities=Rt(t.capabilities);if(r.has("proxy"))n.proxy=Vd(t.proxy);if(r.has("expose"))n.expose=qd(t.expose);return n}function oR(e,t){if(!N(t))return e;return{enabled:t.enabled!==void 0?ot(t.enabled):e.enabled,paths:t.paths!==void 0?Rt(t.paths):e.paths,debounce_ms:t.debounce_ms!==void 0?Pa(_e(t.debounce_ms)):e.debounce_ms,ignore:t.ignore!==void 0?Rt(t.ignore):e.ignore}}function iR(e,t){if(!N(t))return e;return{pre_start:t.pre_start!==void 0?Pr(t.pre_start):e.pre_start,post_start:t.post_start!==void 0?Pr(t.post_start):e.post_start}}function Dh(e,t){if(!N(t))return e;let r={...e.vars},n={...e.defaults},o=e.required;for(let[i,s]of Object.entries(t))if(i==="required")o=Rt(s);else if(i==="defaults")Object.assign(n,Sn(s));else r[i]=G(s);return{vars:r,required:o,defaults:n}}function sR(e,t){if(!N(t))return e;return{type:t.type!==void 0?G(t.type):e.type,url:t.url!==void 0?G(t.url):e.url,address:t.address!==void 0?G(t.address):e.address,command:t.command!==void 0?Pr(t.command):e.command,interval_seconds:t.interval_seconds!==void 0?_e(t.interval_seconds):e.interval_seconds,timeout_seconds:t.timeout_seconds!==void 0?_e(t.timeout_seconds):e.timeout_seconds,start_period_seconds:t.start_period_seconds!==void 0?_e(t.start_period_seconds):e.start_period_seconds,unhealthy_threshold:t.unhealthy_threshold!==void 0?_e(t.unhealthy_threshold):e.unhealthy_threshold,healthy_reset_threshold:t.healthy_reset_threshold!==void 0?_e(t.healthy_reset_threshold):e.healthy_reset_threshold}}function aR(e,t){if(!N(t))return e;return{type:t.type!==void 0?G(t.type):e.type,mode:t.mode!==void 0?G(t.mode):e.mode,service_account:t.service_account!==void 0?G(t.service_account):e.service_account,config:t.config!==void 0&&N(t.config)?{...e.config??{},...t.config}:e.config}}function cR(e,t){if(!N(t))return e;return{stdout:t.stdout!==void 0?ot(t.stdout):e.stdout,stderr:t.stderr!==void 0?ot(t.stderr):e.stderr}}function lR(e,t){if(!N(t))return e;return{enabled:t.enabled!==void 0?ot(t.enabled):e.enabled,policy:t.policy!==void 0?G(t.policy):e.policy,max_retries:t.max_retries!==void 0?_e(t.max_retries):e.max_retries,backoff_seconds:t.backoff_seconds!==void 0?_e(t.backoff_seconds):e.backoff_seconds}}function dR(e,t){if(!N(t))return e;return{wait_for_healthy:t.wait_for_healthy!==void 0?ot(t.wait_for_healthy):e.wait_for_healthy,timeout_seconds:t.timeout_seconds!==void 0?_e(t.timeout_seconds):e.timeout_seconds}}function uR(e,t){if(!N(t))return e;let r=Kd(t);if(!r)return e;return{image:t.image!==void 0?r.image:e?.image??"",runtime:t.runtime!==void 0?r.runtime:e?.runtime??"",ports:t.ports!==void 0?{...e?.ports??{},...r.ports}:e?.ports??{},env:t.env!==void 0?{...e?.env??{},...r.env}:e?.env??{},volumes:t.volumes!==void 0?r.volumes:e?.volumes??[]}}function Ih(e,t){let r=Object.keys(e.services).sort();for(let n of r){let o=e.services[n]?.proxy??[];if(o.length===0)continue;o.forEach((i,s)=>{let c={name:o.length===1?n:`${n}-${s+1}`,match:i.match,upstream:i.upstream,auth:i.auth},l=e.proxy.routes.length;if(e.proxy.routes.push(c),t)rn(t,c,`synthesized from services.${n}.proxy`,"synthesized",`proxy.routes.${l}`)})}pR(e,t)}function Mh(e){let t=e.repoRoot||process.cwd(),r=e.proxy.credentials.trim()===""?"":_i(e.proxy.credentials.trim(),t);e.proxy.credentials=r;for(let n of e.proxy.routes){if(n.auth.client_id.trim()==="")continue;let o=(n.auth.credentials??"").trim();n.auth.credentials=o===""?r:_i(o,t)}}function pR(e,t){if(!e.proxy.enabled)return;let r=new Set(e.proxy.routes.map((n)=>n.name));for(let n of Object.keys(e.services).sort()){let o=e.services[n];if(!o)continue;if(!(o.expose.enabled!==!1&&(o.expose.enabled===!0||e.proxy.gateway&&Jn(o.ports,"http")!==void 0))||r.has(n))continue;let a={name:n,match:{host:o.expose.host||`${n}.local`,path:""},upstream:{url:"",service:n,port:o.expose.port||"http"},auth:Fi()},c=e.proxy.routes.length;if(e.proxy.routes.push(a),r.add(n),t){let l=o.expose.enabled===!0?`services.${n}.expose`:"proxy.gateway";rn(t,a,`synthesized from ${l}`,"synthesized",`proxy.routes.${c}`)}}}function Oh(e,t=ja()){if(Object.keys(e.templates).length===0)return;let r={};for(let[n,o]of Object.entries(e.services))r[n]=$h(e,o,t.services[n]??new Set,t,{});e.services=r}function $h(e,t,r,n,o){if(t.extends==="")return t;if(o[t.extends])throw Error(`template cycle involving "${t.extends}"`);let i=e.templates[t.extends]??vn();if(!e.templates[t.extends])throw Error(`service extends unknown template "${t.extends}"`);let s={...o,[t.extends]:!0},a=n.templates[t.extends]??new Set,c=$h(e,i,a,n,s),l=fR(c,t,r);return l.extends=t.extends,l}function fR(e,t,r){let n={...e};if(r.has("extends"))n.extends=t.extends;if(r.has("description"))n.description=t.description;if(r.has("command"))n.command=t.command;if(r.has("shell"))n.shell=t.shell;if(r.has("working_dir"))n.working_dir=t.working_dir;if(r.has("dependencies"))n.dependencies=t.dependencies;if(r.has("ports"))n.ports=t.ports;if(r.has("capabilities"))n.capabilities=t.capabilities;if(r.has("proxy"))n.proxy=t.proxy;if(r.has("container")){let o=t.container,i=e.container;n.container=o&&i?{image:r.has("container.image")?o.image:i.image,runtime:r.has("container.runtime")?o.runtime:i.runtime,ports:r.has("container.ports")?o.ports:i.ports,env:r.has("container.env")?o.env:i.env,volumes:r.has("container.volumes")?o.volumes:i.volumes}:o??i}if(r.has("watch"))n.watch=t.watch;return n.hooks={pre_start:r.has("hooks.pre_start")?t.hooks.pre_start:e.hooks.pre_start,post_start:r.has("hooks.post_start")?t.hooks.post_start:e.hooks.post_start},n.health={type:r.has("health.type")?t.health.type:e.health.type,url:r.has("health.url")?t.health.url:e.health.url,address:r.has("health.address")?t.health.address:e.health.address,command:r.has("health.command")?t.health.command:e.health.command,interval_seconds:r.has("health.interval_seconds")?t.health.interval_seconds:e.health.interval_seconds,timeout_seconds:r.has("health.timeout_seconds")?t.health.timeout_seconds:e.health.timeout_seconds,start_period_seconds:r.has("health.start_period_seconds")?t.health.start_period_seconds:e.health.start_period_seconds,unhealthy_threshold:r.has("health.unhealthy_threshold")?t.health.unhealthy_threshold:e.health.unhealthy_threshold,healthy_reset_threshold:r.has("health.healthy_reset_threshold")?t.health.healthy_reset_threshold:e.health.healthy_reset_threshold},n.identity={type:r.has("identity.type")?t.identity.type:e.identity.type,mode:r.has("identity.mode")?t.identity.mode:e.identity.mode,service_account:r.has("identity.service_account")?t.identity.service_account:e.identity.service_account,config:r.has("identity.config")?t.identity.config:e.identity.config},n.logs={stdout:r.has("logs.stdout")?t.logs.stdout:e.logs.stdout,stderr:r.has("logs.stderr")?t.logs.stderr:e.logs.stderr},n.restart={enabled:r.has("restart.enabled")?t.restart.enabled:e.restart.enabled,policy:r.has("restart.policy")?t.restart.policy:e.restart.policy,max_retries:r.has("restart.max_retries")?t.restart.max_retries:e.restart.max_retries,backoff_seconds:r.has("restart.backoff_seconds")?t.restart.backoff_seconds:e.restart.backoff_seconds},n.startup={wait_for_healthy:r.has("startup.wait_for_healthy")?t.startup.wait_for_healthy:e.startup.wait_for_healthy,timeout_seconds:r.has("startup.timeout_seconds")?t.startup.timeout_seconds:e.startup.timeout_seconds},n.environment={vars:{...e.environment.vars,...t.environment.vars},defaults:{...e.environment.defaults,...t.environment.defaults},required:r.has("environment.required")?t.environment.required:e.environment.required},n}te();ht();function Fh(e){if(e.version===0)throw E(Y,"version is required");if(e.version===Uo)return e;throw E(Y,`unsupported config version ${e.version} (expected ${Uo}); no migration is available`)}var Nh=["version","project","google","profiles","templates","services","tasks","proxy","logs","auth","shutdown","ui","secrets","doctor","plugins","environment"],Hh=["extends","description","command","shell","working_dir","dependencies","ports","environment","health","identity","logs","restart","startup","capabilities","proxy","expose","container","watch","hooks"],jh=["type","url","address","command","interval_seconds","timeout_seconds","start_period_seconds","unhealthy_threshold","healthy_reset_threshold"],Bh=["service","condition"],Uh=["type","mode","service_account","config"],Wh=["enabled","policy","max_retries","backoff_seconds"],Gh=["wait_for_healthy","timeout_seconds"],Kh=["enabled","host","port"],qh=["enabled","gateway","credentials","listen","token_endpoint","routes"],Vh=["host","port"],Yd=["name","match","upstream","auth"],Jd=["host","path"],Xd=["url","service","port"],Qd=["type","identity","audience","service_account","client_id","client_secret","credentials"],zh=["max_memory_events","persistence"],Yh=["enabled","directory","retention_days","max_session_logs"],Jh=["refresh_threshold_seconds"],Xh=["stop_services_on_exit","grace_seconds"],Qh=["theme","keymap"],Zh=["name"],eg=["project_id","region"],tg=["services","environment"],Zd=["required","defaults"],rg=["stdout","stderr"],ng=["extra_markers","extra_patterns"],og=["tools"],ig=["name","command"],sg=["enabled","host","port"],ag=["path"],cg=["sources","secrets"],lg=["image","runtime","ports","env","volumes"],dg=["enabled","paths","debounce_ms","ignore"],ug=["pre_start","post_start"],pg=["command","shell","working_dir","dependencies","environment"],fg=["google","google_api","iap","network","service_identity","local_http"],mg=["|","||","&&",";",">",">>","<","&"];var mR=2;function kn(e,t){if(Array.isArray(e))return e.flatMap((o,i)=>kn(o,hg(t,String(i))));if(!wR(e))return[];let r=gR(t),n=[];for(let o of Object.keys(e)){let i=hg(t,o);if(r.length>0&&!r.includes(o)&&!hR(t))n.push(i);n.push(...kn(e[o],i))}return n}function hR(e){if(e==="services"||e==="profiles"||e==="templates"||e==="tasks")return!0;if(e.endsWith(".environment")||e.endsWith(".defaults")||e.endsWith(".keymap"))return!0;if(e.endsWith(".container.ports")||e.endsWith(".container.env"))return!0;if(e.endsWith(".identity.config")||e.includes(".identity.config."))return!0;return e.includes(".environment.")&&!e.endsWith(".environment")}function gR(e){switch(e){case"":return Nh;case"project":return Zh;case"google":return eg;case"proxy":return qh;case"proxy.listen":return Vh;case"proxy.token_endpoint":return sg;case"logs":return zh;case"logs.persistence":return Yh;case"auth":return Jh;case"shutdown":return Xh;case"ui":return Qh;case"secrets":return ng;case"doctor":return og;case"environment":return cg;default:return vR(e)}}function vR(e){if(e==="services"||e==="profiles"||e==="templates"||e==="tasks")return[];if(e.startsWith("services.")||e.startsWith("templates.")){if(e.includes(".dependencies."))return Bh;return yR(e)}if(e.startsWith("profiles.")){if(e.split(".").length===2)return tg}if(e.startsWith("tasks.")){let t=e.split(".");if(t.length===2)return pg;if(t[2]==="environment")return Zd}if(e.includes("routes"))return xR(e);if(e.startsWith("doctor.tools")&&e.split(".").length>=3)return ig;if(e==="plugins")return[];if(e.startsWith("plugins.")&&e.split(".").length===2)return ag;return[]}function yR(e){let t=e.split(".");if(t.length===2)return Hh;if(t.length>=3)switch(t[2]){case"health":return jh;case"identity":return Uh;case"restart":return Wh;case"startup":return Gh;case"logs":return rg;case"environment":return Zd;case"proxy":return bR(t);case"expose":return Kh;case"container":return lg;case"watch":return dg;case"hooks":return ug;default:return[]}return[]}function bR(e){let t=e.slice(3),r=t[0]!==void 0&&/^\d+$/.test(t[0])?1:0,n=t[r]??"";if(n==="match")return Jd;if(n==="upstream")return Xd;if(n==="auth")return Qd;return Yd}function xR(e){if(e==="proxy.routes")return[];if(e.endsWith(".match"))return Jd;if(e.endsWith(".upstream"))return Xd;if(e.endsWith(".auth"))return Qd;if(e.includes("proxy.routes")&&e.split(".").length===mR+1)return Yd;return[]}function hg(e,t){if(e==="")return t;return`${e}.${t}`}function wR(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}function Wo(e){return`unknown fields: ${e.join(", ")}`}ht();function Cn(e){let t=e.trim().toLowerCase();if(t==="")return!0;if(t==="localhost"||t==="::1")return!0;if(gg(t))return!0;return SR(t)}function gg(e){let t=e.split(".");if(t.length!==4)return!1;let r=t.map((n)=>Number(n));if(r.some((n)=>!Number.isInteger(n)||n<0||n>255))return!1;return r[0]===127}function SR(e){if(!e.startsWith("::ffff:"))return!1;return gg(e.slice(7))}import{existsSync as PR}from"fs";import{isAbsolute as _R,resolve as RR}from"path";import{fileURLToPath as ER}from"url";ht();function vg(e,t){if(t){if(t.http!==void 0)return t.http;let n=Object.values(t)[0];if(n!==void 0)return n}let r=Sh(e.ports);if(r&&!r.auto)return r.value;return}function kR(e,t,r){let n=e,o="";for(;;){let i=n.indexOf("${");if(i<0)return o+n;o+=n.slice(0,i);let s=n.slice(i).indexOf("}");if(s<0)throw Error(`unclosed environment reference in "${e}"`);let a=n.slice(i+2,i+s);o+=CR(a,t,r),n=n.slice(i+s+1)}}function CR(e,t,r){let n=e.split(".");if(n.length<3||n[0]!=="services")throw Error(`unsupported reference \${${e}}`);let o=n[1]??"",i=t.services[o];if(!i)throw Error(`reference \${${e}}: unknown service`);let s=r[o];if(n[2]==="host"||n[2]==="url"){let a=t.proxy.enabled?t.proxy.routes.find((l)=>l.upstream.service===o):void 0;if(n[2]==="host")return a?a.match.host||"127.0.0.1":"127.0.0.1";if(a)return`http://${a.match.host||"127.0.0.1"}:${t.proxy.listen.port}${a.match.path}`;let c=vg(i,s);if(c===void 0)throw Error(`unresolvable reference \${${e}}`);return`http://127.0.0.1:${c}`}if(n[2]==="port"){let a=vg(i,s);if(a!==void 0)return String(a)}if(n[2]==="ports"){let a=n[3];if(!a)throw Error(`reference \${${e}}: missing port name`);if(s&&s[a]!==void 0)return String(s[a]);let c=Jn(i.ports,a);if(c&&!c.auto)return String(c.value);let l=Number.parseInt(a,10);if(!Number.isNaN(l)&&l>=0&&l<i.ports.length){let d=i.ports[l];if(d&&!d.auto)return String(d.value)}}throw Error(`unresolvable reference \${${e}}`)}function eu(e,t,r){let n={};for(let[o,i]of Object.entries(e))n[o]=kR(i,t,r);return n}function yg(e){let t=[],r=e;for(;;){let n=r.indexOf("${");if(n<0)return t;let o=r.slice(n).indexOf("}");if(o<0)return t;t.push(r.slice(n+2,n+o)),r=r.slice(n+o+1)}}function bg(e,t){let r=e.split(".");if(r.length<2)return!1;if(r[0]!=="services"||r.length<3)return!1;let n=t.services[r[1]??""];if(!n)return!1;if(r[2]==="port"||r[2]==="ports"){if(r.length===3)return n.ports.length>0;if(r.length>=4){let o=r[3]??"";if(!Number.isNaN(Number.parseInt(o,10)))return!0;return Jn(n.ports,o)!==void 0}}return!0}ht();var Wa=65535,Ga=1,Pg=["http","tcp","process","command"];function ru(e){let t=[];for(let[r,n]of Object.entries(e.services)){let o=n.health.type;if(o!==""&&!Pg.includes(o.toLowerCase()))t.push({service:r,type:o})}return t}var TR=0,kg=1,Cg=2;function Zn(e){let t=[];if(e.version===0)t.push("version is required");else if(e.version!==Uo)t.push(`unsupported config version ${e.version} (expected ${Uo}); no migration is available`);if(Object.keys(e.services).length===0)t.push("at least one service must be defined");t.push(...LR(e)),t.push(...AR(e)),t.push(...OR(e)),t.push(...$R(e)),t.push(...FR(e));for(let[r,n]of e.plugins.entries())if(n.path==="")t.push(`plugins.${r}.path is required`);else try{let o=n.path.startsWith("file:")?ER(n.path):_R(n.path)?n.path:RR(e.repoRoot,n.path);if(!PR(o))t.push(`plugins.${r}.path does not exist: ${n.path}`)}catch{t.push(`plugins.${r}.path is invalid: ${n.path}`)}if(e.logs.max_memory_events<0)t.push("logs.max_memory_events must be >= 0");return t}function AR(e){let t=[];for(let[r,n]of Object.entries(e.tasks)){let o=`tasks.${r}`;if(yn(n.command))t.push(`${o}.command is required`);t.push(...Ua(o,n.command,n.shell));for(let i of n.dependencies)if(!e.services[i])t.push(`${o}.dependencies: unknown service "${i}"`);t.push(..._g(`${o}.environment`,n.environment,e))}return t}function LR(e){let t=[],r={};for(let[n,o]of Object.entries(e.services)){let i=`services.${n}`;if(yn(o.command)&&!o.container)t.push(`${i}.command is required`);t.push(...Ua(i,o.command,o.shell)),t.push(...Ua(`${i}.hooks.pre_start`,o.hooks.pre_start,o.shell)),t.push(...Ua(`${i}.hooks.post_start`,o.hooks.post_start,o.shell)),t.push(...UR(i,o.capabilities));for(let c of o.dependencies){let l=zt(c);if(!e.services[l])t.push(`${i}.dependencies: unknown service "${l}"`);if(l===n)t.push(`${i}.dependencies: service cannot depend on itself`);if(!["service_started","service_healthy"].includes(gn(c)))t.push(`${i}.dependencies: condition must be service_started or service_healthy`);if(gn(c)==="service_healthy"&&e.services[l]?.health.type==="")t.push(`${i}.dependencies: service_healthy requires ${l} to define a health check`)}if(o.extends!==""&&!e.templates[o.extends])t.push(`${i}.extends: unknown template "${o.extends}"`);for(let c of o.ports.filter((l)=>!l.auto))if(c.value<Ga||c.value>Wa)t.push(`${i}.ports.${c.name}: invalid port ${c.value}`);else if(r[c.value])t.push(`duplicate port ${c.value} used by ${r[c.value]} and ${n}`);else r[c.value]=n;let s=MR(`${i}.identity`,o.identity,e.plugins.length>0);if(s!=="")t.push(s);if(t.push(...DR(i,o,e.plugins.length>0)),o.health.start_period_seconds<0)t.push(`${i}.health.start_period_seconds must be >= 0`);if(o.health.unhealthy_threshold<1)t.push(`${i}.health.unhealthy_threshold must be >= 1`);if(o.health.healthy_reset_threshold<1)t.push(`${i}.health.healthy_reset_threshold must be >= 1`);let a=Ea(o.restart);if(a!==Sa&&a!==ka&&a!==Ca)t.push(`${i}.restart.policy must be never, on_failure, or always`);if(t.push(..._g(`${i}.environment`,o.environment,e)),o.container){if(o.container.image==="")t.push(`${i}.container.image is required`);if(o.container.runtime!==""&&o.container.runtime!=="docker"&&o.container.runtime!=="podman")t.push(`${i}.container.runtime must be docker or podman`);for(let[c,l]of Object.entries(o.container.ports)){if(!o.ports.some((d)=>d.name===c))t.push(`${i}.container.ports.${c}: no matching service port`);if(l<Ga||l>Wa)t.push(`${i}.container.ports.${c}: invalid container port ${l}`)}}}return t}function DR(e,t,r){let n=[];if(t.health.type==="")return n;let o=t.health.type.toLowerCase();if(!Pg.includes(o)&&!r)n.push(`${e}.health.type must be http, tcp, process, or command`);if(o==="http"&&t.health.url==="")n.push(`${e}.health.url is required for http health checks`);if(o==="tcp"&&t.health.address===""&&t.ports.length===0)n.push(`${e}.health.address is required for tcp health checks without ports`);if(o==="command"&&yn({args:t.health.command.args,shell:!1}))n.push(`${e}.health.command is required for command health checks`);return n}var IR=["","user","service","service_account"];function MR(e,t,r){let n=Hi(t).toLowerCase();if(n===""||n==="user")return"";if(n==="service"||n==="service_account"){if(t.service_account==="")return`${e}.service_account is required for service identity`;if(!t.service_account.includes("@"))return`${e}.service_account must be an email`;return""}if(r)return"";return`${e}.type must be user or service_account`}function nu(e){let t=[];for(let[r,n]of Object.entries(e.services)){let o=Hi(n.identity).toLowerCase();if(!IR.includes(o))t.push({service:r,type:o})}return t}function OR(e){let t={},r=[],n=[],o=(i)=>{let s=t[i]??TR;if(s===Cg)return;if(s===kg){r.push(`dependency cycle: ${[...n,i].join(" \u2192 ")}`);return}t[i]=kg,n.push(i);let a=e.services[i];if(a)for(let c of a.dependencies){let l=zt(c);if(e.services[l])o(l)}n.pop(),t[i]=Cg};for(let i of Object.keys(e.services))o(i);return r}function _g(e,t,r){let n=[],o=(i,s)=>{for(let a of yg(s))if(!bg(a,r))n.push(`${e}.${i}: unresolvable reference \${${a}}`)};for(let[i,s]of Object.entries(t.vars))o(i,s);for(let[i,s]of Object.entries(t.defaults))o(i,s);return n}function $R(e){let t=[];for(let[r,n]of Object.entries(e.profiles))for(let o of n.services)if(!e.services[o])t.push(`profiles.${r} references unknown service "${o}"`);return t}function FR(e){let t=[];if(e.proxy.listen.host!==""){if(!WR(e.proxy.listen.host))t.push("proxy.listen.host must be an IP address or localhost");else if(!Cn(e.proxy.listen.host))t.push("proxy.listen.host must be a loopback address")}if(e.proxy.enabled&&e.proxy.listen.port===0)t.push("proxy.listen.port is required when proxy.enabled is true");if(e.proxy.listen.port!==0&&(e.proxy.listen.port<Ga||e.proxy.listen.port>Wa))t.push("proxy.listen.port is invalid");let r={};return e.proxy.routes.forEach((n,o)=>{let i=`proxy.routes[${o}]`;if(n.name==="")t.push(`${i}.name is required`);else if(r[n.name])t.push(`${i}: duplicate route name ${n.name}`);r[n.name]=!0,t.push(...NR(n,i,e)),t.push(...HR(n,i))}),t.push(...BR(e)),t}function NR(e,t,r){let n=[],o=e.upstream.url!=="",i=e.upstream.service??"";if(i===""){if(!o)n.push(`${t}.upstream requires either url or service`);return n}let s=r.services[i];if(!s)return n.push(`${t}.upstream.service references unknown service ${i}`),n;let a=e.upstream.port||"http";if(Jn(s.ports,a)===void 0)n.push(`${t}.upstream: service ${i} has no port named ${a}`);return n}function HR(e,t){let r=[];if(e.auth.type.toLowerCase()==="iap"){if(e.auth.audience.trim()==="")r.push(`${t}.auth.audience is required when auth.type is iap`);if(e.auth.identity.type.trim()==="")r.push(`${t}.auth.identity.type is required when auth.type is iap`)}r.push(...jR(e.auth,t));let n=e.auth.identity.type.toLowerCase();if(n==="service"||n==="service_account"||en({type:n,mode:"",service_account:""})){if((e.auth.identity.service_account||e.auth.service_account)==="")r.push(`${t}.auth.identity.service_account is required`)}return r}function jR(e,t){let r=(e.client_id??"").trim(),n=(e.client_secret??"").trim(),o=(e.credentials??"").trim();if(r===""&&n===""&&o==="")return[];if(e.type.toLowerCase()!=="iap")return[`${t}.auth.${r!==""?"client_id":n!==""?"client_secret":"credentials"} is only valid when auth.type is iap`];let i=[];if(r===""){if(n!=="")i.push(`${t}.auth.client_id is required when client_secret is set`);if(o!=="")i.push(`${t}.auth.credentials requires auth.client_id`);return i}if(n===""&&o==="")i.push(`${t}.auth.client_secret is required when client_id is set`);else if(n!==""&&tu(n).length>0&&!Sg(n))i.push(`${t}.auth.client_secret must be a literal or a single \${NAME} / \${env.NAME} reference`);let s=e.identity.type.toLowerCase();if(s==="service"||s==="service_account")i.push(`${t}.auth.client_id is only valid with identity.type user`);return i}function BR(e){let t=[],r=e.proxy.token_endpoint;if(!r.enabled)return t;let n=r.host||_a;if(!Cn(n))t.push("proxy.token_endpoint.host must be a loopback address");if(r.port!==0&&(r.port<Ga||r.port>Wa))t.push("proxy.token_endpoint.port is invalid");return t}function Ua(e,t,r){if(t.shell||r)return[];for(let n of t.args)if(mg.includes(n)||n.includes("|")||n.includes(";")||n.includes("&&"))return[`${e}.command contains shell metacharacters; set shell: true to run via a shell`];return[]}function UR(e,t){let r=[];for(let n of t)if(!fg.includes(n))r.push(`${e}.capabilities: unknown capability "${n}"`);return r}function WR(e){if(e==="localhost")return!0;return GR(e)}function GR(e){if(/^(\d{1,3}\.){3}\d{1,3}$/.test(e))return e.split(".").every((r)=>{let n=Number(r);return n>=0&&n<=255});return e.includes(":")}function Ui(e,t,r){try{return Wi(e,t,{candidateText:r}),[]}catch(n){if(n instanceof Ve){let o=`${n.kind}: `;return[n.message.startsWith(o)?n.message.slice(o.length):n.message]}return[n instanceof Error?n.message:String(n)]}}function nn(e,t){let{repoRoot:r,configPath:n}=Qn(e,t);return Wi(r,n)}function to(e,t){try{return nn(e,t)}catch(r){if(!ba(r,qn))throw r;let n=e===""?process.cwd():YR(e),o=Yn();return o.repoRoot=n,o.configPath=eo(n,Nt,lr),o}}function Wi(e,t,r){let n=Yn(),o=ja();Tg(t,n,o,r?.candidateText,"main"),n.repoRoot=e,n.configPath=t;let i=VR(t);if(Eg(i)===Nt)QR(i,n,o);n=XR(n,e,t,o),n=Fh(n);try{Oh(n,o)}catch(a){throw Pe(Y,"template merge failed",a)}Ih(n,o.provenance),Mh(n),n.provenance=o.provenance;let s=Zn(n);if(s.length>0)throw E(Y,s.join(`
2242
+ `));return n}function XR(e,t,r,n){let o=eo(cr(),"config.local.yaml"),i=eo(t,Nt,"config.local.yaml");for(let s of[o,i]){if(s===r||!Bi(s))continue;Tg(s,e,n,void 0,s===o?"home_local":"repo_local")}return e}function Tg(e,t,r,n,o="main"){let i=n!==void 0?Ag(n,e):ou(e),s=kn(i,"");if(s.length>0)throw E(Y,Wo(s));Lh(t,i,r,{source:e,layer:o})}function QR(e,t,r){Rg(eo(e,"services"),(o,i,s)=>{let a=kn(i,`services.${o}`);if(a.length>0)throw E(Y,Wo(a));let c=t.services[o];t.services[o]=c?Ha(c,i):ji(i),Na(r.services,o,i),rn(r.provenance,i,s,"modular_service",`services.${o}`)}),Rg(eo(e,"profiles"),(o,i,s)=>{let a=kn(i,`profiles.${o}`);if(a.length>0)throw E(Y,Wo(a));t.profiles[o]=Oa(i),rn(r.provenance,i,s,"modular_profile",`profiles.${o}`)});let n=eo(e,"proxy","routes.yaml");if(Bi(n))ZR(n,t,r)}function ZR(e,t,r){let n=ou(e);if(N(n.proxy))rn(r.provenance,n.proxy,e,"modular_proxy","proxy");if(Array.isArray(n.routes))rn(r.provenance,n.routes,e,"modular_proxy","proxy.routes");if(N(n.proxy)){let o=kn({proxy:n.proxy},"");if(o.length>0)throw E(Y,Wo(o))}if(Array.isArray(n.routes))n.routes.forEach((o,i)=>{let s=kn(o,`proxy.routes.${i}`);if(s.length>0)throw E(Y,Wo(s))});if(N(n.proxy)){let o={...n.proxy};if(delete o.routes,zd(t.proxy,o),Array.isArray(n.proxy.routes))t.proxy.routes.push(...n.proxy.routes.map((i)=>Xn(i)))}if(Array.isArray(n.routes))t.proxy.routes.push(...n.routes.map((o)=>Xn(o)))}function Rg(e,t){let r;try{r=KR(e)}catch(n){if(eE(n))return;throw n}for(let n of r.sort()){let o=zR(n);if(o!==".yaml"&&o!==".yml")continue;let i=eo(e,n),s=ou(i);t(Eg(n,o),s,i)}}function ou(e){let t;try{t=qR(e,"utf8")}catch(r){throw Pe(Y,"unable to read config",r)}return Ag(t,e)}function Ag(e,t){let r;try{r=JR(e)}catch(n){throw Pe(Y,`invalid YAML in ${t}`,n)}if(r===null||r===void 0)return{};if(!N(r))throw E(Y,`unable to decode config: ${t} is not a mapping`);return r}function eE(e){return typeof e==="object"&&e!==null&&"code"in e&&e.code==="ENOENT"}ht();te();jr();import{existsSync as Va,readFileSync as rE}from"fs";import{homedir as Og}from"os";import{join as cu}from"path";var{spawn:lu}=globalThis.Bun;var nE="https://www.googleapis.com/auth/cloud-platform",$g=1500,oE=2000;function qo(){let e=globalThis;if(e.window&&typeof e.window.fetch!=="function")e.window.fetch=fetch}function du(){let e=process.env.GOOGLE_APPLICATION_CREDENTIALS||cu(Og(),".config","gcloud","application_default_credentials.json");if(!Va(e))return"";try{let t=JSON.parse(rE(e,"utf8"));return typeof t.quota_project_id==="string"?t.quota_project_id:""}catch{return""}}async function io(e){let t={gcloudInstalled:await uu("gcloud"),adcAvailable:!1,userEmail:"",projectID:"",projectSource:""};if(e!=="")t.projectID=e,t.projectSource="configuration";else{let r=lE("GOOGLE_CLOUD_PROJECT","GCLOUD_PROJECT","GOOGLE_PROJECT");if(r!=="")t.projectID=r,t.projectSource="environment variable";else if(t.gcloudInstalled){let n=await Mg("core/project");if(n!=="")t.projectID=n,t.projectSource="gcloud configuration"}}if(Ki())try{await Bg(iE(t),oE)}catch(r){t.adcAvailable=!1,t.error=Vo(r)}if(t.userEmail===""&&t.gcloudInstalled)t.userEmail=await Mg("core/account");return t}async function iE(e){qo();let{GoogleAuth:t}=await import("google-auth-library"),r=new t({scopes:[nE]});await r.getClient(),e.adcAvailable=!0;let n=await r.getProjectId().catch(()=>"");if(e.projectID===""&&n)e.projectID=n,e.projectSource="application default credentials";let o=await sE(r);if(o!=="")e.userEmail=o}async function sE(e){try{let t=await e.getCredentials();if(t.client_email)return t.client_email}catch{}try{let t=await e.getClient(),r=typeof t.credentials?.id_token==="string"?t.credentials.id_token:"";if(r!==""){let n=aE(r);if(n!=="")return n}}catch{return""}return""}function aE(e){let t=e.split(".");if(t.length<2||!t[1])return"";try{let r=JSON.parse(Buffer.from(t[1],"base64url").toString("utf8"));return typeof r.email==="string"?r.email:""}catch{return""}}async function Fg(e){let t=await io(e);return dr({kind:"user",email:t.userEmail,project:t.projectID,projectSource:t.projectSource,adcAvailable:t.adcAvailable})}async function Ng(){if(await lu({cmd:["gcloud","auth","application-default","login"],stdin:"inherit",stdout:"inherit",stderr:"inherit"}).exited!==0)throw Te(hn,"application-default login failed","run `gcloud auth application-default login`")}async function Hg(){if(await lu({cmd:["gcloud","auth","application-default","revoke","--quiet"],stdout:"ignore",stderr:"pipe"}).exited!==0)throw Te(hn,"failed to revoke application-default credentials","run `gcloud auth application-default revoke`")}function Vo(e){let t=cE(e).toLowerCase();if(oo(t,["unauthorized_client"]))return Te(ya,"IAP OAuth client does not match the ADC refresh token","omit client_id to use ADC's default client, or login with an OAuth client that matches auth.client_id");if(oo(t,["unauth","invalid_grant","token has been expired","expired"]))return Te(hn,"credential expired or invalid","run `devctl auth login` or `gcloud auth application-default login`");if(oo(t,["api has not been used","has not been enabled","access not configured","service_disabled"]))return Te(Vn,"required Google API is not enabled","enable the API in the target service account project; devctl will not enable APIs automatically");if(oo(t,["permission","forbidden","iam"])){if(oo(t,["serviceaccounttoken","iamcredentials","token creator","getaccesstoken"]))return Te(va,"cannot impersonate service account","ask an administrator to grant roles/iam.serviceAccountTokenCreator on the target service account");return Te(Vn,"authorization failure","verify IAM permissions for the current user on this project")}if(t.includes("audience"))return Te(ya,"IAP audience is incorrect","set auth.audience on the proxy route to the IAP OAuth client ID");if(t.includes("iap"))return Pe(ya,"IAP authentication failure",e);if(t.includes("project"))return Te(Y,"wrong or missing Google project","set google.project_id in .devctl/config.yaml");if(oo(t,["timeout","network","connection refused","no such host"]))return Pe(lt,"network problem reaching Google Cloud",e);if(oo(t,["adc","default credentials","could not find"]))return Te(hn,"application default credentials unavailable","run `gcloud auth application-default login`");return Pe(hn,"Google authentication failed",e)}function cE(e){let t=[],r=new Set,n=(o,i)=>{if(o===null||o===void 0||i>8||r.has(o))return;if(typeof o==="string"){t.push(o);return}if(typeof o!=="object")return;r.add(o);let s=o;for(let a of["message","reason","status","code","error_description","permission","service"])n(s[a],i+1);for(let a of["error","errors","details","response","data","cause","metadata"]){let c=s[a];if(Array.isArray(c))c.forEach((l)=>n(l,i+1));else n(c,i+1)}};return n(e,0),t.length>0?t.join(" "):String(e)}function oo(e,t){return t.some((r)=>e.includes(r))}function lE(...e){for(let t of e){let r=process.env[t];if(r)return r}return""}async function uu(e){try{return(await jg(process.platform==="win32"?["where",e]:["which",e],$g)).code===0}catch{return!1}}function Ki(){let e=process.env.GOOGLE_APPLICATION_CREDENTIALS;if(e&&Va(e))return!0;if(Va(cu(Og(),".config","gcloud","application_default_credentials.json")))return!0;let t=process.env.APPDATA;return Boolean(t&&Va(cu(t,"gcloud","application_default_credentials.json")))}async function Mg(e){try{let t=await jg(["gcloud","config","get-value",e],$g);if(t.code!==0)return"";let r=t.stdout.trim();if(r===""||r==="(unset)")return"";return r}catch{return""}}async function jg(e,t){let r=lu({cmd:e,stdout:"pipe",stderr:"ignore",env:{...process.env,CLOUDSDK_CORE_DISABLE_PROMPTS:"1",CLOUDSDK_CORE_DISABLE_USAGE_REPORTING:"true"}}),n=r.stdout?new Response(r.stdout).text():Promise.resolve("");try{let[o,i]=await Bg(Promise.all([n,r.exited]),t);return{code:i??1,stdout:o}}catch{return r.kill(),{code:1,stdout:""}}}function Bg(e,t){return new Promise((r,n)=>{let o=setTimeout(()=>n(Error("timeout")),t);e.then((i)=>{clearTimeout(o),r(i)},(i)=>{clearTimeout(o),n(i)})})}import{createHash as PE}from"crypto";import{readFileSync as _E}from"fs";import{join as RE}from"path";import{GoogleAuth as Vi,Impersonated as EE,UserRefreshClient as TE}from"google-auth-library";var{spawn:hu}=globalThis.Bun;import{existsSync as Ya,readdirSync as dE,readFileSync as Ug,unlinkSync as uE}from"fs";import{join as qi}from"path";var gu="keychain-index.json",so="devctl.credentials",fu=".cred.json";function mu(e){return qi(kr(),`${Xa(e)}${fu}`)}function Ja(e){if(e==="file"||process.env.DEVCTL_HOME&&e!=="keychain")return new za;if(e==="keychain"||process.platform==="darwin"||process.platform==="win32"||process.platform==="linux")return new Wg(new za);return new za}class za{backend="file";async get(e){let t=hE(e);if(!t||t.accessToken==="")return;return t}async set(e,t){Cr(mu(e),`${JSON.stringify({...mE(t),key:e})}
2243
+ `)}async delete(e){let t=mu(e);if(Ya(t))uE(t)}async list(){let e=kr();if(!Ya(e))return[];let t=[];for(let r of dE(e)){if(!r.endsWith(fu))continue;let n=r.slice(0,-fu.length),o=Gg(qi(kr(),r));if(!o)continue;t.push(Kg(o.key??n,o.record))}return t.sort((r,n)=>r.identity.localeCompare(n.identity))}}class Wg{backend="keychain";fallback;constructor(e){this.fallback=e}async get(e){let t=await gE(e);if(t)return t;return this.fallback.get(e)}async set(e,t){if(!await vE(e,t))await this.fallback.set(e,t);pE(e)}async delete(e){await yE(e),await this.fallback.delete(e),fE(e)}async list(){let e=new Set(vu()),t=await this.fallback.list(),r=new Map;for(let n of t)r.set(n.key,n);for(let n of e){let o=await this.get(n);if(o)r.set(n,Kg(n,o))}return[...r.values()].sort((n,o)=>n.identity.localeCompare(o.identity))}}function pE(e){let t=vu();if(!t.includes(e))Cr(qi(kr(),gu),`${JSON.stringify([...t,e])}
2244
+ `)}function fE(e){let t=vu(),r=t.filter((n)=>n!==e);if(r.length!==t.length)Cr(qi(kr(),gu),`${JSON.stringify(r)}
2245
+ `)}function vu(){let e=qi(kr(),gu);if(!Ya(e))return[];try{let t=JSON.parse(Ug(e,"utf8"));return Array.isArray(t)?t.filter((r)=>typeof r==="string"):[]}catch{return[]}}function mE(e){return{...e,accessToken:""}}function hE(e){return Gg(mu(e))?.record}function Gg(e){if(!Ya(e))return;try{let t=JSON.parse(Ug(e,"utf8"));if(typeof t.identity!=="string")return;return{key:typeof t.key==="string"&&t.key!==""?t.key:void 0,record:{identity:t.identity,audience:typeof t.audience==="string"?t.audience:"",scopes:Array.isArray(t.scopes)?t.scopes.filter((r)=>typeof r==="string"):[],accessToken:typeof t.accessToken==="string"?t.accessToken:"",tokenType:typeof t.tokenType==="string"?t.tokenType:"Bearer",expiresAt:typeof t.expiresAt==="string"?t.expiresAt:""}}}catch{return}}function Kg(e,t){let r=Date.parse(t.expiresAt);return{key:e,identity:t.identity,audience:t.audience,scopes:t.scopes,expires_at:t.expiresAt,valid:Number.isFinite(r)&&r-Date.now()>0}}function Xa(e){return e.replace(/[^A-Za-z0-9._-]+/g,"_").slice(0,80)||"token"}async function gE(e){let t=await bE(e);if(!t)return;try{return JSON.parse(t)}catch{return}}async function vE(e,t){let r=JSON.stringify(t);if(process.platform==="darwin")return await zo(["security","delete-generic-password","-s",so,"-a",e]),await zo(["security","add-generic-password","-s",so,"-a",e,"-w",r,"-U"])===0;if(process.platform==="linux")return await xE(["secret-tool","store","--label",`devctl ${e}`,"service",so,"account",e],r)===0;if(process.platform==="win32"){let o=`Add-Type -AssemblyName System.Security; $bytes = [Convert]::FromBase64String('${Buffer.from(r,"utf8").toString("base64")}'); $enc = [System.Security.Cryptography.ProtectedData]::Protect($bytes, $null, 'CurrentUser'); [IO.File]::WriteAllBytes($env:LOCALAPPDATA + '\\devctl-cred-${Xa(e)}.bin', $enc)`;return await zo(["powershell","-NoProfile","-Command",o])===0}return!1}async function yE(e){if(process.platform==="darwin"){await zo(["security","delete-generic-password","-s",so,"-a",e]);return}if(process.platform==="linux"){await zo(["secret-tool","clear","service",so,"account",e]);return}if(process.platform==="win32")await zo(["powershell","-NoProfile","-Command",`Remove-Item -ErrorAction SilentlyContinue ($env:LOCALAPPDATA + '\\devctl-cred-${Xa(e)}.bin')`])}async function bE(e){if(process.platform==="darwin"){let t=await pu(["security","find-generic-password","-s",so,"-a",e,"-w"]);return t.code===0&&t.stdout!==""?t.stdout:void 0}if(process.platform==="linux"){let t=await pu(["secret-tool","lookup","service",so,"account",e]);return t.code===0&&t.stdout!==""?t.stdout:void 0}if(process.platform==="win32"){let t=`$p = $env:LOCALAPPDATA + '\\devctl-cred-${Xa(e)}.bin'; if (-not (Test-Path $p)) { exit 1 }; Add-Type -AssemblyName System.Security; $raw = [IO.File]::ReadAllBytes($p); $dec = [System.Security.Cryptography.ProtectedData]::Unprotect($raw, $null, 'CurrentUser'); [Text.Encoding]::UTF8.GetString($dec)`,r=await pu(["powershell","-NoProfile","-Command",t]);return r.code===0&&r.stdout!==""?r.stdout:void 0}return}var yu=3000;async function zo(e){try{return await hu({cmd:e,stdout:"ignore",stderr:"ignore",timeout:yu}).exited}catch{return 1}}async function xE(e,t){try{return await hu({cmd:e,stdin:new TextEncoder().encode(t),stdout:"ignore",stderr:"ignore",timeout:yu}).exited}catch{return 1}}async function pu(e){try{let t=hu({cmd:e,stdout:"pipe",stderr:"ignore",timeout:yu}),r=t.stdout?await new Response(t.stdout).text():"";return{code:await t.exited,stdout:r.trim()}}catch{return{code:1,stdout:""}}}te();te();var wE=3,SE=200;async function qg(e,t={}){let r=t.attempts??wE,n=t.backoffMs??SE,o=t.retry??kE,i=Error("retry exhausted");for(let s=0;s<r;s+=1)try{return await e()}catch(a){if(i=a,!o(a)||s===r-1)throw a;await CE(n*(s+1))}throw i}function kE(e){if(e instanceof Ve)return e.kind!==Y&&e.kind!==Vn;return!0}function CE(e){return new Promise((t)=>{setTimeout(t,e)})}var Qa={now:()=>new Date,isoNow:()=>new Date().toISOString(),unixMs:()=>Date.now()};var bu=300000,AE=3000000,LE=3,DE=200,Za="https://www.googleapis.com/auth/cloud-platform",zg="https://www.googleapis.com/auth/userinfo.email";function Vg(e,t,r,n){let o=n??"",i=`${e}|${t}|${r.join(",")}`;return o===""?i:`${i}|oauth:${o}`}function IE(e){let t;try{t=_E(e,"utf8")}catch{throw E(Y,`IAP credentials file not found: ${e}`)}let r;try{r=JSON.parse(t)}catch{throw E(Y,`IAP credentials file is not valid JSON: ${e}`)}if(typeof r!=="object"||r===null)throw E(Y,`IAP credentials file is malformed: ${e}`);let n=r,o=typeof n.refresh_token==="string"?n.refresh_token:"";if(o==="")throw E(Y,`IAP credentials file has no refresh_token: ${e}`);let i=typeof n.client_id==="string"?n.client_id:"";if(i==="")throw E(Y,`IAP credentials file has no client_id: ${e}`);return{clientId:i,clientSecret:typeof n.client_secret==="string"?n.client_secret:"",refreshToken:o}}function ME(e,t=process.env){let r=(e.client_id??"").trim();if(r==="")return;let n=(e.client_secret??"").trim(),{value:o,missing:i}=wg(n,t),s=(e.credentials??"").trim();if(s!==""){let a=IE(s);if(a.clientId!==r)throw E(Y,`IAP credentials file ${s} is for client_id ${a.clientId}, not the route's ${r}`);let c=o!==""?o:a.clientSecret;if(c==="")throw E(Y,`IAP client_id ${r} requires a client_secret (in the route or ${s})`);return{clientId:r,clientSecret:c,refreshToken:a.refreshToken}}if(o===""){let a=i[0];throw E(Y,a?`IAP client_secret env ${a} is empty`:`IAP client_id ${r} requires client_secret`)}return{clientId:r,clientSecret:o}}function ec(e,t=process.env){let r=(e.client_id??"").trim();if(r==="")return;return{clientId:r,resolve:()=>{let n=ME(e,t);if(!n)throw E(Y,`IAP client_id ${r} requires client_secret`);return n}}}function OE(e,t=bu,r=Date.now()){if(e.accessToken==="")return!1;return e.expiresAt.getTime()-r>=t}function $E(e,t=bu,r=Date.now()){if(e.accessToken==="")return!0;let n=e.expiresAt.getTime()-r;return n>0&&n<t}class ao{cache=new Map;inflight=new Map;providers;thresholdMs;bus;store;clock;constructor(e,t,r,n,o=Qa){this.thresholdMs=e>0?e:bu,this.providers=[...t],this.bus=r,this.store=n??Ja(process.env.DEVCTL_CREDENTIAL_BACKEND==="file"?"file":void 0),this.clock=o}replaceProviders(e){this.providers.splice(0,this.providers.length,...e)}isValid(e){return OE(e,this.thresholdMs,this.clock.unixMs())}expiresSoon(e){return $E(e,this.thresholdMs,this.clock.unixMs())}invalidate(e){if(e===void 0||e===""){this.cache.clear(),this.clearStore();return}this.cache.delete(e),this.store.delete(e)}async listStatus(){return this.store.list()}storeBackend(){return this.store.backend}async get(e,t,r,n){let o=Vg(e,t,r,n?.clientId),i=this.cache.get(o)??await this.loadStored(o);if(i&&this.isValid(i))return this.cache.set(o,i),i;return this.refresh(e,t,r,n)}async refresh(e,t,r,n){let o=Vg(e,t,r,n?.clientId),i=this.inflight.get(o);if(i)return i;let s=this.refreshOnce(o,e,t,r,n);this.inflight.set(o,s);try{return await s}finally{this.inflight.delete(o)}}async refreshOnce(e,t,r,n,o){let i=o?.resolve(),s=this.providers.filter((c)=>!c.accepts||c.accepts(t,r,n,i));if(s.length===0)throw E(Fr,`no token provider accepts identity ${t}`);let a=E(Fr,"no token provider available");for(let c of s)try{let l=await qg(()=>c.fetch(t,r,n,i),{attempts:LE,backoffMs:DE,retry:XE});return this.cache.set(e,l),NE(e,l),await this.store.set(e,HE(l)),this.bus?.publish(Se(Mo,"",{identity:l.identity,audience:l.audience})),l}catch(l){a=l instanceof Error?l:Error(String(l))}throw this.bus?.publish(Se(Gn,"",{identity:t,audience:r,error:L(a)})),a}async loadStored(e){let t=await this.store.get(e);if(!t||t.accessToken==="")return;return jE(t)}async clearStore(){let e=await this.store.list();await Promise.all(e.map((t)=>this.store.delete(t.key)))}}function FE(e){let t=PE("sha256").update(e).digest("hex").slice(0,16);return RE(kr(),`${t}.json`)}function NE(e,t){let r={identity:t.identity,audience:t.audience,expires_at:t.expiresAt.toISOString(),scopes:t.scopes};Cr(FE(e),`${JSON.stringify(r)}
2246
+ `)}function HE(e){return{identity:e.identity,audience:e.audience,scopes:e.scopes,accessToken:e.accessToken,tokenType:e.tokenType,expiresAt:e.expiresAt.toISOString()}}function jE(e){return{accessToken:e.accessToken,tokenType:e.tokenType||"Bearer",expiresAt:new Date(e.expiresAt),audience:e.audience,identity:e.identity,scopes:e.scopes}}function _n(){return[WE(),UE(),BE()]}function BE(){return{name:"user",accepts:(e,t)=>!e.startsWith("sa:")&&t==="",fetch:async(e,t,r)=>{if(e.startsWith("sa:"))throw E(Fr,"user provider cannot mint service-account tokens");return zE(e,t,r)}}}function UE(){return{name:"service_account",accepts:(e,t)=>e.startsWith("sa:")&&t==="",fetch:async(e,t,r)=>{if(!e.startsWith("sa:"))throw E(Fr,"service-account provider cannot mint user tokens");return GE(e,t,r)}}}function WE(){return{name:"iap",accepts:(e,t)=>t!=="",fetch:async(e,t,r,n)=>{if(t==="")throw E(Fr,"IAP audience is required");if(e.startsWith("sa:"))return KE(e,t,r);return qE(e,t,r,n)}}}async function GE(e,t,r){let n=e.slice(3);try{qo();let i=await new Vi({scopes:r.length>0?r:[Za]}).getClient(),s=new EE({sourceClient:i,targetPrincipal:n,targetScopes:r.length>0?r:[Za],lifetime:3600}),a=await s.getAccessToken(),c=typeof a==="string"?a:a?.token;if(!c)throw E(Fr,"empty impersonated token");return{accessToken:c,tokenType:"Bearer",expiresAt:zi(c,s),audience:t,identity:e,scopes:r}}catch(o){throw Vo(o)}}async function KE(e,t,r){let n=e.slice(3);try{qo();let i=await new Vi({scopes:[Za]}).getClient(),s=`https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/${encodeURIComponent(n)}:generateIdToken`,c=(await i.request({url:s,method:"POST",data:{audience:t,includeEmail:!0}})).data.token;if(!c)throw E(Fr,"empty impersonated IAP id token");return{accessToken:c,tokenType:"Bearer",expiresAt:zi(c),audience:t,identity:e,scopes:r}}catch(o){throw Vo(o)}}async function qE(e,t,r,n){try{if(qo(),n)return await VE(e,t,r,n);let s=await(await new Vi({scopes:r.length>0?r:[zg]}).getIdTokenClient(t)).idTokenProvider.fetchIdToken(t);return{accessToken:s,tokenType:"Bearer",expiresAt:zi(s),audience:t,identity:e,scopes:r}}catch(o){if(o instanceof Ve)throw o;throw Vo(o)}}async function VE(e,t,r,n){let o=n.refreshToken;if(!o){if(o=(await new Vi({scopes:r.length>0?r:[zg]}).getClient()).credentials.refresh_token??void 0,!o)throw Te(hn,"ADC has no refresh token for a custom IAP OAuth client","set auth.credentials (or proxy.credentials) to an authorized_user file whose refresh_token was issued by this client_id, or run `gcloud auth application-default login` with that client")}let s=await new TE({clientId:n.clientId,clientSecret:n.clientSecret,refreshToken:o}).fetchIdToken(t);if(!s)throw E(Fr,"empty IAP id token");return{accessToken:s,tokenType:"Bearer",expiresAt:zi(s),audience:t,identity:e,scopes:r}}async function zE(e,t,r){try{qo();let o=await new Vi({scopes:r.length>0?r:[Za]}).getClient(),i=await o.getAccessToken();if(!i.token)throw E(Fr,"empty access token");return{accessToken:i.token,tokenType:"Bearer",expiresAt:YE(o,i.token),audience:t,identity:e,scopes:r}}catch(n){throw Vo(n)}}function zi(e,t){if(t?.credentials?.expiry_date)return new Date(t.credentials.expiry_date);return JE(e)??new Date(Date.now()+AE)}function YE(e,t){return zi(t,e)}function JE(e){let t=e.split(".");if(t.length<2||!t[1])return;try{let r=JSON.parse(Buffer.from(t[1],"base64url").toString("utf8"));if(typeof r.exp==="number")return new Date(r.exp*1000)}catch{return}return}function XE(e){if(e instanceof Ve)return e.kind!==Y&&e.kind!==Vn;let t=e instanceof Error?e.message.toLowerCase():String(e).toLowerCase();return t.includes("network")||t.includes("econnreset")||t.includes("etimedout")||t.includes("503")||t.includes("429")}te();jr();var Ji=4000,QE=8000;function Yo(e){let t=e?.tokens,r=()=>(t??=new ao(60000,_n(),void 0,Ja("file")),t);return{detectGoogle:io,hasCommand:uu,portAvailable:jo,hasLocalAdc:Ki,adcQuotaProject:du,mintToken:async(n,o,i)=>{await r().get(n,o,[],i)},probeServiceUsage:async(n,o)=>{let i=await Xi(r().get("user","",[]),Ji),s=`https://serviceusage.googleapis.com/v1/projects/${n}/services/${o}`,a=await fetch(s,{headers:{Authorization:`Bearer ${i.accessToken}`},signal:AbortSignal.timeout(Ji)});if(!a.ok)return!1;return(await a.json().catch(()=>{return}))?.state==="ENABLED"},containerRuntimeAvailable:async(n)=>{try{return await Bun.spawn({cmd:[n,"info"],stdout:"ignore",stderr:"ignore",stdin:"ignore"}).exited===0}catch{return!1}}}}var tc=Yo();function rc(e=Yo()){return{run:(t,r,n)=>ZE(t,e,r,n)}}async function ZE(e,t=tc,r,n){let o={checks:[],issues:0},i="Preparing diagnostics",s=()=>r?.({active:i,checks:[...o.checks]}),a=(p)=>{i=p,s()},c=(p)=>{if(o.checks.push(p),p.severity==="error")o.issues+=1;s()},l=[...new Set(Object.values(e.services).flatMap((p)=>p.container?[p.container.runtime||"docker"]:[]))];for(let p of l){a(`${p} container runtime`);let m=await t.hasCommand(p),h=m&&await(t.containerRuntimeAvailable??tc.containerRuntimeAvailable)(p);c(h?{name:`${p} container runtime`,severity:"ok",message:`${p} daemon reachable`}:{name:`${p} container runtime`,severity:"error",message:m?`${p} daemon is not reachable`:`${p} not found`,hint:`install and start ${p}`})}if(a("Google CLI installed"),await t.hasCommand("gcloud"))c({name:"Google CLI installed",severity:"ok",message:"gcloud found"});else c({name:"Google CLI installed",severity:Gi(e)?"error":"warn",message:"gcloud not installed",hint:"install the Google Cloud CLI from https://cloud.google.com/sdk/docs/install"});a("Google environment");let d=await t.detectGoogle(e.google.project_id);if(d.adcAvailable)c({name:"Google authentication available",severity:"ok",message:"Application Default Credentials found"});else c({name:"Google authentication available",severity:Gi(e)?"error":"warn",message:"ADC unavailable",hint:"run `gcloud auth application-default login`"});if(d.projectID!=="")c({name:"Project configured",severity:"ok",message:`${d.projectID} (source: ${d.projectSource})`});else c({name:"Project configured",severity:Gi(e)?"error":"warn",message:"no Google project configured",hint:"set google.project_id in .devctl/config.yaml"});if(Gi(e)||Object.values(e.services).some((p)=>p.capabilities.includes("google")||p.identity.type!=="")){a("Live Google access");let p=!0,m=(h)=>{if(p)c(h)};try{await Xi((async()=>{await tT(e,m,t,d.userEmail),await eT(e,m,t)})(),t.liveDeadlineMs??QE)}catch(h){m({name:"Live Google probes",severity:"warn",message:L(h),hint:"network or timeout \u2014 retry when online"})}finally{p=!1}}for(let p of e.doctor.tools){let m=p.command||p.name;if(a(`${p.name} installed`),await t.hasCommand(m))c({name:`${p.name} installed`,severity:"ok",message:`${m} found`});else c({name:`${p.name} installed`,severity:"error",message:`${m} not found`,hint:`install ${p.name} and ensure it is on PATH`})}if(a("Repository configuration"),n?.repositoryConfigError!==void 0)if(n.repositoryConfigError==="")c({name:"Repository configuration",severity:"ok",message:"valid"});else c({name:"Repository configuration",severity:"error",message:n.repositoryConfigError});else try{let p=Zn(e);if(p.length>0)c({name:"Repository configuration",severity:"error",message:p.join("; ")});else c({name:"Repository configuration",severity:"ok",message:"valid"})}catch(p){c({name:"Repository configuration",severity:"error",message:L(p)})}let f={};for(let[p,m]of Object.entries(e.services))for(let h of m.ports){if(h.auto)continue;let v=`Port ${h.value}`;if(a(v),f[h.value])c({name:v,severity:"error",message:`configured on both ${f[h.value]} and ${p}`});else{f[h.value]=p;let b=n?.services?.[p],g=Boolean(m.container&&b&&["STARTING","RUNNING","HEALTHY","UNHEALTHY"].includes(b.state??""));if(Boolean(b&&(b.pid>0||g)&&Object.values(b.ports).includes(h.value)))c({name:v,severity:"ok",message:`in use by running service ${p}`});else if(await t.portAvailable(h.value))c({name:v,severity:"ok",message:"available"});else c(await Jg(v,h.value,`services.${p}.ports`))}}if(e.proxy.listen.port>0){let p=`Port ${e.proxy.listen.port}`;if(a(p),n?.proxyRunning)c({name:p,severity:"ok",message:"in use by the running proxy"});else if(await t.portAvailable(e.proxy.listen.port))c({name:p,severity:"ok",message:"proxy listen port available"});else c(await Jg(p,e.proxy.listen.port,"proxy.listen.port"))}return i="Diagnostics complete",s(),o}async function eT(e,t,r){let n=e.google.project_id,o=r.hasLocalAdc??Ki;if(n===""||!o())return;let i=r.probeServiceUsage??tc.probeServiceUsage,s=[{name:"IAM Credentials API",service:"iamcredentials.googleapis.com"},{name:"Resource Manager API",service:"cloudresourcemanager.googleapis.com"},{name:"IAP API",service:"iap.googleapis.com"}];(await Promise.allSettled(s.map((c)=>Xi(i(n,c.service),Ji)))).forEach((c,l)=>{let d=s[l];if(!d)return;if(c.status==="fulfilled"){t({name:d.name,severity:c.value?"ok":"warn",message:c.value?"enabled":"not enabled or unreachable",hint:c.value?void 0:`enable ${d.service} in the Google Cloud console \u2014 doctor never auto-enables APIs`});return}t({name:d.name,severity:"warn",message:L(c.reason),hint:`enable ${d.service} in the Google Cloud console \u2014 doctor never auto-enables APIs`})})}async function tT(e,t,r,n){let o=Hr(e),i=e.proxy.routes.filter((u)=>u.auth.type.toLowerCase()==="iap"),s=i.filter((u)=>u.auth.audience.trim()!=="");for(let u of i)if(u.auth.audience.trim()==="")t({name:`IAP audience ${u.name}`,severity:"error",message:"missing audience",hint:"set auth.audience to the IAP OAuth client ID"});if(o.length===0&&s.length===0)return;if(!(r.hasLocalAdc??Ki)())return;let c=r.mintToken??tc.mintToken,l=o.map(async(u)=>{try{await Xi(c(`sa:${u}`,""),Ji),t({name:`Impersonate ${u}`,severity:"ok",message:"token minted"})}catch(f){let p=(r.adcQuotaProject??du)(),m=n||"the ADC principal",h=p===""?"unknown":p,v=Yg(`Impersonate ${u}`,f,`grant roles/iam.serviceAccountTokenCreator to ${m} on ${u}; ADC quota project: ${h}`);if(p!==""&&v.message.toLowerCase().includes("api is not enabled"))v.message=`IAM Credentials API is disabled in ADC quota project ${p}`,v.hint=`enable iamcredentials.googleapis.com in ${p}, or set the ADC quota project to ${e.google.project_id}`;else if(v.message.toLowerCase()==="google authentication failed")v.message=`access-token mint failed: ${m} \u2192 ${u}`,v.hint=`verify roles/iam.serviceAccountTokenCreator for ${m} on this service account; ADC quota project: ${h}`;t(v)}}),d=s.map(async(u)=>{let f=no(u.auth).kind===ro?`sa:${no(u.auth).serviceAccount}`:"user";try{let p=ec(u.auth);await Xi(c(f,u.auth.audience,p),Ji),t({name:`IAP ${u.name}`,severity:"ok",message:"id token minted"})}catch(p){t(Yg(`IAP ${u.name}`,p,"check IAP OAuth client ID and ADC"))}});await Promise.all([...l,...d])}function Yg(e,t,r){let n=L(t),o=n.toLowerCase();if(o.includes("timeout")||o.includes("network")||o.includes("econnrefused")||o.includes("enotfound"))return{name:e,severity:"warn",message:n,hint:"network or timeout \u2014 retry when online"};if(t instanceof Ve&&t.hint!==""){let i=`${t.kind}: `,s=t.message.startsWith(i)?t.message.slice(i.length):t.message;return{name:e,severity:"error",message:s,hint:t.hint}}return{name:e,severity:"error",message:n,hint:r}}function Xi(e,t){return new Promise((r,n)=>{let o=setTimeout(()=>n(Error("timeout")),t);e.then((i)=>{clearTimeout(o),r(i)},(i)=>{clearTimeout(o),n(i)})})}async function Jg(e,t,r){let n=await zn(t);if(!n)return{name:e,severity:"error",message:"already in use",hint:`stop the process using port ${t} or change ${r}`};if(n.pid===process.pid)return{name:e,severity:"error",message:`in use by this TUI (${n.command} pid ${n.pid})`,hint:"stop the proxy from the proxy screen (x), then rerun doctor"};return{name:e,severity:"error",message:`in use by ${n.command} (pid ${n.pid})`,hint:"enter stop that process after a confirmation",action:{kind:"free-port",holder:n}}}function Xg(e){let t=[`${er()} doctor`,""];for(let r of e.checks){let n=r.severity==="error"?"\u2717":r.severity==="warn"?"!":"\u2713";if(t.push(`${n} ${r.name}`),r.severity!=="ok"){if(t.push(` ${r.message}`),r.hint)t.push(` \u2192 ${r.hint}`)}}return t.push("",`${e.issues} issue(s) found.`),t.join(`
2175
2247
  `)+`
2176
- `}Tt();class ev{start;constructor(e){this.start=e}execute(e){return this.start({...e,profile:e.profile?ur(e.profile):void 0,services:e.services?.map((t)=>xu(t))})}}class tv{stop;constructor(e){this.stop=e}execute(e){return this.stop(e.map((t)=>xu(t)))}}class rv{restart;constructor(e){this.restart=e}execute(e,t){return this.restart(e.map((r)=>xu(r)),t)}}class nv{start;constructor(e){this.start=e}execute(e,t){return this.start.execute({profile:e,client_env:t})}}class ov{host;constructor(e){this.host=e}execute(){return this.host.startProxy()}}class iv{host;constructor(e){this.host=e}execute(){return this.host.stopProxy()}}class sv{host;constructor(e){this.host=e}execute(){return this.host.reload()}}class av{host;constructor(e){this.host=e}execute(){return this.host.refreshIdentity({probeServiceAccounts:!0})}}class sc{runner;constructor(e){this.runner=e}execute(e,t,r){return this.runner.run(e,t,r)}}class ac{execute(e,t,r){return tc(e,t,r)}}class cc{execute(e,t){return rc(e,t)}}class cv{host;constructor(e){this.host=e}execute(){return this.host.snapshot()}}class lc{execute(e,t){return ic(e,t)}}function xu(e){return zg(e)}function lv(e,t,r){let n=r??e,o=new ev((i)=>n.start(i));return{startService:o,stopService:new tv((i)=>n.stop(i)),restartService:new rv((i,s)=>n.restart(i,s)),startProfile:new nv(o),startProxy:new ov(e),stopProxy:new iv(e),reloadConfig:new sv(e),refreshIdentity:new av(e),runDoctor:new sc(t),getServiceStatus:new cv(e),getStartupPlan:new ac,getShutdownPlan:new cc,resolveStart:new lc}}import{createConnection as uT}from"net";var{spawn:pT}=globalThis.Bun;import{readdirSync as ZE}from"fs";import{join as dv,resolve as bu,sep as wu}from"path";function eT(e){let t=dv(cr(),"state"),r;try{r=ZE(t)}catch{return}let n=uv(bu(e)),o;for(let i of r){let s=rd(dv(t,i,"state.json"));if(!s)continue;let a=uv(bu(s.repo_root));if(!tT(a,n))continue;if(!o||a.length>o.length)o=a}return o}function dc(e,t,r=""){if(t!=="")return{repoRoot:bu(t),source:"explicit"};let n=e===""?process.cwd():e;try{let{repoRoot:i}=Zn(n,r);return{repoRoot:i,source:"config"}}catch{if(r!=="")return}let o=eT(n);return o?{repoRoot:o,source:"state-scan"}:void 0}function uv(e){let t=e.length>1&&e.endsWith(wu)?e.slice(0,-1):e;return process.platform==="win32"?t.toLowerCase():t}function tT(e,t){return t===e||t.startsWith(e.endsWith(wu)?e:`${e}${wu}`)}import{existsSync as fv,readFileSync as mv}from"fs";import{isAbsolute as rT,join as Su}from"path";import{parse as nT}from"dotenv";te();var Qi=["process","profile","dotenv","generated","keychain","secret_manager","defaults","vars","runtime"],oT=/^projects\/[^/]+\/secrets\/[^/]+(?:\/versions\/[^/]+)?$/,iT=["process","defaults","vars","runtime"];function sT(){return{name:"dotenv",load:(e)=>{let t=pv(e.repoRoot,e.profile);if(e.workDir!=="")Object.assign(t,pv(e.workDir,e.profile));return t}}}function aT(e){let t=e?.environment.sources??[];if(t.length===0)return[...Qi];let r=new Set([...iT,...t]),n=new Set(Qi),o=t.filter((s)=>!n.has(s)),i=[];for(let s of Qi){if(s==="defaults")i.push(...o);if(r.has(s))i.push(s)}return i}async function Zi(e,t){let r={},n=t.serviceCfg.working_dir;if(n!==""&&!rT(n))n=Su(e,n);let o={repoRoot:e,profile:t.profile,service:t.service,serviceCfg:t.serviceCfg,workDir:n,cfg:t.cfg},i=cT(t),s={process:t.includeProcess===!1?{}:t.clientEnv??Xo(),profile:Xi(t.profileEnv,t.cfg,i),dotenv:Xi(await sT().load(o),t.cfg,i),generated:{},keychain:t.sourceValues?.keychain??lT(o),secret_manager:t.sourceValues?.secret_manager??await dT(o,t.fetchSecret),defaults:Xi(t.serviceCfg.environment.defaults,t.cfg,i),vars:Xi(t.serviceCfg.environment.vars,t.cfg,i),runtime:t.runtime};for(let a of aT(t.cfg)){if(s[a]!==void 0){Object.assign(r,s[a]);continue}let c=t.pluginSources?.find((l)=>l.name===a);if(c)Object.assign(r,Xi(await c.load(o),t.cfg,i))}for(let a of t.serviceCfg.environment.required)if((r[a]??"").trim()==="")throw I(Q,`service ${t.service} missing required environment variable ${a}`);return r}function cT(e){let t={};if(e.cfg)for(let[r,n]of Object.entries(e.cfg.services)){let o={};for(let i of n.ports)if(!i.auto)o[i.name]=i.value;t[r]=o}if(e.assignedPorts)t[e.service]=e.assignedPorts;return t}function Xi(e,t,r){if(!t||Object.keys(e).length===0)return e;return Qd(e,t,r)}function lT(e){let t=e.cfg?.environment.sources??[];if(t.length>0&&!t.includes("keychain"))return{};let r={},n=new Set([...Object.keys(e.serviceCfg.environment.defaults),...Object.keys(e.serviceCfg.environment.vars),...e.serviceCfg.environment.required]);for(let o of n){let i=Su(kr(),"env",o);if(!fv(i))continue;try{r[o]=mv(i,"utf8").replace(/\n$/,"")}catch(s){throw Pe(Q,`unable to read keychain env ${o}`,s)}}return r}async function dT(e,t){if(!(e.cfg?.environment.sources??[]).includes("secret_manager"))return{};let n=e.cfg?.environment.secrets??{},o={};for(let[i,s]of Object.entries(n)){if(!oT.test(s))throw I(Q,`environment.secrets.${i} is not a Secret Manager resource`);if(!t)throw I(Q,"environment source secret_manager is not configured \u2014 set credentials or remove it from environment.sources");o[i]=await t(s)}return o}function pv(e,t){let r={},n=[".env",".env.development",".env.local"];if(t!=="")n.push(`.env.${t}`);for(let o of n){let i=Su(e,o);if(!fv(i))continue;try{let s=nT(mv(i));Object.assign(r,s)}catch(s){throw Pe(Q,`unable to read ${i}`,s)}}return r}function Xo(){let e={};for(let[t,r]of Object.entries(process.env))if(r!==void 0)e[t]=r;return e}function es(e,t,r,n,o){let i={DEVCTL_SERVICE_NAME:e,DEVCTL_ENVIRONMENT:o,SERVICE_HOST:t};if(n!=="")i.DEVCTL_PROXY_URL=n;if(r.http!==void 0)i.SERVICE_PORT=String(r.http);else{let s=Object.values(r)[0];if(s!==void 0)i.SERVICE_PORT=String(s)}for(let[s,a]of Object.entries(r))i[`${s.toUpperCase()}_PORT`]=String(a);return i}function Qo(e){return{...e}}te();var fT=50,mT=8000,hT=15000,gT=200,vT=30000,hv=86400000,yT=new Set(["logs","logs_page","logs_stats"]);function xT(e){if(e.legacy)return"attached daemon predates the client/daemon compatibility handshake";return`attached daemon speaks RPC protocol ${e.daemonProtocol??"unknown"}; this client speaks ${Vi}`}function bT(e){if(e.compatible&&e.daemonVersion!==void 0&&e.daemonVersion!==jt)return`attached daemon is devctl ${e.daemonVersion}; this client is ${jt} (run \`devctl down\` then start again to update it)`;return}function ku(e,t){if(!e.compat.compatible&&!yT.has(t))throw Te(lt,xT(e.compat),"run `devctl down` to stop it, then start again")}class gv{socket;buf="";pending=new Map;listeners=[];nextID=0;compat={compatible:!0,legacy:!1};session="";constructor(e){this.socket=e,e.on("data",(t)=>{this.buf+=t.toString("utf8");let r=this.buf.split(`
2177
- `);this.buf=r.pop()??"";for(let n of r){if(n.trim()==="")continue;this.onLine(n)}}),e.on("error",(t)=>this.rejectPending(t instanceof Error?t:Error(String(t)))),e.on("close",()=>this.rejectPending(Error("supervisor connection closed")))}onLine(e){let t;try{t=JSON.parse(e)}catch{return}if(t.event){for(let n of this.listeners)n(t.event);return}let r=t.id?this.pending.get(t.id):void 0;if(!r)return;if(this.pending.delete(t.id??""),clearTimeout(r.timer),t.error){r.reject(dh({error:t.error,kind:t.kind,hint:t.hint,service:t.service}));return}r.resolve(t.result)}onEvent(e){return this.listeners.push(e),()=>{let t=this.listeners.indexOf(e);if(t>=0)this.listeners.splice(t,1)}}call(e,t,r=vT){this.nextID+=1;let n=String(this.nextID);return new Promise((o,i)=>{let s=setTimeout(()=>{this.pending.delete(n),i(Error(`${e} timed out after ${r}ms`))},r);this.pending.set(n,{resolve:o,reject:i,timer:s}),this.socket.write(JSON.stringify({id:n,method:e,params:t})+`
2178
- `)})}close(){this.rejectPending(Error("supervisor connection closed")),this.socket.destroy()}rejectPending(e){for(let t of this.pending.values())clearTimeout(t.timer),t.reject(e);this.pending.clear()}}function vv(e,t){let r=Qs(e),n=Date.now()+t;return new Promise((o,i)=>{let s=()=>{let a=uT(r);a.once("connect",()=>{let c=new gv(a);wT(c).finally(()=>o(c))}),a.once("error",(c)=>{if(a.destroy(),Date.now()>=n){i(Te(lt,"supervisor is not running","run `devctl start` or `devctl attach` after starting services"));return}setTimeout(s,fT)})};s()})}async function wT(e){let t;try{t=await e.call("ping",null,mT)}catch{return}let r=ST(t)?t:{};if(typeof r.session==="string")e.session=r.session;let n=typeof r.protocol==="number"?r.protocol:void 0,o=typeof r.version==="string"?r.version:void 0;if(n===void 0){e.compat={compatible:!1,legacy:!0,daemonVersion:o};return}e.compat={compatible:n===Vi,legacy:!1,daemonVersion:o,daemonProtocol:n}}function ST(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}async function po(e){try{return await vv(e,gT)}catch{return}}function kT(e,t,r,n){return r?[e,...n]:[e,t,...n]}async function yv(e,t){let r=await po(e);if(r)return r;km(e);let n=Pi(e),o=kT(process.execPath,process.argv[1]??"",Bun.isStandaloneExecutable===!0,["--config",t,"_supervisor","--repo",e]),i=pT({cmd:o,env:{...process.env},stdout:"ignore",stderr:Bun.file(n),stdin:"ignore",detached:!0});try{let s=await PT(e,hT,i.exited);return i.unref(),s}catch{throw await RT(i),Te(lt,"supervisor failed to start",`see ${n} for details`)}}var CT=1000;function PT(e,t,r){return new Promise((n,o)=>{let i=!1,s=(a)=>{if(i)return;i=!0,a()};r.then((a)=>{s(()=>o(Error(`supervisor exited (code ${a}) before accepting connections`)))}),vv(e,t).then((a)=>s(()=>n(a)),(a)=>s(()=>o(a)))})}async function RT(e){let t=e.pid;try{e.kill("SIGKILL")}catch{}if(t!==void 0&&t>0&&process.platform!=="win32")try{process.kill(-t,"SIGKILL")}catch{try{process.kill(t,"SIGKILL")}catch{}}await Promise.race([e.exited.then(()=>{return}),new Promise((r)=>{setTimeout(r,CT)})])}class uc{cfg;client;previousPersisted;constructor(e){this.cfg=e}async start(e){return await this.call("start",{...e,client_env:Xo()})}async stop(e){await this.call("stop",{services:e})}async restart(e,t){await this.call("restart",{services:e,cascade:t===!0,client_env:Xo()})}async runTask(e){return await this.call("run_task",{name:e,client_env:Xo()},hv)}async execService(e,t,r=!1){return await this.call("exec",{service:e,command:t,print_env:r,client_env:Xo()},hv)}async status(){return await this.call("status",null)}async refreshAuth(){return await this.call("auth_refresh",null)}async configSnapshot(){return await this.call("config_snapshot",null)}async logs(e){return(await this.call("logs",e)).events??[]}async logsPage(e){return await this.call("logs_page",e)}async logsStats(e){return await this.call("logs_stats",e)}async proxyStart(){await this.call("proxy_start",null)}async proxyStop(){await this.call("proxy_stop",null)}async mcpStart(e){await this.call("mcp_start",e??null)}async mcpStop(){await this.call("mcp_stop",null)}async mcpSetTools(e){return(await this.call("mcp_set_tools",{disabled:[...e]})).disabled_tools??[]}async reload(){return await this.call("reload",null)}async invalidateAuth(){await this.call("auth_invalidate",null)}onEvent(e){if(this.client)return this.client.onEvent(e);return()=>{return}}async shutdown(e){if(!this.client)return;let t=Math.max(5000,this.cfg.shutdown.grace_seconds*1000+2000);await this.client.call("shutdown",{stop_services:e.stopServices},t)}async close(e){if(!this.client)return;try{if(e?.shutdownSupervisor===!0&&e.detach!==!0)await this.shutdown({stopServices:!0})}finally{this.client.close()}}async call(e,t,r){if(!this.client)throw Pe(lt,"supervisor is not running",Error("no client"));return ku(this.client,e),this.client.call(e,t,r)}}function ts(e){let t=e&&bT(e.compat);if(t)process.stderr.write(`warning: ${t}
2179
- `)}async function xv(e,t,r=""){let n=dc(e,t,r);if(!n)throw Te(lt,"no devctl configuration found","run `devctl setup`, create a .devctl/config.yaml in the repository root, or pass --repo");let o=await po(n.repoRoot);return ts(o),{repoRoot:n.repoRoot,client:o}}async function bv(e,t,r,n){let o=n?.allowMissingConfig===!0?ro(e,t):nn(e,t),i=new uc(o);if(!r)return i.client=await po(o.repoRoot),ts(i.client),i;return i.client=await yv(o.repoRoot,o.configPath),ts(i.client),i}async function wv(e){let t=new uc(Jn());return t.client=e,ts(t.client),t.cfg=await t.configSnapshot(),t}async function Sv(e,t){let r=dc(e,"",t),n=r?await po(r.repoRoot):void 0;if(!n)throw Te(lt,"supervisor is not running","run `devctl start` before `devctl attach`");return wv(n)}async function kv(e,t){let r=dc(e,"",t),n=r?await po(r.repoRoot):void 0;if(n)return wv(n);let o=nn(e,t),i=new uc(o),s=fn(o.repoRoot);if(i.client=await yv(o.repoRoot,o.configPath),ts(i.client),i.cfg=await i.configSnapshot(),s&&s.processes.length>0)i.previousPersisted=s;return i}import{realpathSync as ET}from"fs";var TT="https://api.github.com/repos/amr-m-abdelgawad/devctl/releases/latest";function AT(){let e=Bun.isStandaloneExecutable===!0,t=e?"":Av(process.argv[1]??"");return _v({scriptPath:t,execPath:Av(process.execPath),standalone:e})}async function LT(e=fetch,t=AT()){try{let r=await e(TT,{headers:{Accept:"application/vnd.github+json"}});if(!r.ok)return Tv(t);let o=((await r.json()).tag_name??"").replace(/^v/,"");return{current:jt,latest:o,newer:o!==""&&Ev(o,jt)>0,hint:t.hint,kind:t.kind,command:t.command}}catch{return Tv(t)}}async function DT(e,t=Lv){return t(e)}async function Lv(e,t=!1){let r=Bun.spawn([...e],{stdout:t?"inherit":"pipe",stderr:t?"inherit":"pipe",stdin:"ignore"});if(t)return{code:await r.exited,stdout:"",stderr:""};let n=await new Response(r.stdout).text(),o=await new Response(r.stderr).text();return{code:await r.exited,stdout:n,stderr:o}}function Dv(e=fetch){return{check:()=>LT(e),apply:(t,r)=>r===!0?Lv(t,!0):DT(t)}}function Tv(e){return{current:jt,latest:"",newer:!1,hint:e.hint,kind:e.kind,command:e.command}}function Av(e){if(e==="")return"";try{return ET(e)}catch{return e}}function Iv(e){let t=e?.tokens??new co(60000,Rn(),void 0),r=e?.doctorHost??Yo({tokens:t}),n=Dv(),o={loadTuiConfig:Io,saveTuiPreferences:Am,resolveTuiOverridePath:ia,userTuiConfigPath:oa,listSessions:nh,loadSessionEvents:oh,loadPath:Bi,validateConfigText:ji,discover:Zn,configDiff:Pn,openTui:kv,findDaemon:xv,tryDial:po,assertMethodAllowed:ku,readPersistedState:fn,bootstrapLogPath:Pi,exportsDir:Xs,resolveExportPath:th,writeLogExport:_d,openInFileManager:rh,freePort:xa,createStarterConfig:wh,runSetup:(i,s,a)=>Sh(o,i,s,a),readTextFile:(i)=>MT(i,"utf8"),writeTextFile:(i,s)=>OT(i,s),fileExists:IT,load:nn,loadOrEmpty:ro,validate:eo,detectGoogle:so,loginGoogle:Mg,logoutGoogle:Og,refreshUserToken:async(i="user")=>t.refresh(i,"",[]),runDoctor:new sc(e?.doctorRunner??ec(r)),getStartupPlan:new ac,getShutdownPlan:new cc,resolveStart:new lc,formatDoctor:Vg,openController:bv,openAttach:Sv,checkUpdate:()=>n.check(),applyUpdate:(i,s)=>n.apply(i,s),formatUpdateStatus:Rv};return o}import{existsSync as nL,unlinkSync as oL}from"fs";import{connect as $T}from"net";Tt();var FT=2000,NT=200,HT=300;function Mv(e){return{lookup(t){let r=t.toLowerCase();return["","http","tcp","command","process"].includes(r)||e.some((o)=>o.name.toLowerCase()===r)?{check:(o,i)=>jT({...o,type:t},i.pid,i.ports,i.workDir,i.env,e)}:void 0}}}async function jT(e,t,r,n,o,i=[]){let s=e.timeout_seconds>0?e.timeout_seconds*1000:FT,a=e.type.toLowerCase(),c=i.find((l)=>l.name.toLowerCase()===a);if(c)try{let l=await c.check(e,{pid:t,ports:r,workDir:n,env:o});return{status:l.status,message:l.message}}catch(l){return{status:Oe,message:l instanceof Error?l.message:String(l)}}if(a==="http")return BT(e.url,s);if(a==="tcp")return WT(UT(e.address,r),s);if(a==="command")return GT(e.command.args,e.command.shell,n,o,s);if(a==="process"||a===""){if(t>0&&Qt(t))return{status:Bt,message:"process running"};return{status:Oe,message:"process not running"}}return{status:Oe,message:`unknown health type ${e.type}`}}async function BT(e,t){let r=new AbortController,n=setTimeout(()=>r.abort(),t);try{let o=await fetch(e,{signal:r.signal});if(o.status>=NT&&o.status<HT)return{status:Bt,message:String(o.status)};return{status:Oe,message:String(o.status)}}catch(o){return{status:Oe,message:o instanceof Error?o.message:String(o)}}finally{clearTimeout(n)}}function UT(e,t){if(e!=="")return e;if(t.http!==void 0)return`127.0.0.1:${t.http}`;let r=Object.values(t)[0];return r!==void 0?`127.0.0.1:${r}`:""}function WT(e,t){return new Promise((r)=>{if(e===""){r({status:Oe,message:"no tcp address"});return}let n=e.lastIndexOf(":"),o=Number.parseInt(e.slice(n+1),10),i=e.slice(0,n).replace(/^\[|\]$/g,"");if(n<1||!Number.isFinite(o)){r({status:Oe,message:`invalid tcp address ${e}`});return}let s=$T({host:i,port:o},()=>{s.end(),r({status:Bt,message:"connected"})});s.setTimeout(t),s.on("error",(a)=>{r({status:Oe,message:a.message})}),s.on("timeout",()=>{s.destroy(),r({status:Oe,message:"timeout"})})})}async function GT(e,t,r,n,o){if(e.length===0)return{status:Oe,message:"empty health command"};let i=t?process.platform==="win32"?["cmd.exe","/c",e.join(" ")]:["/bin/sh","-c",e.join(" ")]:e,s;try{s=Bun.spawn({cmd:i,cwd:r===""?void 0:r,env:n,stdout:"ignore",stderr:"ignore"})}catch(l){return{status:Oe,message:l instanceof Error?l.message:String(l)}}let a=setTimeout(()=>s.kill("SIGKILL"),o),c=await s.exited;if(clearTimeout(a),c===0)return{status:Bt,message:"ok"};return{status:Oe,message:`exit ${c}`}}te();var{spawn:zv}=globalThis.Bun;var{spawn:KT}=globalThis.Bun;async function Ov(e,t){if(e<=0)return;try{process.kill(-e,t);return}catch{try{process.kill(e,t)}catch{return}}}async function $v(e){if(!Qt(e))return;let t=await rs(["ps","-p",String(e),"-o","command="]),r=Date.now(),n=qT(await rs(["ps","-p",String(e),"-o","etime="])),o=await VT(e);return{pid:e,command:t.trim(),cwd:o,startTime:n===void 0?void 0:new Date(r-n).toISOString()}}function qT(e){let t=e.trim();if(t==="")return;let r=t.indexOf("-"),n=r>=0?t.slice(0,r):"0",i=(r>=0?t.slice(r+1):t).split(":");if(i.length!==2&&i.length!==3)return;let s=Number(n),a=i.length===3?Number(i[0]):0,c=Number(i.length===3?i[1]:i[0]),l=Number(i.at(-1));if(!Number.isInteger(s)||s<0||!Number.isInteger(a)||a<0||a>23||!Number.isInteger(c)||c<0||c>59||!Number.isInteger(l)||l<0||l>59)return;return(((s*24+a)*60+c)*60+l)*1000}async function VT(e){let r=(await rs(["lsof","-a","-p",String(e),"-d","cwd","-Fn"])).split(`
2180
- `).find((i)=>i.startsWith("n"));if(r&&r.length>1)return r.slice(1).trim();let n=await rs(["pwdx",String(e)]),o=n.indexOf(":");if(o>=0)return n.slice(o+1).trim();return""}async function rs(e){try{let t=KT({cmd:e,stdout:"pipe",stderr:"ignore"}),r=t.stdout?await new Response(t.stdout).text():"";return await t.exited,r}catch{return""}}async function Fv(e){let t=new Map;if(e.length===0)return t;let r=await rs(["ps","-o","pid=,pcpu=,rss=","-p",e.join(",")]);for(let n of r.split(`
2181
- `)){let o=n.trim();if(o==="")continue;let[i,s,a]=o.split(/\s+/),c=Number(i),l=Number(s),d=Number(a);if(Number.isFinite(c)&&Number.isFinite(l)&&Number.isFinite(d))t.set(c,{pid:c,cpuPercent:l,memoryKB:d})}return t}function Nv(e,t){if(e.length===0||t==="")return!1;let r=e.join(" ");if(t.includes(r))return!0;let o=(e[0]??"").split(/[/\\]/).pop()??"";if(o!==""&&t.includes(o))return!0;let i=e[e.length-1]??"";return i!==""&&t.includes(i)}var{spawn:Hv}=globalThis.Bun;var zT=1024;async function jv(e,t){if(e<=0)return;try{await Hv({cmd:["taskkill","/PID",String(e),"/T","/F"],stdout:"ignore",stderr:"ignore"}).exited}catch{return}}async function Bv(e){let t=YT(nd(e),e);if(t)return t;if(!Qt(e))return;let r=await Cu(["powershell","-NoProfile","-Command",`Get-CimInstance Win32_Process -Filter "ProcessId=${e}" | Select-Object CommandLine,ExecutablePath,CreationDate | ConvertTo-Json -Compress`]),n=XT(r);if(n&&n.command!=="")return{pid:e,command:n.command,cwd:n.cwd,startTime:n.startTime};let o=await Cu(["powershell","-NoProfile","-Command",`Get-Process -Id ${e} -ErrorAction SilentlyContinue | Select-Object Path,StartTime | ConvertTo-Json -Compress`]),i=JT(o);if(!i&&!n)return;return{pid:e,command:n?.command||i?.command||"",cwd:n?.cwd||i?.cwd||"",startTime:n?.startTime||i?.startTime}}function YT(e,t){let r=String(t);for(let n of e.split(/\r?\n/)){if(!n.includes(r))continue;let i=n.split(",").map((s)=>s.replace(/^"|"$/g,"").trim())[0]??"";if(i===""||i.toLowerCase().includes("info:"))continue;return{pid:t,command:i,cwd:""}}return}function JT(e){let t=e.trim();if(t==="")return;try{let r=JSON.parse(t),n=(r.Path||"").trim();if(n==="")return;return{command:n,cwd:mc(n),startTime:r.StartTime}}catch{let r=fc(t,"Path");if(r==="")return;return{command:r,cwd:mc(r)}}}function XT(e){let t=e.trim();if(t==="")return;try{let r=JSON.parse(t),n=(r.CommandLine||r.ExecutablePath||"").trim(),o=(r.ExecutablePath||"").trim(),i=mc(o);return{command:n,cwd:i,startTime:r.CreationDate||void 0}}catch{return{command:fc(t,"CommandLine")||fc(t,"ExecutablePath"),cwd:mc(fc(t,"ExecutablePath"))}}}async function Uv(e){let t=new Map;if(e.length===0)return t;let r=e.filter((o)=>o>0).join(",");if(r==="")return t;let n=await Cu(["powershell","-NoProfile","-Command",`${r}.Split(',') | ForEach-Object { Get-Process -Id $_ -ErrorAction SilentlyContinue } | Select-Object Id,CPU,WorkingSet64 | ConvertTo-Json -Compress`]);return QT(n)}function QT(e){let t=new Map,r=e.trim();if(r==="")return t;try{let n=JSON.parse(r),o=Array.isArray(n)?n:[n];for(let i of o){if(!i||typeof i!=="object")continue;let s=i,a=Number(s.Id),c=Number(s.CPU??0),l=Number(s.WorkingSet64??0)/zT;if(Number.isFinite(a)&&a>0&&Number.isFinite(c)&&Number.isFinite(l))t.set(a,{pid:a,cpuPercent:c,memoryKB:l})}}catch{return t}return t}function mc(e){if(e==="")return"";let t=e.replace(/\//g,"\\"),r=t.lastIndexOf("\\");return r>0?t.slice(0,r):""}function fc(e,t){let r=`${t}=`,n=e.split(/\r?\n/).find((o)=>o.startsWith(r));return n?n.slice(r.length).trim():""}async function Cu(e){try{let t=Hv({cmd:e,stdout:"pipe",stderr:"ignore"}),r=t.stdout?await new Response(t.stdout).text():"";return await t.exited,r}catch{return""}}te();var{spawn:Pu}=globalThis.Bun;async function Gv(e){await t0(e.runtime,e.containerName);let t=ZT(e),r=await Zo(e.runtime,t,e.env);if(r.code!==0||r.stdout.trim()==="")throw I(He,`failed to start ${e.containerName}: ${r.stderr.trim()||"container runtime returned no id"}`);let n=r.stdout.trim().split(/\s+/)[0]??"";return Vv(e.runtime,e.containerName,n,e.onLine,e.onExit)}function ZT(e){let t=["run","--detach","--name",e.containerName,"--label","devctl.managed=true"];for(let[r,n]of Object.entries(e.ports)){let o=e.targetPorts[r]??n;t.push("--publish",`127.0.0.1:${n}:${o}`)}for(let r of Object.keys(e.env).sort())t.push("--env",r);for(let r of e.volumes)t.push("--volume",r);return t.push(e.image,...e.command),t}var e0=new Set(["PATH","HOME","HOSTNAME","PWD","OLDPWD","SHLVL","_","TMPDIR"]);function Kv(e){return Object.fromEntries(Object.entries(e).filter(([t])=>!e0.has(t)))}async function qv(e,t,r,n){let o=await Zo(e,["inspect","--format","{{.Id}} {{.State.Running}}",t],process.env);if(o.code!==0)return;let[i,s]=o.stdout.trim().split(/\s+/);if(!i||s!=="true")return;return Vv(e,t,i,r,n)}function Vv(e,t,r,n,o){let i=!0,s=Pu({cmd:[e,"logs","--follow",r],stdout:"pipe",stderr:"pipe",stdin:"ignore"});Wv(s.stdout,"stdout",n),Wv(s.stderr,"stderr",n);let a=Pu({cmd:[e,"wait",r],stdout:"pipe",stderr:"pipe",stdin:"ignore"}),c=Promise.all([new Response(a.stdout).text(),a.exited]).then(async([l,d])=>{i=!1,s.kill();let u=Number.parseInt(l.trim(),10),f=Number.isFinite(u)?u:d??1,p=f===0?void 0:Error(`container exited with code ${f}`);return await Zo(e,["rm","--force",r],process.env),o?.(f,p),{code:f,err:p}});return{id:r,runtime:e,containerName:t,done:c,running:()=>i,stop:async(l)=>{let d=Math.max(1,Math.ceil(l/1000));await Zo(e,["stop","--time",String(d),r],process.env),await c}}}async function t0(e,t){let r=await Zo(e,["inspect","--format","{{.State.Running}}",t],process.env);if(r.code===0&&r.stdout.trim()==="false")await Zo(e,["rm","--force",t],process.env)}async function Zo(e,t,r){try{let n=Pu({cmd:[e,...t],env:{...process.env,...r},stdout:"pipe",stderr:"pipe",stdin:"ignore"}),[o,i,s]=await Promise.all([new Response(n.stdout).text(),new Response(n.stderr).text(),n.exited]);return{code:s??1,stdout:o,stderr:i}}catch(n){throw Pe(He,`unable to run ${e}`,n)}}async function Wv(e,t,r){if(!e||typeof e==="number"||!r)return;let n=e.getReader(),o=new TextDecoder,i="";for(;;){let s=await n.read();if(s.done)break;i+=o.decode(s.value,{stream:!0});let a=i.split(`
2182
- `);i=a.pop()??"";for(let c of a)r(t,c.replace(/\r$/,""))}if(i!=="")r(t,i)}var r0=1e4,n0=2000,o0=500;class Ru{isRunning(e){let t=this.running.get(e);return t!==void 0&&ns(t)}running=new Map;async start(e){let t=this.running.get(e.name);if(t&&ns(t))return t;if(this.running.delete(e.name),e.args.length===0)throw I(He,"empty command");let r=e.shell?Xv(e.args):e.args,n;try{n=zv({cmd:r,cwd:e.workDir===""?void 0:e.workDir,env:e.env,stdout:e.captureStdout===!1?"ignore":"pipe",stderr:e.captureStderr===!1?"ignore":"pipe",stdin:"ignore",detached:process.platform!=="win32"})}catch(i){throw Pe(He,`failed to start ${e.name}`,i)}let o={name:e.name,pid:n.pid??0,startTime:new Date,workDir:e.workDir,args:[...e.args],proc:n,done:Promise.resolve({code:0})};return hc(n.stdout,"stdout",e.onLine),hc(n.stderr,"stderr",e.onLine),o.done=n.exited.then((i)=>{let s=typeof i==="number"?i:0,a=s===0?void 0:Error(`exited with code ${s}`);if(this.running.get(e.name)===o)this.running.delete(e.name);if(e.onExit)e.onExit(s,a);return{code:s,err:a}}),this.running.set(e.name,o),o}async runOnce(e){if(e.args.length===0)throw I(He,"empty command");let t;try{t=zv({cmd:e.shell?Xv(e.args):e.args,cwd:e.workDir||void 0,env:e.env,stdout:"pipe",stderr:"pipe",stdin:"ignore"})}catch(a){throw Pe(He,`failed to run ${e.name}`,a)}let r="",n="",o=(a,c)=>{if(a==="stdout")r+=`${c}
2248
+ `}At();class ov{start;constructor(e){this.start=e}execute(e){return this.start({...e,profile:e.profile?ur(e.profile):void 0,services:e.services?.map((t)=>wu(t))})}}class iv{stop;constructor(e){this.stop=e}execute(e){return this.stop(e.map((t)=>wu(t)))}}class sv{restart;constructor(e){this.restart=e}execute(e,t){return this.restart(e.map((r)=>wu(r)),t)}}class av{start;constructor(e){this.start=e}execute(e,t){return this.start.execute({profile:e,client_env:t})}}class cv{host;constructor(e){this.host=e}execute(){return this.host.startProxy()}}class lv{host;constructor(e){this.host=e}execute(){return this.host.stopProxy()}}class dv{host;constructor(e){this.host=e}execute(){return this.host.reload()}}class uv{host;constructor(e){this.host=e}execute(){return this.host.refreshIdentity({probeServiceAccounts:!0})}}class cc{runner;constructor(e){this.runner=e}execute(e,t,r){return this.runner.run(e,t,r)}}class lc{execute(e,t,r){return nc(e,t,r)}}class dc{execute(e,t){return oc(e,t)}}class pv{host;constructor(e){this.host=e}execute(){return this.host.snapshot()}}class uc{execute(e,t){return ac(e,t)}}function wu(e){return Qg(e)}function fv(e,t,r){let n=r??e,o=new ov((i)=>n.start(i));return{startService:o,stopService:new iv((i)=>n.stop(i)),restartService:new sv((i,s)=>n.restart(i,s)),startProfile:new av(o),startProxy:new cv(e),stopProxy:new lv(e),reloadConfig:new dv(e),refreshIdentity:new uv(e),runDoctor:new cc(t),getServiceStatus:new pv(e),getStartupPlan:new lc,getShutdownPlan:new dc,resolveStart:new uc}}import{createConnection as gT}from"net";var{spawn:vT}=globalThis.Bun;import{readdirSync as oT}from"fs";import{join as mv,resolve as Su,sep as ku}from"path";function iT(e){let t=mv(cr(),"state"),r;try{r=oT(t)}catch{return}let n=hv(Su(e)),o;for(let i of r){let s=nd(mv(t,i,"state.json"));if(!s)continue;let a=hv(Su(s.repo_root));if(!sT(a,n))continue;if(!o||a.length>o.length)o=a}return o}function pc(e,t,r=""){if(t!=="")return{repoRoot:Su(t),source:"explicit"};let n=e===""?process.cwd():e;try{let{repoRoot:i}=Qn(n,r);return{repoRoot:i,source:"config"}}catch{if(r!=="")return}let o=iT(n);return o?{repoRoot:o,source:"state-scan"}:void 0}function hv(e){let t=e.length>1&&e.endsWith(ku)?e.slice(0,-1):e;return process.platform==="win32"?t.toLowerCase():t}function sT(e,t){return t===e||t.startsWith(e.endsWith(ku)?e:`${e}${ku}`)}import{existsSync as vv,readFileSync as yv}from"fs";import{isAbsolute as aT,join as Cu}from"path";import{parse as cT}from"dotenv";te();var es=["process","profile","dotenv","generated","keychain","secret_manager","defaults","vars","runtime"],lT=/^projects\/[^/]+\/secrets\/[^/]+(?:\/versions\/[^/]+)?$/,dT=["process","defaults","vars","runtime"];function uT(){return{name:"dotenv",load:(e)=>{let t=gv(e.repoRoot,e.profile);if(e.workDir!=="")Object.assign(t,gv(e.workDir,e.profile));return t}}}function pT(e){let t=e?.environment.sources??[];if(t.length===0)return[...es];let r=new Set([...dT,...t]),n=new Set(es),o=t.filter((s)=>!n.has(s)),i=[];for(let s of es){if(s==="defaults")i.push(...o);if(r.has(s))i.push(s)}return i}async function ts(e,t){let r={},n=t.serviceCfg.working_dir;if(n!==""&&!aT(n))n=Cu(e,n);let o={repoRoot:e,profile:t.profile,service:t.service,serviceCfg:t.serviceCfg,workDir:n,cfg:t.cfg},i=fT(t),s={process:t.includeProcess===!1?{}:t.clientEnv??Xo(),profile:Zi(t.profileEnv,t.cfg,i),dotenv:Zi(await uT().load(o),t.cfg,i),generated:{},keychain:t.sourceValues?.keychain??mT(o),secret_manager:t.sourceValues?.secret_manager??await hT(o,t.fetchSecret),defaults:Zi(t.serviceCfg.environment.defaults,t.cfg,i),vars:Zi(t.serviceCfg.environment.vars,t.cfg,i),runtime:t.runtime};for(let a of pT(t.cfg)){if(s[a]!==void 0){Object.assign(r,s[a]);continue}let c=t.pluginSources?.find((l)=>l.name===a);if(c)Object.assign(r,Zi(await c.load(o),t.cfg,i))}for(let a of t.serviceCfg.environment.required)if((r[a]??"").trim()==="")throw E(Y,`service ${t.service} missing required environment variable ${a}`);return r}function fT(e){let t={};if(e.cfg)for(let[r,n]of Object.entries(e.cfg.services)){let o={};for(let i of n.ports)if(!i.auto)o[i.name]=i.value;t[r]=o}if(e.assignedPorts)t[e.service]=e.assignedPorts;return t}function Zi(e,t,r){if(!t||Object.keys(e).length===0)return e;return eu(e,t,r)}function mT(e){let t=e.cfg?.environment.sources??[];if(t.length>0&&!t.includes("keychain"))return{};let r={},n=new Set([...Object.keys(e.serviceCfg.environment.defaults),...Object.keys(e.serviceCfg.environment.vars),...e.serviceCfg.environment.required]);for(let o of n){let i=Cu(kr(),"env",o);if(!vv(i))continue;try{r[o]=yv(i,"utf8").replace(/\n$/,"")}catch(s){throw Pe(Y,`unable to read keychain env ${o}`,s)}}return r}async function hT(e,t){if(!(e.cfg?.environment.sources??[]).includes("secret_manager"))return{};let n=e.cfg?.environment.secrets??{},o={};for(let[i,s]of Object.entries(n)){if(!lT.test(s))throw E(Y,`environment.secrets.${i} is not a Secret Manager resource`);if(!t)throw E(Y,"environment source secret_manager is not configured \u2014 set credentials or remove it from environment.sources");o[i]=await t(s)}return o}function gv(e,t){let r={},n=[".env",".env.development",".env.local"];if(t!=="")n.push(`.env.${t}`);for(let o of n){let i=Cu(e,o);if(!vv(i))continue;try{let s=cT(yv(i));Object.assign(r,s)}catch(s){throw Pe(Y,`unable to read ${i}`,s)}}return r}function Xo(){let e={};for(let[t,r]of Object.entries(process.env))if(r!==void 0)e[t]=r;return e}function rs(e,t,r,n,o){let i={DEVCTL_SERVICE_NAME:e,DEVCTL_ENVIRONMENT:o,SERVICE_HOST:t};if(n!=="")i.DEVCTL_PROXY_URL=n;if(r.http!==void 0)i.SERVICE_PORT=String(r.http);else{let s=Object.values(r)[0];if(s!==void 0)i.SERVICE_PORT=String(s)}for(let[s,a]of Object.entries(r))i[`${s.toUpperCase()}_PORT`]=String(a);return i}function Qo(e){return{...e}}te();var yT=50,bT=8000,xT=15000,wT=200,ST=30000,bv=86400000,kT=new Set(["logs","logs_page","logs_stats"]);function CT(e){if(e.legacy)return"attached daemon predates the client/daemon compatibility handshake";return`attached daemon speaks RPC protocol ${e.daemonProtocol??"unknown"}; this client speaks ${Yi}`}function PT(e){if(e.compatible&&e.daemonVersion!==void 0&&e.daemonVersion!==jt)return`attached daemon is devctl ${e.daemonVersion}; this client is ${jt} (run \`devctl down\` then start again to update it)`;return}function Pu(e,t){if(!e.compat.compatible&&!kT.has(t))throw Te(lt,CT(e.compat),"run `devctl down` to stop it, then start again")}class xv{socket;buf="";pending=new Map;listeners=[];nextID=0;compat={compatible:!0,legacy:!1};session="";constructor(e){this.socket=e,e.on("data",(t)=>{this.buf+=t.toString("utf8");let r=this.buf.split(`
2249
+ `);this.buf=r.pop()??"";for(let n of r){if(n.trim()==="")continue;this.onLine(n)}}),e.on("error",(t)=>this.rejectPending(t instanceof Error?t:Error(String(t)))),e.on("close",()=>this.rejectPending(Error("supervisor connection closed")))}onLine(e){let t;try{t=JSON.parse(e)}catch{return}if(t.event){for(let n of this.listeners)n(t.event);return}let r=t.id?this.pending.get(t.id):void 0;if(!r)return;if(this.pending.delete(t.id??""),clearTimeout(r.timer),t.error){r.reject(fh({error:t.error,kind:t.kind,hint:t.hint,service:t.service}));return}r.resolve(t.result)}onEvent(e){return this.listeners.push(e),()=>{let t=this.listeners.indexOf(e);if(t>=0)this.listeners.splice(t,1)}}call(e,t,r=ST){this.nextID+=1;let n=String(this.nextID);return new Promise((o,i)=>{let s=setTimeout(()=>{this.pending.delete(n),i(Error(`${e} timed out after ${r}ms`))},r);this.pending.set(n,{resolve:o,reject:i,timer:s}),this.socket.write(JSON.stringify({id:n,method:e,params:t})+`
2250
+ `)})}close(){this.rejectPending(Error("supervisor connection closed")),this.socket.destroy()}rejectPending(e){for(let t of this.pending.values())clearTimeout(t.timer),t.reject(e);this.pending.clear()}}function wv(e,t){let r=ea(e),n=Date.now()+t;return new Promise((o,i)=>{let s=()=>{let a=gT(r);a.once("connect",()=>{let c=new xv(a);_T(c).finally(()=>o(c))}),a.once("error",(c)=>{if(a.destroy(),Date.now()>=n){i(Te(lt,"supervisor is not running","run `devctl start` or `devctl attach` after starting services"));return}setTimeout(s,yT)})};s()})}async function _T(e){let t;try{t=await e.call("ping",null,bT)}catch{return}let r=RT(t)?t:{};if(typeof r.session==="string")e.session=r.session;let n=typeof r.protocol==="number"?r.protocol:void 0,o=typeof r.version==="string"?r.version:void 0;if(n===void 0){e.compat={compatible:!1,legacy:!0,daemonVersion:o};return}e.compat={compatible:n===Yi,legacy:!1,daemonVersion:o,daemonProtocol:n}}function RT(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}async function uo(e){try{return await wv(e,wT)}catch{return}}function ET(e,t,r,n){return r?[e,...n]:[e,t,...n]}async function Sv(e,t){let r=await uo(e);if(r)return r;_m(e);let n=Ri(e),o=ET(process.execPath,process.argv[1]??"",Bun.isStandaloneExecutable===!0,["--config",t,"_supervisor","--repo",e]),i=vT({cmd:o,env:{...process.env},stdout:"ignore",stderr:Bun.file(n),stdin:"ignore",detached:!0});try{let s=await AT(e,xT,i.exited);return i.unref(),s}catch{throw await LT(i),Te(lt,"supervisor failed to start",`see ${n} for details`)}}var TT=1000;function AT(e,t,r){return new Promise((n,o)=>{let i=!1,s=(a)=>{if(i)return;i=!0,a()};r.then((a)=>{s(()=>o(Error(`supervisor exited (code ${a}) before accepting connections`)))}),wv(e,t).then((a)=>s(()=>n(a)),(a)=>s(()=>o(a)))})}async function LT(e){let t=e.pid;try{e.kill("SIGKILL")}catch{}if(t!==void 0&&t>0&&process.platform!=="win32")try{process.kill(-t,"SIGKILL")}catch{try{process.kill(t,"SIGKILL")}catch{}}await Promise.race([e.exited.then(()=>{return}),new Promise((r)=>{setTimeout(r,TT)})])}class fc{cfg;client;previousPersisted;constructor(e){this.cfg=e}async start(e){return await this.call("start",{...e,client_env:Xo()})}async stop(e){await this.call("stop",{services:e})}async restart(e,t){await this.call("restart",{services:e,cascade:t===!0,client_env:Xo()})}async runTask(e){return await this.call("run_task",{name:e,client_env:Xo()},bv)}async execService(e,t,r=!1){return await this.call("exec",{service:e,command:t,print_env:r,client_env:Xo()},bv)}async status(){return await this.call("status",null)}async refreshAuth(){return await this.call("auth_refresh",null)}async configSnapshot(){return await this.call("config_snapshot",null)}async logs(e){return(await this.call("logs",e)).events??[]}async logsPage(e){return await this.call("logs_page",e)}async logsStats(e){return await this.call("logs_stats",e)}async proxyStart(){await this.call("proxy_start",null)}async proxyStop(){await this.call("proxy_stop",null)}async mcpStart(e){await this.call("mcp_start",e??null)}async mcpStop(){await this.call("mcp_stop",null)}async mcpSetTools(e){return(await this.call("mcp_set_tools",{disabled:[...e]})).disabled_tools??[]}async reload(){return await this.call("reload",null)}async invalidateAuth(){await this.call("auth_invalidate",null)}onEvent(e){if(this.client)return this.client.onEvent(e);return()=>{return}}async shutdown(e){if(!this.client)return;let t=Math.max(5000,this.cfg.shutdown.grace_seconds*1000+2000);await this.client.call("shutdown",{stop_services:e.stopServices},t)}async close(e){if(!this.client)return;try{if(e?.shutdownSupervisor===!0&&e.detach!==!0)await this.shutdown({stopServices:!0})}finally{this.client.close()}}async call(e,t,r){if(!this.client)throw Pe(lt,"supervisor is not running",Error("no client"));return Pu(this.client,e),this.client.call(e,t,r)}}function ns(e){let t=e&&PT(e.compat);if(t)process.stderr.write(`warning: ${t}
2251
+ `)}async function kv(e,t,r=""){let n=pc(e,t,r);if(!n)throw Te(lt,"no devctl configuration found","run `devctl setup`, create a .devctl/config.yaml in the repository root, or pass --repo");let o=await uo(n.repoRoot);return ns(o),{repoRoot:n.repoRoot,client:o}}async function Cv(e,t,r,n){let o=n?.allowMissingConfig===!0?to(e,t):nn(e,t),i=new fc(o);if(!r)return i.client=await uo(o.repoRoot),ns(i.client),i;return i.client=await Sv(o.repoRoot,o.configPath),ns(i.client),i}async function Pv(e){let t=new fc(Yn());return t.client=e,ns(t.client),t.cfg=await t.configSnapshot(),t}async function _v(e,t){let r=pc(e,"",t),n=r?await uo(r.repoRoot):void 0;if(!n)throw Te(lt,"supervisor is not running","run `devctl start` before `devctl attach`");return Pv(n)}async function Rv(e,t){let r=pc(e,"",t),n=r?await uo(r.repoRoot):void 0;if(n)return Pv(n);let o=nn(e,t),i=new fc(o),s=fn(o.repoRoot);if(i.client=await Sv(o.repoRoot,o.configPath),ns(i.client),i.cfg=await i.configSnapshot(),s&&s.processes.length>0)i.previousPersisted=s;return i}import{realpathSync as IT}from"fs";var MT="https://api.github.com/repos/amr-m-abdelgawad/devctl/releases/latest";function OT(){let e=Bun.isStandaloneExecutable===!0,t=e?"":Mv(process.argv[1]??"");return Lv({scriptPath:t,execPath:Mv(process.execPath),standalone:e})}async function $T(e=fetch,t=OT()){try{let r=await e(MT,{headers:{Accept:"application/vnd.github+json"}});if(!r.ok)return Iv(t);let o=((await r.json()).tag_name??"").replace(/^v/,"");return{current:jt,latest:o,newer:o!==""&&Dv(o,jt)>0,hint:t.hint,kind:t.kind,command:t.command}}catch{return Iv(t)}}async function FT(e,t=Ov){return t(e)}async function Ov(e,t=!1){let r=Bun.spawn([...e],{stdout:t?"inherit":"pipe",stderr:t?"inherit":"pipe",stdin:"ignore"});if(t)return{code:await r.exited,stdout:"",stderr:""};let n=await new Response(r.stdout).text(),o=await new Response(r.stderr).text();return{code:await r.exited,stdout:n,stderr:o}}function $v(e=fetch){return{check:()=>$T(e),apply:(t,r)=>r===!0?Ov(t,!0):FT(t)}}function Iv(e){return{current:jt,latest:"",newer:!1,hint:e.hint,kind:e.kind,command:e.command}}function Mv(e){if(e==="")return"";try{return IT(e)}catch{return e}}function Fv(e){let t=e?.tokens??new ao(60000,_n(),void 0),r=e?.doctorHost??Yo({tokens:t}),n=$v(),o={loadTuiConfig:Do,saveTuiPreferences:Im,resolveTuiOverridePath:aa,userTuiConfigPath:sa,listSessions:sh,loadSessionEvents:ah,loadPath:Wi,validateConfigText:Ui,discover:Qn,configDiff:Pn,openTui:Rv,findDaemon:kv,tryDial:uo,assertMethodAllowed:Pu,readPersistedState:fn,bootstrapLogPath:Ri,exportsDir:Zs,resolveExportPath:oh,writeLogExport:Ed,openInFileManager:ih,freePort:wa,createStarterConfig:Ch,runSetup:(i,s,a)=>Ph(o,i,s,a),readTextFile:(i)=>HT(i,"utf8"),writeTextFile:(i,s)=>jT(i,s),fileExists:NT,load:nn,loadOrEmpty:to,validate:Zn,detectGoogle:io,loginGoogle:Ng,logoutGoogle:Hg,refreshUserToken:async(i="user")=>t.refresh(i,"",[]),runDoctor:new cc(e?.doctorRunner??rc(r)),getStartupPlan:new lc,getShutdownPlan:new dc,resolveStart:new uc,formatDoctor:Xg,openController:Cv,openAttach:_v,checkUpdate:()=>n.check(),applyUpdate:(i,s)=>n.apply(i,s),formatUpdateStatus:Av};return o}import{existsSync as lL,unlinkSync as dL}from"fs";import{connect as BT}from"net";At();var UT=2000,WT=200,GT=300;function Nv(e){return{lookup(t){let r=t.toLowerCase();return["","http","tcp","command","process"].includes(r)||e.some((o)=>o.name.toLowerCase()===r)?{check:(o,i)=>KT({...o,type:t},i.pid,i.ports,i.workDir,i.env,e)}:void 0}}}async function KT(e,t,r,n,o,i=[]){let s=e.timeout_seconds>0?e.timeout_seconds*1000:UT,a=e.type.toLowerCase(),c=i.find((l)=>l.name.toLowerCase()===a);if(c)try{let l=await c.check(e,{pid:t,ports:r,workDir:n,env:o});return{status:l.status,message:l.message}}catch(l){return{status:Oe,message:l instanceof Error?l.message:String(l)}}if(a==="http")return qT(e.url,s);if(a==="tcp")return zT(VT(e.address,r),s);if(a==="command")return YT(e.command.args,e.command.shell,n,o,s);if(a==="process"||a===""){if(t>0&&Qt(t))return{status:Bt,message:"process running"};return{status:Oe,message:"process not running"}}return{status:Oe,message:`unknown health type ${e.type}`}}async function qT(e,t){let r=new AbortController,n=setTimeout(()=>r.abort(),t);try{let o=await fetch(e,{signal:r.signal});if(o.status>=WT&&o.status<GT)return{status:Bt,message:String(o.status)};return{status:Oe,message:String(o.status)}}catch(o){return{status:Oe,message:o instanceof Error?o.message:String(o)}}finally{clearTimeout(n)}}function VT(e,t){if(e!=="")return e;if(t.http!==void 0)return`127.0.0.1:${t.http}`;let r=Object.values(t)[0];return r!==void 0?`127.0.0.1:${r}`:""}function zT(e,t){return new Promise((r)=>{if(e===""){r({status:Oe,message:"no tcp address"});return}let n=e.lastIndexOf(":"),o=Number.parseInt(e.slice(n+1),10),i=e.slice(0,n).replace(/^\[|\]$/g,"");if(n<1||!Number.isFinite(o)){r({status:Oe,message:`invalid tcp address ${e}`});return}let s=BT({host:i,port:o},()=>{s.end(),r({status:Bt,message:"connected"})});s.setTimeout(t),s.on("error",(a)=>{r({status:Oe,message:a.message})}),s.on("timeout",()=>{s.destroy(),r({status:Oe,message:"timeout"})})})}async function YT(e,t,r,n,o){if(e.length===0)return{status:Oe,message:"empty health command"};let i=t?process.platform==="win32"?["cmd.exe","/c",e.join(" ")]:["/bin/sh","-c",e.join(" ")]:e,s;try{s=Bun.spawn({cmd:i,cwd:r===""?void 0:r,env:n,stdout:"ignore",stderr:"ignore"})}catch(l){return{status:Oe,message:l instanceof Error?l.message:String(l)}}let a=setTimeout(()=>s.kill("SIGKILL"),o),c=await s.exited;if(clearTimeout(a),c===0)return{status:Bt,message:"ok"};return{status:Oe,message:`exit ${c}`}}te();var{spawn:Qv}=globalThis.Bun;var{spawn:JT}=globalThis.Bun;async function Hv(e,t){if(e<=0)return;try{process.kill(-e,t);return}catch{try{process.kill(e,t)}catch{return}}}async function jv(e){if(!Qt(e))return;let t=await os(["ps","-p",String(e),"-o","command="]),r=Date.now(),n=XT(await os(["ps","-p",String(e),"-o","etime="])),o=await QT(e);return{pid:e,command:t.trim(),cwd:o,startTime:n===void 0?void 0:new Date(r-n).toISOString()}}function XT(e){let t=e.trim();if(t==="")return;let r=t.indexOf("-"),n=r>=0?t.slice(0,r):"0",i=(r>=0?t.slice(r+1):t).split(":");if(i.length!==2&&i.length!==3)return;let s=Number(n),a=i.length===3?Number(i[0]):0,c=Number(i.length===3?i[1]:i[0]),l=Number(i.at(-1));if(!Number.isInteger(s)||s<0||!Number.isInteger(a)||a<0||a>23||!Number.isInteger(c)||c<0||c>59||!Number.isInteger(l)||l<0||l>59)return;return(((s*24+a)*60+c)*60+l)*1000}async function QT(e){let r=(await os(["lsof","-a","-p",String(e),"-d","cwd","-Fn"])).split(`
2252
+ `).find((i)=>i.startsWith("n"));if(r&&r.length>1)return r.slice(1).trim();let n=await os(["pwdx",String(e)]),o=n.indexOf(":");if(o>=0)return n.slice(o+1).trim();return""}async function os(e){try{let t=JT({cmd:e,stdout:"pipe",stderr:"ignore"}),r=t.stdout?await new Response(t.stdout).text():"";return await t.exited,r}catch{return""}}async function Bv(e){let t=new Map;if(e.length===0)return t;let r=await os(["ps","-o","pid=,pcpu=,rss=","-p",e.join(",")]);for(let n of r.split(`
2253
+ `)){let o=n.trim();if(o==="")continue;let[i,s,a]=o.split(/\s+/),c=Number(i),l=Number(s),d=Number(a);if(Number.isFinite(c)&&Number.isFinite(l)&&Number.isFinite(d))t.set(c,{pid:c,cpuPercent:l,memoryKB:d})}return t}function Uv(e,t){if(e.length===0||t==="")return!1;let r=e.join(" ");if(t.includes(r))return!0;let o=(e[0]??"").split(/[/\\]/).pop()??"";if(o!==""&&t.includes(o))return!0;let i=e[e.length-1]??"";return i!==""&&t.includes(i)}var{spawn:Wv}=globalThis.Bun;var ZT=1024,e0=3000;async function Gv(e,t){if(e<=0)return;try{await Wv({cmd:["taskkill","/PID",String(e),"/T","/F"],stdout:"ignore",stderr:"ignore"}).exited}catch{return}}async function Kv(e){let t=t0(od(e),e);if(t)return t;if(!Qt(e))return;let r=await _u(["powershell","-NoProfile","-Command",`Get-CimInstance Win32_Process -Filter "ProcessId=${e}" | Select-Object CommandLine,ExecutablePath,CreationDate | ConvertTo-Json -Compress`]),n=n0(r);if(n&&n.command!=="")return{pid:e,command:n.command,cwd:n.cwd,startTime:n.startTime};let o=await _u(["powershell","-NoProfile","-Command",`Get-Process -Id ${e} -ErrorAction SilentlyContinue | Select-Object Path,StartTime | ConvertTo-Json -Compress`]),i=r0(o);if(!i&&!n)return;return{pid:e,command:n?.command||i?.command||"",cwd:n?.cwd||i?.cwd||"",startTime:n?.startTime||i?.startTime}}function t0(e,t){let r=String(t);for(let n of e.split(/\r?\n/)){if(!n.includes(r))continue;let i=n.split(",").map((s)=>s.replace(/^"|"$/g,"").trim())[0]??"";if(i===""||i.toLowerCase().includes("info:"))continue;return{pid:t,command:i,cwd:""}}return}function r0(e){let t=e.trim();if(t==="")return;try{let r=JSON.parse(t),n=(r.Path||"").trim();if(n==="")return;return{command:n,cwd:gc(n),startTime:r.StartTime}}catch{let r=hc(t,"Path");if(r==="")return;return{command:r,cwd:gc(r)}}}function n0(e){let t=e.trim();if(t==="")return;try{let r=JSON.parse(t),n=(r.CommandLine||r.ExecutablePath||"").trim(),o=(r.ExecutablePath||"").trim(),i=gc(o);return{command:n,cwd:i,startTime:r.CreationDate||void 0}}catch{return{command:hc(t,"CommandLine")||hc(t,"ExecutablePath"),cwd:gc(hc(t,"ExecutablePath"))}}}async function qv(e){let t=new Map;if(e.length===0)return t;let r=e.filter((o)=>o>0).join(",");if(r==="")return t;let n=await _u(["powershell","-NoProfile","-Command",`${r}.Split(',') | ForEach-Object { Get-Process -Id $_ -ErrorAction SilentlyContinue } | Select-Object Id,CPU,WorkingSet64 | ConvertTo-Json -Compress`]);return o0(n)}function o0(e){let t=new Map,r=e.trim();if(r==="")return t;try{let n=JSON.parse(r),o=Array.isArray(n)?n:[n];for(let i of o){if(!i||typeof i!=="object")continue;let s=i,a=Number(s.Id),c=Number(s.CPU??0),l=Number(s.WorkingSet64??0)/ZT;if(Number.isFinite(a)&&a>0&&Number.isFinite(c)&&Number.isFinite(l))t.set(a,{pid:a,cpuPercent:c,memoryKB:l})}}catch{return t}return t}function gc(e){if(e==="")return"";let t=e.replace(/\//g,"\\"),r=t.lastIndexOf("\\");return r>0?t.slice(0,r):""}function hc(e,t){let r=`${t}=`,n=e.split(/\r?\n/).find((o)=>o.startsWith(r));return n?n.slice(r.length).trim():""}async function _u(e){let t,r=setTimeout(()=>{try{t?.kill()}catch{}},e0);try{t=Wv({cmd:e,stdout:"pipe",stderr:"ignore"});let n=t.stdout?await new Response(t.stdout).text():"";return await t.exited,n}catch{return""}finally{clearTimeout(r)}}te();var{spawn:Ru}=globalThis.Bun;async function zv(e){await a0(e.runtime,e.containerName);let t=i0(e),r=await Zo(e.runtime,t,e.env);if(r.code!==0||r.stdout.trim()==="")throw E(He,`failed to start ${e.containerName}: ${r.stderr.trim()||"container runtime returned no id"}`);let n=r.stdout.trim().split(/\s+/)[0]??"";return Xv(e.runtime,e.containerName,n,e.onLine,e.onExit)}function i0(e){let t=["run","--detach","--name",e.containerName,"--label","devctl.managed=true"];for(let[r,n]of Object.entries(e.ports)){let o=e.targetPorts[r]??n;t.push("--publish",`127.0.0.1:${n}:${o}`)}for(let r of Object.keys(e.env).sort())t.push("--env",r);for(let r of e.volumes)t.push("--volume",r);return t.push(e.image,...e.command),t}var s0=new Set(["PATH","HOME","HOSTNAME","PWD","OLDPWD","SHLVL","_","TMPDIR"]);function Yv(e){return Object.fromEntries(Object.entries(e).filter(([t])=>!s0.has(t)))}async function Jv(e,t,r,n){let o=await Zo(e,["inspect","--format","{{.Id}} {{.State.Running}}",t],process.env);if(o.code!==0)return;let[i,s]=o.stdout.trim().split(/\s+/);if(!i||s!=="true")return;return Xv(e,t,i,r,n)}function Xv(e,t,r,n,o){let i=!0,s=Ru({cmd:[e,"logs","--follow",r],stdout:"pipe",stderr:"pipe",stdin:"ignore"});Vv(s.stdout,"stdout",n),Vv(s.stderr,"stderr",n);let a=Ru({cmd:[e,"wait",r],stdout:"pipe",stderr:"pipe",stdin:"ignore"}),c=Promise.all([new Response(a.stdout).text(),a.exited]).then(async([l,d])=>{i=!1,s.kill();let u=Number.parseInt(l.trim(),10),f=Number.isFinite(u)?u:d??1,p=f===0?void 0:Error(`container exited with code ${f}`);return await Zo(e,["rm","--force",r],process.env),o?.(f,p),{code:f,err:p}});return{id:r,runtime:e,containerName:t,done:c,running:()=>i,stop:async(l)=>{let d=Math.max(1,Math.ceil(l/1000));await Zo(e,["stop","--time",String(d),r],process.env),await c}}}async function a0(e,t){let r=await Zo(e,["inspect","--format","{{.State.Running}}",t],process.env);if(r.code===0&&r.stdout.trim()==="false")await Zo(e,["rm","--force",t],process.env)}async function Zo(e,t,r){try{let n=Ru({cmd:[e,...t],env:{...process.env,...r},stdout:"pipe",stderr:"pipe",stdin:"ignore"}),[o,i,s]=await Promise.all([new Response(n.stdout).text(),new Response(n.stderr).text(),n.exited]);return{code:s??1,stdout:o,stderr:i}}catch(n){throw Pe(He,`unable to run ${e}`,n)}}async function Vv(e,t,r){if(!e||typeof e==="number"||!r)return;let n=e.getReader(),o=new TextDecoder,i="";for(;;){let s=await n.read();if(s.done)break;i+=o.decode(s.value,{stream:!0});let a=i.split(`
2254
+ `);i=a.pop()??"";for(let c of a)r(t,c.replace(/\r$/,""))}if(i!=="")r(t,i)}var c0=1e4,l0=2000,d0=500;class Eu{isRunning(e){let t=this.running.get(e);return t!==void 0&&is(t)}running=new Map;async start(e){let t=this.running.get(e.name);if(t&&is(t))return t;if(this.running.delete(e.name),e.args.length===0)throw E(He,"empty command");let r=e.shell?ty(e.args):e.args,n;try{n=Qv({cmd:r,cwd:e.workDir===""?void 0:e.workDir,env:e.env,stdout:e.captureStdout===!1?"ignore":"pipe",stderr:e.captureStderr===!1?"ignore":"pipe",stdin:"ignore",detached:process.platform!=="win32"})}catch(i){throw Pe(He,`failed to start ${e.name}`,i)}let o={name:e.name,pid:n.pid??0,startTime:new Date,workDir:e.workDir,args:[...e.args],proc:n,done:Promise.resolve({code:0})};return vc(n.stdout,"stdout",e.onLine),vc(n.stderr,"stderr",e.onLine),o.done=n.exited.then((i)=>{let s=typeof i==="number"?i:0,a=s===0?void 0:Error(`exited with code ${s}`);if(this.running.get(e.name)===o)this.running.delete(e.name);if(e.onExit)e.onExit(s,a);return{code:s,err:a}}),this.running.set(e.name,o),o}async runOnce(e){if(e.args.length===0)throw E(He,"empty command");let t;try{t=Qv({cmd:e.shell?ty(e.args):e.args,cwd:e.workDir||void 0,env:e.env,stdout:"pipe",stderr:"pipe",stdin:"ignore"})}catch(a){throw Pe(He,`failed to run ${e.name}`,a)}let r="",n="",o=(a,c)=>{if(a==="stdout")r+=`${c}
2183
2255
  `;else n+=`${c}
2184
- `;e.onLine?.(a,c)},i=[hc(t.stdout,"stdout",o),hc(t.stderr,"stderr",o)],s=await t.exited;return await Promise.all(i),{code:typeof s==="number"?s:0,stdout:r,stderr:n}}async startContainer(e){let t=this.running.get(e.name);if(t&&ns(t))return t;let r=await Gv({...e,env:Kv(e.env)}),n={name:e.name,pid:0,startTime:new Date,workDir:e.workDir,args:[...e.command],done:r.done,container:r};return r.done.finally(()=>{if(this.running.get(e.name)===n)this.running.delete(e.name)}),this.running.set(e.name,n),n}async adoptContainer(e){let t=this.running.get(e.name);if(t&&ns(t))return t;let r=await qv(e.runtime,e.containerName,e.onLine,e.onExit);if(!r)return;let n={name:e.name,pid:0,startTime:new Date,workDir:e.workDir,args:[],done:r.done,container:r};return r.done.finally(()=>{if(this.running.get(e.name)===n)this.running.delete(e.name)}),this.running.set(e.name,n),n}adopt(e){let t=this.running.get(e.name);if(t&&ns(t))return t;if(!Qt(e.pid))throw I(He,`cannot adopt ${e.name}: pid ${e.pid} is not running`);let r={name:e.name,pid:e.pid,startTime:e.startTime,workDir:e.workDir,args:[...e.args],done:Promise.resolve({code:0})};return r.done=s0(e.pid).then((n)=>{if(this.running.get(e.name)===r)this.running.delete(e.name);if(e.onExit)e.onExit(n,n===0?void 0:Error(`exited with code ${n}`));return{code:n}}),this.running.set(e.name,r),r}async stop(e,t){let r=this.running.get(e);if(!r)return;let n=t>0?t:r0;if(r.container){await r.container.stop(n),this.running.delete(e);return}if(await Yv(r.pid,"SIGTERM"),!await Zv(r.done,n)){if(await Yv(r.pid,"SIGKILL"),!await Zv(r.done,n0))throw I(He,`process ${e} did not exit after SIGKILL`)}this.running.delete(e)}get(e){return this.running.get(e)}all(){return[...this.running.values()]}}function ns(e){if(e.container)return e.container.running();if(e.proc)return e.proc.exitCode===null&&!e.proc.killed;return Qt(e.pid)}async function Yv(e,t){if(process.platform==="win32"){await jv(e,t);return}await Ov(e,t)}async function ey(e){if(process.platform==="win32")return Bv(e);return $v(e)}async function ty(e){if(process.platform==="win32")return Uv(e);return Fv(e)}var i0=2000;function os(e,t){if(e.workDir!==""&&t.cwd!==""&&Qv(e.workDir)!==Qv(t.cwd))return!1;if(!Nv(e.args,t.command))return!1;let r=Jv(e.startTime),n=Jv(t.startTime);if(r!==void 0&&n!==void 0)return Math.abs(r-n)<=i0;return!0}function Jv(e){if(e===void 0)return;if(e instanceof Date)return Number.isNaN(e.getTime())?void 0:e.getTime();let t=Date.parse(e);return Number.isNaN(t)?void 0:t}function Xv(e){if(process.platform==="win32")return["cmd.exe","/c",e.join(" ")];return["/bin/sh","-c",e.join(" ")]}function Qv(e){return e.replace(/\\/g,"/").replace(/\/+$/,"")}async function s0(e){while(Qt(e))await a0(o0);return 0}async function hc(e,t,r){if(!e||typeof e==="number"||!r)return;let n=e.getReader(),o=new TextDecoder,i="";for(;;){let{value:s,done:a}=await n.read();if(a){if(i!=="")r(t,i);return}i+=o.decode(s,{stream:!0});let c=i.split(`
2185
- `);i=c.pop()??"";for(let l of c)r(t,l.replace(/\r$/,""))}}async function Zv(e,t){return Promise.race([e.then(()=>!0),new Promise((r)=>{setTimeout(()=>r(!1),t)})])}function a0(e){return new Promise((t)=>{setTimeout(t,e)})}import{existsSync as c0,readFileSync as l0,writeFileSync as d0}from"fs";var ry={exists:(e)=>c0(e),readText:(e)=>l0(e,"utf8"),writeText:(e,t)=>d0(e,t)};ht();var _u=3,u0=3,p0=10,is={shouldRestart(e){if(!e.enabled||e.policy===ba)return!1;if(e.retryCount>=e.maxRetries)return!1;if(e.policy===Sa)return!0;return e.policy===wa&&e.exitCode!==0},fromService(e,t,r){return{policy:Ra(e.restart),enabled:e.restart.enabled!==!1,exitCode:r,retryCount:t,maxRetries:e.restart.max_retries>0?e.restart.max_retries:_u}}},Eu={shouldRestartUnhealthy(e,t=u0){return e>=t},shouldResetRestartBudget(e,t=p0){return e>=t}},gc={timeoutMs(e,t){let r=e.startup.timeout_seconds;return r>0?r*1000:t},waitForHealthy(e){return e.startup.wait_for_healthy}};Tt();te();var f0=2,m0=2000,h0=3,g0=10;class Tu{host;processes;clock;actions;generation=new Map;healthTimers=new Map;restarts=new Map;unhealthyStreak=new Map;healthyStreak=new Map;restartTimers=new Map;constructor(e,t,r,n){this.host=e;this.processes=t;this.clock=r;this.actions=n}forget(e){this.clearHealthWatch(e),this.clearRestartTimer(e),this.restarts.delete(e),this.bumpGeneration(e),this.unhealthyStreak.delete(e),this.healthyStreak.delete(e)}dispose(){this.clearAllRestartTimers();for(let e of this.generation.keys())this.bumpGeneration(e);for(let e of this.healthTimers.keys())this.clearHealthWatch(e)}onExit(e,t,r,n){if(!this.isCurrentGeneration(e,t))return;this.clearHealthWatch(e);let o=this.host().runtimes.get(e);if(o?.state===dt)return;if(o&&(o.state===Rr||o.state===_t)){this.host().setState(e,_t,Et,0,""),this.host().bus.publish(Se(sa,e,{code:r}));return}let i=this.host().cfg.services[e],s=n?.message??`exited with code ${r}`,a=this.restarts.get(e)??0;if((i?is.shouldRestart(is.fromService(i,a,r)):!1)&&i){this.host().setState(e,fr,Et,0,s),this.bumpRestartCount(e,a+1),this.host().persistState(),this.armRestart(e,i,t,a,()=>{let l=this.host().serviceProfile.get(e)??this.host().profile,d=this.host().serviceProfileEnv.get(e)??this.host().profileEnv;this.actions.startOne(e,l,d,!1).catch((u)=>this.host().log(e,"ERROR",A(u)))});return}this.host().fail(e,I("process_start",s))}startHealth(e,t,r,n,o,i,s){let a=this.healthTimers.get(e);if(a)clearInterval(a);if(t.health.type===""){this.setHealth(e,Bt,"no health check");return}let c=t.health.interval_seconds>0?t.health.interval_seconds*1000:m0,l=Date.parse(this.host().runtimes.get(e)?.startTime??"")||this.clock.unixMs(),d=()=>{(t.container&&t.health.type.toLowerCase()==="process"?Promise.resolve(this.processes.isRunning(e)?{status:Bt,message:"container running"}:{status:Oe,message:"container not running"}):Promise.resolve().then(()=>this.host().healthCheckers.lookup(t.health.type)?.check(t.health,{pid:r,ports:n,workDir:o,env:i})??{status:Oe,message:`unknown health type ${t.health.type}`})).catch((f)=>({status:Oe,message:A(f)})).then((f)=>{if(!this.isCurrentGeneration(e,s))return;let p=this.host().runtimes.get(e);if(p&&ny(p.state))return;if(f.status===Oe&&this.clock.unixMs()-l<t.health.start_period_seconds*1000){this.host().logs.append({timestamp:this.clock.isoNow(),service:e,source:"health",level:"INFO",message:`health check still in start period: ${f.message}`,pid:r});return}this.setHealth(e,f.status,f.message),this.host().logs.append({timestamp:this.clock.isoNow(),service:e,source:"health",level:v0(f.status),message:`health ${f.status} ${f.message}`,pid:r}),this.maybeRestartUnhealthy(e,t,f.status,s)})};d(),this.healthTimers.set(e,setInterval(d,c))}bumpRestartCount(e,t){this.restarts.set(e,t);let r=this.host().runtimes.get(e);if(r)r.restarts=t}resetRestartCount(e){this.healthyStreak.set(e,0),this.bumpRestartCount(e,0)}bumpGeneration(e){let t=(this.generation.get(e)??0)+1;return this.generation.set(e,t),this.healthyStreak.set(e,0),t}isCurrentGeneration(e,t){return this.generation.get(e)===t}scheduleRestart(e,t,r){this.clearRestartTimer(e);let n=setTimeout(()=>{this.restartTimers.delete(e),r()},t);this.restartTimers.set(e,n)}armRestart(e,t,r,n,o){let i=t.restart.backoff_seconds>0?t.restart.backoff_seconds:f0;this.scheduleRestart(e,i*2**n*1000,()=>{if(this.isCurrentGeneration(e,r))o()})}clearRestartTimer(e){let t=this.restartTimers.get(e);if(t)clearTimeout(t),this.restartTimers.delete(e)}clearAllRestartTimers(){for(let e of this.restartTimers.values())clearTimeout(e);this.restartTimers.clear()}setHealth(e,t,r){let n=this.host().runtimes.get(e);if(!n||ny(n.state))return;if(n.health=t,n.state===je||n.state===pr||n.state===Br)n.state=t===Bt?pr:t===Oe?Br:n.state;this.host().bus.publish(Se(Im,e,{health:t,message:r}))}maybeRestartUnhealthy(e,t,r,n){if(r!==Oe){if(this.unhealthyStreak.set(e,0),r===Bt)this.maybeForgiveRestarts(e,t);return}this.healthyStreak.set(e,0);let o=t.restart.policy||(t.restart.enabled?"on_failure":"never");if(o!=="on_failure"&&o!=="always")return;if(this.restartTimers.has(e))return;let i=(this.unhealthyStreak.get(e)??0)+1;this.unhealthyStreak.set(e,i);let s=t.health.unhealthy_threshold>0?t.health.unhealthy_threshold:h0;if(!Eu.shouldRestartUnhealthy(i,s))return;this.unhealthyStreak.set(e,0);let a=this.restarts.get(e)??0,c=t.restart.max_retries>0?t.restart.max_retries:_u;if(a>=c){this.host().log(e,"WARN",`unhealthy after ${i} consecutive checks but the restart limit (${c}) has already been reached; not restarting`);return}this.bumpRestartCount(e,a+1),this.host().log(e,"WARN",`restarting after ${i} consecutive unhealthy checks (attempt ${a+1}/${c})`),this.armRestart(e,t,n,a,()=>{this.actions.restart([e],{auto:!0}).catch((l)=>this.host().log(e,"ERROR",A(l)))})}maybeForgiveRestarts(e,t){let r=(this.healthyStreak.get(e)??0)+1;this.healthyStreak.set(e,r);let n=t.health.healthy_reset_threshold>0?t.health.healthy_reset_threshold:g0;if(Eu.shouldResetRestartBudget(r,n)&&(this.restarts.get(e)??0)>0)this.host().log(e,"INFO",`restart count reset after ${r} consecutive healthy checks`),this.bumpRestartCount(e,0)}clearHealthWatch(e){let t=this.healthTimers.get(e);if(t)clearInterval(t),this.healthTimers.delete(e)}}function ny(e){return e===fr||e===Rr||e===_t||e===dt}function v0(e){return e===Oe?"WARN":e===Bt?"INFO":"DEBUG"}ht();te();jr();te();Tt();var Au=[Rr,dt,fr,je,_t],y0={[Jg]:[_t,on,je,dt,fr,Rr],[_t]:[on,je,dt],[on]:[je,dt,_t,Rr],[je]:Au,[pr]:Au,[Br]:Au,[Rr]:[_t,dt],[dt]:[on,_t,je],[fr]:[on,je,dt,_t]};function ss(e,t){if(e===t)return!0;return(y0[e]??[]).includes(t)}function oy(e,t){if(!ss(e,t))throw I(Q,`illegal service lifecycle transition ${e} \u2192 ${t}`);return t}Tt();var x0=100,iy=30000;class Lu{processes;clock;session;health;constructor(e,t){this.processes=e;this.clock=t;this.health=new Tu(()=>this.host(),e,t,{startOne:(r,n,o,i)=>this.startOne(r,n,o,i),restart:(r,n)=>this.restart(r,n)})}bind(e){this.session=e}host(){if(!this.session)throw I(He,"service orchestrator is not bound");return this.session}planStart(e,t,r){return tc(e,t,r)}planStop(e,t,r=!1){return r?nc(e,t):rc(e,t)}applyLifecycle(e,t){return oy(e,t)}isLegalLifecycle(e,t){return ss(e,t)}async start(e){let t=this.host();if(e.detach===!0)t.detached=!0;let r=ic(t.cfg,{services:e.services,profile:e.profile?ur(e.profile):void 0,activeProfile:ur(t.profile)});if(r.profile)t.profile=r.profile,t.profileEnv=r.env;if(e.client_env)for(let a of r.services)t.clientEnv.set(a,e.client_env);if(e.auto!==!0)for(let a of r.services)this.health.resetRestartCount(a);let n=this.planStart(t.cfg,r.services,r.profile),o=await t.detectGoogle(t.cfg.google.project_id);n.blockers=Eg(t.cfg,n.waves.flat(),o.adcAvailable);let i=new Set(n.blockers.map((a)=>a.name));for(let a of n.blockers)await t.fail(a.name,I(He,a.message));if(t.cfg.proxy.enabled&&!t.proxySuppressed)await t.startProxy().catch((a)=>t.log("devctl","ERROR",A(a)));let s=[];for(let a of n.waves.flat()){if(i.has(a)||await t.claimIfAlreadyUp(a))continue;s.push(a)}for(let a of s)if(e.profile||!t.serviceProfile.has(a))t.serviceProfile.set(a,r.profile);if(s.length>0)await t.assignPendingPorts(s);for(let a of n.waves){let c=a.filter((l)=>s.includes(l));if(c.length>0){let l=await Promise.allSettled(c.map((u)=>this.startOne(u,r.profile,r.env,e.auto!==!0))),d=!1;for(let u of l)if(u.status==="rejected")d=!0,t.log("devctl","ERROR",A(u.reason));if(d)throw I(He,"one or more services failed to start")}try{await this.awaitWaveHealth(a,n.waves.flat())}catch(l){throw t.log("devctl","ERROR",A(l)),l}}return t.persistState(),n}async stop(e){let t=this.host(),r=e;if(r.length===0)r=[...t.runtimes.entries()].filter(([,s])=>s.state!==_t).map(([s])=>s);if(r.length===0)return;let n=r.filter((s)=>!t.cfg.services[s]&&t.runtimes.has(s)),o=r.filter((s)=>!t.cfg.services[s]&&!t.runtimes.has(s));if(o.length>0)throw I(Ho,`unknown service "${o[0]}"`);let i=r.filter((s)=>t.cfg.services[s]!==void 0);if(i.length>0)await this.runStopPlan(this.planStop(t.cfg,i),{resetRestartCounts:!0});if(n.length>0){await this.runStopPlan({profile:t.profile,steps:[],waves:n.map((s)=>[s])},{resetRestartCounts:!0});for(let s of n)t.forgetService(s)}}async restart(e,t){let r=this.host(),n=t?.cascade===!0,o=n?lo(r.cfg,e):e,i=n?this.planStop(r.cfg,e):this.planStop(r.cfg,e,!0),s=t?.auto!==!0;await this.runStopPlan(i,{resetRestartCounts:s}),await this.start({services:o,client_env:t?.clientEnv,auto:t?.auto})}async runStopPlan(e,t){let r=this.host();for(let i of e.waves.flat())if(this.health.clearRestartTimer(i),this.health.bumpGeneration(i),t?.resetRestartCounts)this.health.resetRestartCount(i);let n=bn(r.cfg.shutdown)*1000,o=[];for(let i of e.waves){let s=await Promise.allSettled(i.map(async(a)=>{r.setState(a,Rr,Et,0,""),this.health.clearHealthWatch(a);try{await this.processes.stop(a,n)}catch(c){throw r.setState(a,dt,Et,0,A(c)),r.bus.publish(Se(aa,a,{error:A(c)})),c}try{await r.releasePorts(a)}catch(c){r.log(a,"WARN",A(c))}r.setState(a,_t,Et,0,""),r.bus.publish(Se(sa,a,{}))}));for(let a=0;a<s.length;a+=1){let c=s[a];if(c?.status==="rejected"){let l=i[a]??"";o.push(l),r.log(l||"devctl","ERROR",A(c.reason))}}}if(r.persistState(),o.length>0)throw I(He,`failed to stop: ${o.join(", ")}`)}serviceIsActive(e){if(this.processes.isRunning(e))return!0;let t=this.host().runtimes.get(e);if(!t)return!1;if(t.state===fr||t.state===Rr||t.state===_t||t.state===dt)return!1;if(t.health===Bt||t.state===pr)return!0;return t.state===on||t.state===je}async startOne(e,t,r,n=!1){let o=this.host();if(this.serviceIsActive(e))return;let i=o.cfg.services[e];if(!i)throw I(lt,`unknown service ${e}`);o.setState(e,on,Et,0,"");let s=this.health.bumpGeneration(e),a=o.serviceProfile.get(e)??t,c=a!==""?uo(o.cfg,a):{...r};o.serviceProfileEnv.set(e,c);let l={},d={},u="",f;try{await o.prepareServiceIdentity(e,i),l=o.ports.get(e)??{};let p=await o.resolveServiceExecution(e,i,a,c,o.clientEnv.get(e),!i.container);if(d=p.env,u=p.workDir,n)await this.runTransient(`${e}:pre_start`,i.hooks.pre_start,i.shell,u,d);if(!this.health.isCurrentGeneration(e,s))return;let m=(v,x)=>{o.logs.append({timestamp:this.clock.isoNow(),service:e,source:v,stream:v,level:"",message:x,pid:f.pid})},h=(v,x)=>this.health.onExit(e,s,v,x);f=i.container?await this.processes.startContainer({name:e,runtime:i.container.runtime==="podman"?"podman":"docker",containerName:`${o.containerPrefix}${e.replace(/[^a-zA-Z0-9_.-]/g,"-")}`,image:i.container.image,command:[...i.command.args],env:{...d,...i.container.env},ports:l,targetPorts:i.container.ports,volumes:i.container.volumes,workDir:u,onLine:m,onExit:h}):await this.processes.start({name:e,args:[...i.command.args],shell:i.shell||i.command.shell,workDir:u,env:d,graceMs:bn(o.cfg.shutdown)*1000,captureStdout:vh(i),captureStderr:yh(i),onLine:m,onExit:h})}catch(p){if(this.health.isCurrentGeneration(e,s))await o.fail(e,p);throw p}if(!this.health.isCurrentGeneration(e,s)){if(this.processes.get(e)===f)await this.processes.stop(e,bn(o.cfg.shutdown)*1000).catch(()=>{});return}if(o.processMeta.set(e,{command:[...i.command.args],cwd:u,startTime:f.startTime}),o.setState(e,je,Et,f.pid,""),o.bus.publish(Se(Lm,e,{pid:f.pid})),n)try{await this.runTransient(`${e}:post_start`,i.hooks.post_start,i.shell,u,d)}catch(p){throw await o.fail(e,p),p}if(!this.health.isCurrentGeneration(e,s))return;if(this.health.startHealth(e,i,f.pid,l,u,d,s),o.persistState(),o.clearRestartRequired([e]),gc.waitForHealthy(i)){let p=gc.timeoutMs(i,iy);try{await this.waitHealthy(e,p)}catch(m){throw await o.fail(e,m),m}}}async awaitWaveHealth(e,t){for(let r of e){let n=this.host().cfg.services[r],o=t.some((s)=>(this.host().cfg.services[s]?.dependencies??[]).some((a)=>zt(a)===r&&gn(a)==="service_healthy"));if(!n||n.health.type===""||!o)continue;let i=gc.timeoutMs(n,iy);try{await this.waitHealthy(r,i)}catch(s){throw await this.host().fail(r,s),s}}}async waitHealthy(e,t){let r=this.clock.unixMs()+t;while(this.clock.unixMs()<r){let n=this.host().runtimes.get(e);if(n?.health===Bt)return;if(n?.state===dt)throw I(Ed,`service ${e} failed while waiting for healthy`);await b0(x0)}throw I(Ed,`service ${e} did not become healthy in time`)}async runTransient(e,t,r,n,o){if(yn(t))return{code:0,stdout:"",stderr:""};this.host().log(e,"INFO",`running ${t.args.join(" ")}`);let i=await this.processes.runOnce({name:e,args:[...t.args],shell:r||t.shell,workDir:n,env:o,graceMs:bn(this.host().cfg.shutdown)*1000,onLine:(s,a)=>this.host().logs.append({timestamp:this.clock.isoNow(),service:e,source:s,stream:s,level:"",message:a,pid:0})});if(i.code!==0)throw I(He,`${e} exited with code ${i.code}`);return i}}function b0(e){return new Promise((t)=>setTimeout(t,e))}te();function as(e){return async(t)=>{let r=t.includes("/versions/")?t:`${t}/versions/latest`,n=await e(),o=await fetch(`https://secretmanager.googleapis.com/v1/${r}:access`,{headers:{Authorization:`Bearer ${n}`}});if(!o.ok)throw I(Q,`secret manager request failed for ${t}: HTTP ${o.status}`);let i=await o.json();if(!i.payload?.data)throw I(Q,`secret manager response for ${t} had no payload`);return Buffer.from(i.payload.data,"base64").toString("utf8")}}Tt();te();async function vc(e,t,r,n){let o="";if(e.proxy?.isRunning())o=`http://${e.proxy.address()}`;else if(e.cfg.proxy.enabled)o=`http://${xn(e.cfg.proxy.listen)}`;let i=es(t,"127.0.0.1",n,o,e.cfg.project.name);if(i.DEVCTL_INTERNAL_TOKEN=e.internalTok,e.cfg.proxy.token_endpoint.enabled)i.DEVCTL_TOKEN_URL=e.boundTokenURL||`http://127.0.0.1:${e.tokenEP?.listenPort()||e.cfg.proxy.token_endpoint.port}/token`;try{let s=await Zi(e.cfg.repoRoot,{service:t,profile:e.serviceProfile.get(t)??e.profile,serviceCfg:r,profileEnv:e.serviceProfileEnv.get(t)??e.profileEnv,assignedPorts:n,runtime:i,cfg:e.cfg,fetchSecret:as(async()=>(await e.tokens.get("user","",[])).accessToken),pluginSources:e.registry?.environmentSources});return Qo(s)}catch(s){return e.log(t,"WARN",`could not fully reconstruct environment for adopted service's health check (${A(s)}); using a baseline environment`),Qo(i)}}function sy(e,t,r,n,o,i){if(e.procs.get(t)&&e.processAliveFn(e.procs.get(t)?.pid??0))return;if(!e.processAliveFn(r)||r===process.pid)return;let s=e.orchestrator.health.bumpGeneration(t);try{e.procs.adopt({name:t,pid:r,args:n,workDir:o,startTime:i,onExit:(a,c)=>{e.orchestrator.health.onExit(t,s,a,c)}})}catch(a){let c=a instanceof Error?a.message:String(a);e.log(t,"WARN",`adopt pid ${r} failed (${c}); tracking leftover in snapshot only`)}return e.processMeta.set(t,{command:n,cwd:o,startTime:i}),e.persistState(),s}async function ay(e,t){let r=e.cfg.services[t];if(e.orchestrator.serviceIsActive(t)){if(r&&!e.ports.has(t)){let a=await ya(r);if(a)e.ports.set(t,a)}return!0}if(!r)return!1;if(r.container){let a=e.orchestrator.health.bumpGeneration(t),c=r.container.runtime==="podman"?"podman":"docker",l=e.serviceWorkDir(r),d=await e.procs.adoptContainer({name:t,runtime:c,containerName:`devctl-${ar(e.cfg.repoRoot)}-${t.replace(/[^a-zA-Z0-9_.-]/g,"-")}`,workDir:l,onLine:(p,m)=>e.logs.append({timestamp:e.clock.isoNow(),service:t,source:p,stream:p,level:"",message:m,pid:0}),onExit:(p,m)=>e.orchestrator.health.onExit(t,a,p,m)});if(!d)return!1;let u=e.ports.get(t)??Object.fromEntries(r.ports.filter((p)=>!p.auto).map((p)=>[p.name,p.value]));e.ports.set(t,u),e.processMeta.set(t,{command:[...r.command.args],cwd:l,startTime:d.startTime}),yc(e,t,xc(e,fn(e.cfg.repoRoot)?.processes.find((p)=>p.name===t))),e.setState(t,je,Et,0,"");let f=await vc(e,t,r,u);return e.orchestrator.health.startHealth(t,r,0,u,l,f,a),e.persistState(),e.log(t,"INFO",`claimed running ${c} container ${d.container?.id??""}`),!0}let n=await ya(r);if(!n)return!1;let o=Object.values(n)[0],s=(o===void 0?void 0:await Yn(o))?.pid??0;if(s>0&&s!==process.pid){let a=fn(e.cfg.repoRoot)?.processes.find((d)=>d.name===t&&d.pid===s);if(!a)return e.log(t,"WARN",`port ${o} is held by pid ${s} with no persisted record for ${t}; not adopting`),!1;let c=await e.inspectProcessFn(s);if(c!==void 0&&c.command!==""&&os({args:[...r.command.args],workDir:e.serviceWorkDir(r),startTime:new Date(a.startTime)},c)){e.ports.set(t,n);let d=sy(e,t,s,[...r.command.args],e.serviceWorkDir(r),new Date(a.startTime))??e.orchestrator.health.bumpGeneration(t);yc(e,t,xc(e,a)),e.setState(t,je,Et,s,""),e.log(t,"INFO",`already listening on ${Object.values(n).join(", ")}; not starting again`);let u=e.serviceWorkDir(r),f=await vc(e,t,r,n);return e.orchestrator.health.startHealth(t,r,s,n,u,f,d),!0}e.log(t,"WARN",`port ${o} is in use by an unrelated process (pid ${s}); not adopting`)}return!1}async function cy(e){let t=fn(e.cfg.repoRoot);if(!t)return;w0(e,t.profile);let r=[];for(let[n,o]of Object.entries(e.cfg.services)){if(!o.container)continue;let i=t.processes.find((u)=>u.name===n),s=e.orchestrator.health.bumpGeneration(n),a=o.container.runtime==="podman"?"podman":"docker",c=await e.procs.adoptContainer({name:n,runtime:a,containerName:`devctl-${ar(e.cfg.repoRoot)}-${n.replace(/[^a-zA-Z0-9_.-]/g,"-")}`,workDir:e.serviceWorkDir(o),onLine:(u,f)=>e.logs.append({timestamp:e.clock.isoNow(),service:n,source:u,stream:u,level:"",message:f,pid:0}),onExit:(u,f)=>e.orchestrator.health.onExit(n,s,u,f)});if(!c)continue;let l=i?.ports??Object.fromEntries(o.ports.filter((u)=>!u.auto).map((u)=>[u.name,u.value]));e.ports.set(n,l),e.processMeta.set(n,{command:[...o.command.args],cwd:e.serviceWorkDir(o),startTime:i?.startTime?new Date(i.startTime):c.startTime}),yc(e,n,xc(e,i)),e.setState(n,je,Et,0,"");let d=await vc(e,n,o,l);e.orchestrator.health.startHealth(n,o,0,l,e.serviceWorkDir(o),d,s),e.log(n,"INFO",`adopted ${a} container ${c.container?.id??""}`),r.push(n)}for(let n of t.processes){if(!e.cfg.services[n.name]||e.cfg.services[n.name]?.container||n.pid<=0||n.pid===process.pid||!e.processAliveFn(n.pid))continue;let o=await e.inspectProcessFn(n.pid);if(!(o!==void 0&&o.command!==""&&os({args:n.command,workDir:n.cwd,startTime:n.startTime?new Date(n.startTime):void 0},o))){if(Object.values(n.ports).length>0&&await ya({ports:Object.entries(n.ports).map(([l,d])=>({name:l,value:d,auto:!1}))})!==void 0)e.log(n.name,"WARN",`pid ${n.pid} does not match stored command; leftover listener not adopted`);else e.log(n.name,"WARN",`pid ${n.pid} is a different process; not adopting`);continue}let s=sy(e,n.name,n.pid,n.command,n.cwd,new Date(n.startTime||e.clock.unixMs()))??e.orchestrator.health.bumpGeneration(n.name);if(Object.keys(n.ports).length>0)e.ports.set(n.name,n.ports);yc(e,n.name,xc(e,n)),e.setState(n.name,je,Et,n.pid,"");let a=e.cfg.services[n.name];if(a){let c=n.cwd||e.serviceWorkDir(a),l=await vc(e,n.name,a,n.ports);e.orchestrator.health.startHealth(n.name,a,n.pid,n.ports,c,l,s)}e.log(n.name,"INFO","adopted leftover process; stdout/stderr from before adopt are not captured"),r.push(n.name)}if(r.length>0)e.bus.publish(Se($m,"",{services:r,session_id:t.session_id})),e.log("devctl","INFO",`recovered session processes: ${r.join(", ")}`)}function w0(e,t){e.profile=t||e.profile,e.profileEnv=uo(e.cfg,e.profile)}function yc(e,t,r){e.serviceProfile.set(t,r),e.serviceProfileEnv.set(t,uo(e.cfg,r))}function xc(e,t){return t?.profile??e.profile}import{statSync as $0,watch as F0}from"fs";import{isAbsolute as N0,join as H0,resolve as j0}from"path";function ly(e,t){return t}function Du(e,t){let r={},n=new Set,o=new Set([...Object.keys(e.services),...Object.keys(t.services)]);for(let s of o){let a=e.services[s],c=t.services[s],l=[];if(!a||!c)l.push("presence"),n.add(s);else{if(a.command.args.join("\x00")!==c.command.args.join("\x00")||a.command.shell!==c.command.shell||a.shell!==c.shell)l.push("command"),n.add(s);if(a.working_dir!==c.working_dir)l.push("working_dir"),n.add(s);if(JSON.stringify(a.environment)!==JSON.stringify(c.environment))l.push("environment"),n.add(s);if(JSON.stringify(a.ports)!==JSON.stringify(c.ports))l.push("ports"),n.add(s);if(JSON.stringify(a.identity)!==JSON.stringify(c.identity))l.push("identity"),n.add(s);if(JSON.stringify(a.health)!==JSON.stringify(c.health))l.push("health"),n.add(s);if(JSON.stringify(a.restart)!==JSON.stringify(c.restart))l.push("restart"),n.add(s);if(JSON.stringify(a.startup)!==JSON.stringify(c.startup))l.push("startup"),n.add(s);if(JSON.stringify(a.logs)!==JSON.stringify(c.logs))l.push("logs"),n.add(s);if(JSON.stringify(a.container)!==JSON.stringify(c.container))l.push("container"),n.add(s);if(JSON.stringify(a.watch)!==JSON.stringify(c.watch))l.push("watch"),n.add(s)}if(l.length>0)r[s]=l}let i=[];if(JSON.stringify(e.logs)!==JSON.stringify(t.logs))i.push("logs");if(JSON.stringify(e.auth)!==JSON.stringify(t.auth))i.push("auth");return{restart_required:[...n].sort(),changes:r,supervisor_restart_required:i}}Tt();te();import{pathToFileURL as L0}from"url";import{isAbsolute as D0,resolve as I0}from"path";import{createServer as my,request as S0}from"http";import{request as k0}from"https";import{randomBytes as dy}from"crypto";import{Readable as C0}from"stream";te();jr();var cs="x-devctl-request-id",P0="x-devctl-internal-token",uy=100,Iu=new Set(["connection","keep-alive","proxy-authenticate","proxy-authorization","te","trailers","transfer-encoding","upgrade"]),R0=new Set(["gzip","deflate","br"]);class Mu{cfg;tokens;logs;bus;detector;resolvePort;middleware;server;running=!1;addr="";upgradedSockets=new Set;recentRequests=[];constructor(e,t,r,n,o,i=[],s){this.cfg=e,this.tokens=t,this.logs=r,this.bus=n,this.detector=o,this.middleware=i,this.resolvePort=s}upstreamBase(e){let t=e.upstream.service??"";if(t==="")return e.upstream.url;if(!this.resolvePort)throw I(Nr,`route ${e.name} addresses service ${t} but this proxy has no port resolver configured`);let r=this.resolvePort(t,e.upstream.port??"");if(r===void 0)throw I(Nr,`upstream service ${t} is not running`);return`http://127.0.0.1:${r}`}address(){return this.addr||xn(this.cfg.listen)}setMiddleware(e){this.middleware=e}isRunning(){return this.running}stats(){let e=this.recentRequests.reduce((t,r)=>t+(r.status>=400||r.error?1:0),0);return{total:this.recentRequests.length,errors:e,recent:[...this.recentRequests].reverse()}}recordRequest(e){if(this.recentRequests.push(e),this.recentRequests.length>uy)this.recentRequests.splice(0,this.recentRequests.length-uy)}start(){let e=this.cfg.listen.host||"127.0.0.1";if(!Cn(e))return Promise.reject(I(Nr,`refusing to bind proxy to ${e}`));if(this.cfg.listen.port===0)return Promise.reject(I(Nr,"proxy.listen.port is required when the proxy is enabled"));let t=this.cfg.listen.port;return new Promise((r,n)=>{this.server=my((o,i)=>{this.serve(o,i)}),this.server.on("upgrade",(o,i,s)=>{this.serveUpgrade(o,i,s)}),this.server.on("error",(o)=>n(Pe(Nr,`unable to listen on ${e}:${t}`,o))),this.server.listen(t,e,()=>{this.running=!0,this.addr=`${e}:${t}`,this.bus?.publish(Se(Mm,"",{address:this.addr})),r()})})}stop(){let e=this.server;if(!e)return Promise.resolve();for(let t of this.upgradedSockets)t.destroy();return this.upgradedSockets.clear(),new Promise((t)=>{e.close(()=>{this.running=!1,this.bus?.publish(Se(Om,"",{})),t()})})}async serveUpgrade(e,t,r){let n=Date.now(),o=e.headers[cs]?.toString()||dy(8).toString("hex"),i=e.method??"GET",s=e.url??"/",a=this.detector?this.detector.redactText(s):s,c=fy(this.cfg.routes,e);if(!c){t.end(`HTTP/1.1 404 Not Found\r
2256
+ `;e.onLine?.(a,c)},i=[vc(t.stdout,"stdout",o),vc(t.stderr,"stderr",o)],s=await t.exited;return await Promise.all(i),{code:typeof s==="number"?s:0,stdout:r,stderr:n}}async startContainer(e){let t=this.running.get(e.name);if(t&&is(t))return t;let r=await zv({...e,env:Yv(e.env)}),n={name:e.name,pid:0,startTime:new Date,workDir:e.workDir,args:[...e.command],done:r.done,container:r};return r.done.finally(()=>{if(this.running.get(e.name)===n)this.running.delete(e.name)}),this.running.set(e.name,n),n}async adoptContainer(e){let t=this.running.get(e.name);if(t&&is(t))return t;let r=await Jv(e.runtime,e.containerName,e.onLine,e.onExit);if(!r)return;let n={name:e.name,pid:0,startTime:new Date,workDir:e.workDir,args:[],done:r.done,container:r};return r.done.finally(()=>{if(this.running.get(e.name)===n)this.running.delete(e.name)}),this.running.set(e.name,n),n}adopt(e){let t=this.running.get(e.name);if(t&&is(t))return t;if(!Qt(e.pid))throw E(He,`cannot adopt ${e.name}: pid ${e.pid} is not running`);let r={name:e.name,pid:e.pid,startTime:e.startTime,workDir:e.workDir,args:[...e.args],done:Promise.resolve({code:0})};return r.done=p0(e.pid).then((n)=>{if(this.running.get(e.name)===r)this.running.delete(e.name);if(e.onExit)e.onExit(n,n===0?void 0:Error(`exited with code ${n}`));return{code:n}}),this.running.set(e.name,r),r}async stop(e,t){let r=this.running.get(e);if(!r)return;let n=t>0?t:c0;if(r.container){await r.container.stop(n),this.running.delete(e);return}if(await Zv(r.pid,"SIGTERM"),!await ny(r.done,n)){if(await Zv(r.pid,"SIGKILL"),!await ny(r.done,l0))throw E(He,`process ${e} did not exit after SIGKILL`)}this.running.delete(e)}get(e){return this.running.get(e)}all(){return[...this.running.values()]}}function is(e){if(e.container)return e.container.running();if(e.proc)return e.proc.exitCode===null&&!e.proc.killed;return Qt(e.pid)}async function Zv(e,t){if(process.platform==="win32"){await Gv(e,t);return}await Hv(e,t)}async function oy(e){if(process.platform==="win32")return Kv(e);return jv(e)}async function iy(e){if(process.platform==="win32")return qv(e);return Bv(e)}var u0=2000;function ss(e,t){if(e.workDir!==""&&t.cwd!==""&&ry(e.workDir)!==ry(t.cwd))return!1;if(!Uv(e.args,t.command))return!1;let r=ey(e.startTime),n=ey(t.startTime);if(r!==void 0&&n!==void 0)return Math.abs(r-n)<=u0;return!0}function ey(e){if(e===void 0)return;if(e instanceof Date)return Number.isNaN(e.getTime())?void 0:e.getTime();let t=Date.parse(e);return Number.isNaN(t)?void 0:t}function ty(e){if(process.platform==="win32")return["cmd.exe","/c",e.join(" ")];return["/bin/sh","-c",e.join(" ")]}function ry(e){return e.replace(/\\/g,"/").replace(/\/+$/,"")}async function p0(e){while(Qt(e))await f0(d0);return 0}async function vc(e,t,r){if(!e||typeof e==="number"||!r)return;let n=e.getReader(),o=new TextDecoder,i="";for(;;){let{value:s,done:a}=await n.read();if(a){if(i!=="")r(t,i);return}i+=o.decode(s,{stream:!0});let c=i.split(`
2257
+ `);i=c.pop()??"";for(let l of c)r(t,l.replace(/\r$/,""))}}async function ny(e,t){return Promise.race([e.then(()=>!0),new Promise((r)=>{setTimeout(()=>r(!1),t)})])}function f0(e){return new Promise((t)=>{setTimeout(t,e)})}import{existsSync as m0,readFileSync as h0,writeFileSync as g0}from"fs";var sy={exists:(e)=>m0(e),readText:(e)=>h0(e,"utf8"),writeText:(e,t)=>g0(e,t)};ht();var Tu=3,v0=3,y0=10,as={shouldRestart(e){if(!e.enabled||e.policy===Sa)return!1;if(e.retryCount>=e.maxRetries)return!1;if(e.policy===Ca)return!0;return e.policy===ka&&e.exitCode!==0},fromService(e,t,r){return{policy:Ea(e.restart),enabled:e.restart.enabled!==!1,exitCode:r,retryCount:t,maxRetries:e.restart.max_retries>0?e.restart.max_retries:Tu}}},Au={shouldRestartUnhealthy(e,t=v0){return e>=t},shouldResetRestartBudget(e,t=y0){return e>=t}},yc={timeoutMs(e,t){let r=e.startup.timeout_seconds;return r>0?r*1000:t},waitForHealthy(e){return e.startup.wait_for_healthy}};At();te();var b0=2,x0=2000,w0=3,S0=10;class Lu{host;processes;clock;actions;generation=new Map;healthTimers=new Map;restarts=new Map;unhealthyStreak=new Map;healthyStreak=new Map;restartTimers=new Map;constructor(e,t,r,n){this.host=e;this.processes=t;this.clock=r;this.actions=n}forget(e){this.clearHealthWatch(e),this.clearRestartTimer(e),this.restarts.delete(e),this.bumpGeneration(e),this.unhealthyStreak.delete(e),this.healthyStreak.delete(e)}dispose(){this.clearAllRestartTimers();for(let e of this.generation.keys())this.bumpGeneration(e);for(let e of this.healthTimers.keys())this.clearHealthWatch(e)}onExit(e,t,r,n){if(!this.isCurrentGeneration(e,t))return;this.clearHealthWatch(e);let o=this.host().runtimes.get(e);if(o?.state===dt)return;if(o&&(o.state===_r||o.state===Et)){this.host().setState(e,Et,Tt,0,""),this.host().bus.publish(Se(ca,e,{code:r}));return}let i=this.host().cfg.services[e],s=n?.message??`exited with code ${r}`,a=this.restarts.get(e)??0;if((i?as.shouldRestart(as.fromService(i,a,r)):!1)&&i){this.host().setState(e,fr,Tt,0,s),this.bumpRestartCount(e,a+1),this.host().persistState(),this.armRestart(e,i,t,a,()=>{let l=this.host().serviceProfile.get(e)??this.host().profile,d=this.host().serviceProfileEnv.get(e)??this.host().profileEnv;this.actions.startOne(e,l,d,!1).catch((u)=>this.host().log(e,"ERROR",L(u)))});return}this.host().fail(e,E("process_start",s))}startHealth(e,t,r,n,o,i,s){let a=this.healthTimers.get(e);if(a)clearInterval(a);if(t.health.type===""){this.setHealth(e,Bt,"no health check");return}let c=t.health.interval_seconds>0?t.health.interval_seconds*1000:x0,l=Date.parse(this.host().runtimes.get(e)?.startTime??"")||this.clock.unixMs(),d=()=>{(t.container&&t.health.type.toLowerCase()==="process"?Promise.resolve(this.processes.isRunning(e)?{status:Bt,message:"container running"}:{status:Oe,message:"container not running"}):Promise.resolve().then(()=>this.host().healthCheckers.lookup(t.health.type)?.check(t.health,{pid:r,ports:n,workDir:o,env:i})??{status:Oe,message:`unknown health type ${t.health.type}`})).catch((f)=>({status:Oe,message:L(f)})).then((f)=>{if(!this.isCurrentGeneration(e,s))return;let p=this.host().runtimes.get(e);if(p&&ay(p.state))return;if(f.status===Oe&&this.clock.unixMs()-l<t.health.start_period_seconds*1000){this.host().logs.append({timestamp:this.clock.isoNow(),service:e,source:"health",level:"INFO",message:`health check still in start period: ${f.message}`,pid:r});return}this.setHealth(e,f.status,f.message),this.host().logs.append({timestamp:this.clock.isoNow(),service:e,source:"health",level:k0(f.status),message:`health ${f.status} ${f.message}`,pid:r}),this.maybeRestartUnhealthy(e,t,f.status,s)})};d(),this.healthTimers.set(e,setInterval(d,c))}bumpRestartCount(e,t){this.restarts.set(e,t);let r=this.host().runtimes.get(e);if(r)r.restarts=t}resetRestartCount(e){this.healthyStreak.set(e,0),this.bumpRestartCount(e,0)}bumpGeneration(e){let t=(this.generation.get(e)??0)+1;return this.generation.set(e,t),this.healthyStreak.set(e,0),t}isCurrentGeneration(e,t){return this.generation.get(e)===t}scheduleRestart(e,t,r){this.clearRestartTimer(e);let n=setTimeout(()=>{this.restartTimers.delete(e),r()},t);this.restartTimers.set(e,n)}armRestart(e,t,r,n,o){let i=t.restart.backoff_seconds>0?t.restart.backoff_seconds:b0;this.scheduleRestart(e,i*2**n*1000,()=>{if(this.isCurrentGeneration(e,r))o()})}clearRestartTimer(e){let t=this.restartTimers.get(e);if(t)clearTimeout(t),this.restartTimers.delete(e)}clearAllRestartTimers(){for(let e of this.restartTimers.values())clearTimeout(e);this.restartTimers.clear()}setHealth(e,t,r){let n=this.host().runtimes.get(e);if(!n||ay(n.state))return;if(n.health=t,n.state===je||n.state===pr||n.state===Br)n.state=t===Bt?pr:t===Oe?Br:n.state;this.host().bus.publish(Se($m,e,{health:t,message:r}))}maybeRestartUnhealthy(e,t,r,n){if(r!==Oe){if(this.unhealthyStreak.set(e,0),r===Bt)this.maybeForgiveRestarts(e,t);return}this.healthyStreak.set(e,0);let o=t.restart.policy||(t.restart.enabled?"on_failure":"never");if(o!=="on_failure"&&o!=="always")return;if(this.restartTimers.has(e))return;let i=(this.unhealthyStreak.get(e)??0)+1;this.unhealthyStreak.set(e,i);let s=t.health.unhealthy_threshold>0?t.health.unhealthy_threshold:w0;if(!Au.shouldRestartUnhealthy(i,s))return;this.unhealthyStreak.set(e,0);let a=this.restarts.get(e)??0,c=t.restart.max_retries>0?t.restart.max_retries:Tu;if(a>=c){this.host().log(e,"WARN",`unhealthy after ${i} consecutive checks but the restart limit (${c}) has already been reached; not restarting`);return}this.bumpRestartCount(e,a+1),this.host().log(e,"WARN",`restarting after ${i} consecutive unhealthy checks (attempt ${a+1}/${c})`),this.armRestart(e,t,n,a,()=>{this.actions.restart([e],{auto:!0}).catch((l)=>this.host().log(e,"ERROR",L(l)))})}maybeForgiveRestarts(e,t){let r=(this.healthyStreak.get(e)??0)+1;this.healthyStreak.set(e,r);let n=t.health.healthy_reset_threshold>0?t.health.healthy_reset_threshold:S0;if(Au.shouldResetRestartBudget(r,n)&&(this.restarts.get(e)??0)>0)this.host().log(e,"INFO",`restart count reset after ${r} consecutive healthy checks`),this.bumpRestartCount(e,0)}clearHealthWatch(e){let t=this.healthTimers.get(e);if(t)clearInterval(t),this.healthTimers.delete(e)}}function ay(e){return e===fr||e===_r||e===Et||e===dt}function k0(e){return e===Oe?"WARN":e===Bt?"INFO":"DEBUG"}ht();te();jr();te();At();var Du=[_r,dt,fr,je,Et],C0={[ev]:[Et,on,je,dt,fr,_r],[Et]:[on,je,dt],[on]:[je,dt,Et,_r],[je]:Du,[pr]:Du,[Br]:Du,[_r]:[Et,dt],[dt]:[on,Et,je],[fr]:[on,je,dt,Et]};function cs(e,t){if(e===t)return!0;return(C0[e]??[]).includes(t)}function cy(e,t){if(!cs(e,t))throw E(Y,`illegal service lifecycle transition ${e} \u2192 ${t}`);return t}At();var P0=100,ly=30000;class Iu{processes;clock;session;health;constructor(e,t){this.processes=e;this.clock=t;this.health=new Lu(()=>this.host(),e,t,{startOne:(r,n,o,i)=>this.startOne(r,n,o,i),restart:(r,n)=>this.restart(r,n)})}bind(e){this.session=e}host(){if(!this.session)throw E(He,"service orchestrator is not bound");return this.session}planStart(e,t,r){return nc(e,t,r)}planStop(e,t,r=!1){return r?ic(e,t):oc(e,t)}applyLifecycle(e,t){return cy(e,t)}isLegalLifecycle(e,t){return cs(e,t)}async start(e){let t=this.host();if(e.detach===!0)t.detached=!0;let r=ac(t.cfg,{services:e.services,profile:e.profile?ur(e.profile):void 0,activeProfile:ur(t.profile)});if(r.profile)t.profile=r.profile,t.profileEnv=r.env;if(e.client_env)for(let a of r.services)t.clientEnv.set(a,e.client_env);if(e.auto!==!0)for(let a of r.services)this.health.resetRestartCount(a);let n=this.planStart(t.cfg,r.services,r.profile),o=await t.detectGoogle(t.cfg.google.project_id);n.blockers=Dg(t.cfg,n.waves.flat(),o.adcAvailable);let i=new Set(n.blockers.map((a)=>a.name));for(let a of n.blockers)await t.fail(a.name,E(He,a.message));if(t.cfg.proxy.enabled&&!t.proxySuppressed)await t.startProxy().catch((a)=>t.log("devctl","ERROR",L(a)));let s=[];for(let a of n.waves.flat()){if(i.has(a)||await t.claimIfAlreadyUp(a))continue;s.push(a)}for(let a of s)if(e.profile||!t.serviceProfile.has(a))t.serviceProfile.set(a,r.profile);if(s.length>0)await t.assignPendingPorts(s);for(let a of n.waves){let c=a.filter((l)=>s.includes(l));if(c.length>0){let l=await Promise.allSettled(c.map((u)=>this.startOne(u,r.profile,r.env,e.auto!==!0))),d=!1;for(let u of l)if(u.status==="rejected")d=!0,t.log("devctl","ERROR",L(u.reason));if(d)throw E(He,"one or more services failed to start")}try{await this.awaitWaveHealth(a,n.waves.flat())}catch(l){throw t.log("devctl","ERROR",L(l)),l}}return t.persistState(),n}async stop(e){let t=this.host(),r=e;if(r.length===0)r=[...t.runtimes.entries()].filter(([,s])=>s.state!==Et).map(([s])=>s);if(r.length===0)return;let n=r.filter((s)=>!t.cfg.services[s]&&t.runtimes.has(s)),o=r.filter((s)=>!t.cfg.services[s]&&!t.runtimes.has(s));if(o.length>0)throw E(Ho,`unknown service "${o[0]}"`);let i=r.filter((s)=>t.cfg.services[s]!==void 0);if(i.length>0)await this.runStopPlan(this.planStop(t.cfg,i),{resetRestartCounts:!0});if(n.length>0){await this.runStopPlan({profile:t.profile,steps:[],waves:n.map((s)=>[s])},{resetRestartCounts:!0});for(let s of n)t.forgetService(s)}}async restart(e,t){let r=this.host(),n=t?.cascade===!0,o=n?co(r.cfg,e):e,i=n?this.planStop(r.cfg,e):this.planStop(r.cfg,e,!0),s=t?.auto!==!0;await this.runStopPlan(i,{resetRestartCounts:s}),await this.start({services:o,client_env:t?.clientEnv,auto:t?.auto})}async runStopPlan(e,t){let r=this.host();for(let i of e.waves.flat())if(this.health.clearRestartTimer(i),this.health.bumpGeneration(i),t?.resetRestartCounts)this.health.resetRestartCount(i);let n=xn(r.cfg.shutdown)*1000,o=[];for(let i of e.waves){let s=await Promise.allSettled(i.map(async(a)=>{r.setState(a,_r,Tt,0,""),this.health.clearHealthWatch(a);try{await this.processes.stop(a,n)}catch(c){throw r.setState(a,dt,Tt,0,L(c)),r.bus.publish(Se(la,a,{error:L(c)})),c}try{await r.releasePorts(a)}catch(c){r.log(a,"WARN",L(c))}r.setState(a,Et,Tt,0,""),r.bus.publish(Se(ca,a,{}))}));for(let a=0;a<s.length;a+=1){let c=s[a];if(c?.status==="rejected"){let l=i[a]??"";o.push(l),r.log(l||"devctl","ERROR",L(c.reason))}}}if(r.persistState(),o.length>0)throw E(He,`failed to stop: ${o.join(", ")}`)}serviceIsActive(e){if(this.processes.isRunning(e))return!0;let t=this.host().runtimes.get(e);if(!t)return!1;if(t.state===fr||t.state===_r||t.state===Et||t.state===dt)return!1;if(t.health===Bt||t.state===pr)return!0;return t.state===on||t.state===je}async startOne(e,t,r,n=!1){let o=this.host();if(this.serviceIsActive(e))return;let i=o.cfg.services[e];if(!i)throw E(lt,`unknown service ${e}`);o.setState(e,on,Tt,0,"");let s=this.health.bumpGeneration(e),a=o.serviceProfile.get(e)??t,c=a!==""?lo(o.cfg,a):{...r};o.serviceProfileEnv.set(e,c);let l={},d={},u="",f;try{await o.prepareServiceIdentity(e,i),l=o.ports.get(e)??{};let p=await o.resolveServiceExecution(e,i,a,c,o.clientEnv.get(e),!i.container);if(d=p.env,u=p.workDir,n)await this.runTransient(`${e}:pre_start`,i.hooks.pre_start,i.shell,u,d);if(!this.health.isCurrentGeneration(e,s))return;let m=(v,b)=>{o.logs.append({timestamp:this.clock.isoNow(),service:e,source:v,stream:v,level:"",message:b,pid:f.pid})},h=(v,b)=>this.health.onExit(e,s,v,b);f=i.container?await this.processes.startContainer({name:e,runtime:i.container.runtime==="podman"?"podman":"docker",containerName:`${o.containerPrefix}${e.replace(/[^a-zA-Z0-9_.-]/g,"-")}`,image:i.container.image,command:[...i.command.args],env:{...d,...i.container.env},ports:l,targetPorts:i.container.ports,volumes:i.container.volumes,workDir:u,onLine:m,onExit:h}):await this.processes.start({name:e,args:[...i.command.args],shell:i.shell||i.command.shell,workDir:u,env:d,graceMs:xn(o.cfg.shutdown)*1000,captureStdout:xh(i),captureStderr:wh(i),onLine:m,onExit:h})}catch(p){if(this.health.isCurrentGeneration(e,s))await o.fail(e,p);throw p}if(!this.health.isCurrentGeneration(e,s)){if(this.processes.get(e)===f)await this.processes.stop(e,xn(o.cfg.shutdown)*1000).catch(()=>{});return}if(o.processMeta.set(e,{command:[...i.command.args],cwd:u,startTime:f.startTime}),o.setState(e,je,Tt,f.pid,""),o.bus.publish(Se(Mm,e,{pid:f.pid})),n)try{await this.runTransient(`${e}:post_start`,i.hooks.post_start,i.shell,u,d)}catch(p){throw await o.fail(e,p),p}if(!this.health.isCurrentGeneration(e,s))return;if(this.health.startHealth(e,i,f.pid,l,u,d,s),o.persistState(),o.clearRestartRequired([e]),yc.waitForHealthy(i)){let p=yc.timeoutMs(i,ly);try{await this.waitHealthy(e,p)}catch(m){throw await o.fail(e,m),m}}}async awaitWaveHealth(e,t){for(let r of e){let n=this.host().cfg.services[r],o=t.some((s)=>(this.host().cfg.services[s]?.dependencies??[]).some((a)=>zt(a)===r&&gn(a)==="service_healthy"));if(!n||n.health.type===""||!o)continue;let i=yc.timeoutMs(n,ly);try{await this.waitHealthy(r,i)}catch(s){throw await this.host().fail(r,s),s}}}async waitHealthy(e,t){let r=this.clock.unixMs()+t;while(this.clock.unixMs()<r){let n=this.host().runtimes.get(e);if(n?.health===Bt)return;if(n?.state===dt)throw E(Td,`service ${e} failed while waiting for healthy`);await _0(P0)}throw E(Td,`service ${e} did not become healthy in time`)}async runTransient(e,t,r,n,o){if(yn(t))return{code:0,stdout:"",stderr:""};this.host().log(e,"INFO",`running ${t.args.join(" ")}`);let i=await this.processes.runOnce({name:e,args:[...t.args],shell:r||t.shell,workDir:n,env:o,graceMs:xn(this.host().cfg.shutdown)*1000,onLine:(s,a)=>this.host().logs.append({timestamp:this.clock.isoNow(),service:e,source:s,stream:s,level:"",message:a,pid:0})});if(i.code!==0)throw E(He,`${e} exited with code ${i.code}`);return i}}function _0(e){return new Promise((t)=>setTimeout(t,e))}te();function ls(e){return async(t)=>{let r=t.includes("/versions/")?t:`${t}/versions/latest`,n=await e(),o=await fetch(`https://secretmanager.googleapis.com/v1/${r}:access`,{headers:{Authorization:`Bearer ${n}`}});if(!o.ok)throw E(Y,`secret manager request failed for ${t}: HTTP ${o.status}`);let i=await o.json();if(!i.payload?.data)throw E(Y,`secret manager response for ${t} had no payload`);return Buffer.from(i.payload.data,"base64").toString("utf8")}}At();te();async function bc(e,t,r,n){let o="";if(e.proxy?.isRunning())o=`http://${e.proxy.address()}`;else if(e.cfg.proxy.enabled)o=`http://${bn(e.cfg.proxy.listen)}`;let i=rs(t,"127.0.0.1",n,o,e.cfg.project.name);if(i.DEVCTL_INTERNAL_TOKEN=e.internalTok,e.cfg.proxy.token_endpoint.enabled)i.DEVCTL_TOKEN_URL=e.boundTokenURL||`http://127.0.0.1:${e.tokenEP?.listenPort()||e.cfg.proxy.token_endpoint.port}/token`;try{let s=await ts(e.cfg.repoRoot,{service:t,profile:e.serviceProfile.get(t)??e.profile,serviceCfg:r,profileEnv:e.serviceProfileEnv.get(t)??e.profileEnv,assignedPorts:n,runtime:i,cfg:e.cfg,fetchSecret:ls(async()=>(await e.tokens.get("user","",[])).accessToken),pluginSources:e.registry?.environmentSources});return Qo(s)}catch(s){return e.log(t,"WARN",`could not fully reconstruct environment for adopted service's health check (${L(s)}); using a baseline environment`),Qo(i)}}function dy(e,t,r,n,o,i){if(e.procs.get(t)&&e.processAliveFn(e.procs.get(t)?.pid??0))return;if(!e.processAliveFn(r)||r===process.pid)return;let s=e.orchestrator.health.bumpGeneration(t);try{e.procs.adopt({name:t,pid:r,args:n,workDir:o,startTime:i,onExit:(a,c)=>{e.orchestrator.health.onExit(t,s,a,c)}})}catch(a){let c=a instanceof Error?a.message:String(a);e.log(t,"WARN",`adopt pid ${r} failed (${c}); tracking leftover in snapshot only`)}return e.processMeta.set(t,{command:n,cwd:o,startTime:i}),e.persistState(),s}async function uy(e,t){let r=e.cfg.services[t];if(e.orchestrator.serviceIsActive(t)){if(r&&!e.ports.has(t)){let a=await xa(r);if(a)e.ports.set(t,a)}return!0}if(!r)return!1;if(r.container){let a=e.orchestrator.health.bumpGeneration(t),c=r.container.runtime==="podman"?"podman":"docker",l=e.serviceWorkDir(r),d=await e.procs.adoptContainer({name:t,runtime:c,containerName:`devctl-${ar(e.cfg.repoRoot)}-${t.replace(/[^a-zA-Z0-9_.-]/g,"-")}`,workDir:l,onLine:(p,m)=>e.logs.append({timestamp:e.clock.isoNow(),service:t,source:p,stream:p,level:"",message:m,pid:0}),onExit:(p,m)=>e.orchestrator.health.onExit(t,a,p,m)});if(!d)return!1;let u=e.ports.get(t)??Object.fromEntries(r.ports.filter((p)=>!p.auto).map((p)=>[p.name,p.value]));e.ports.set(t,u),e.processMeta.set(t,{command:[...r.command.args],cwd:l,startTime:d.startTime}),xc(e,t,wc(e,fn(e.cfg.repoRoot)?.processes.find((p)=>p.name===t))),e.setState(t,je,Tt,0,"");let f=await bc(e,t,r,u);return e.orchestrator.health.startHealth(t,r,0,u,l,f,a),e.persistState(),e.log(t,"INFO",`claimed running ${c} container ${d.container?.id??""}`),!0}let n=await xa(r);if(!n)return!1;let o=Object.values(n)[0],s=(o===void 0?void 0:await zn(o))?.pid??0;if(s>0&&s!==process.pid){let a=fn(e.cfg.repoRoot)?.processes.find((d)=>d.name===t&&d.pid===s);if(!a)return e.log(t,"WARN",`port ${o} is held by pid ${s} with no persisted record for ${t}; not adopting`),!1;let c=await e.inspectProcessFn(s);if(c!==void 0&&c.command!==""&&ss({args:[...r.command.args],workDir:e.serviceWorkDir(r),startTime:new Date(a.startTime)},c)){e.ports.set(t,n);let d=dy(e,t,s,[...r.command.args],e.serviceWorkDir(r),new Date(a.startTime))??e.orchestrator.health.bumpGeneration(t);xc(e,t,wc(e,a)),e.setState(t,je,Tt,s,""),e.log(t,"INFO",`already listening on ${Object.values(n).join(", ")}; not starting again`);let u=e.serviceWorkDir(r),f=await bc(e,t,r,n);return e.orchestrator.health.startHealth(t,r,s,n,u,f,d),!0}e.log(t,"WARN",`port ${o} is in use by an unrelated process (pid ${s}); not adopting`)}return!1}async function py(e){let t=fn(e.cfg.repoRoot);if(!t)return;R0(e,t.profile);let r=[];for(let[n,o]of Object.entries(e.cfg.services)){if(!o.container)continue;let i=t.processes.find((u)=>u.name===n),s=e.orchestrator.health.bumpGeneration(n),a=o.container.runtime==="podman"?"podman":"docker",c=await e.procs.adoptContainer({name:n,runtime:a,containerName:`devctl-${ar(e.cfg.repoRoot)}-${n.replace(/[^a-zA-Z0-9_.-]/g,"-")}`,workDir:e.serviceWorkDir(o),onLine:(u,f)=>e.logs.append({timestamp:e.clock.isoNow(),service:n,source:u,stream:u,level:"",message:f,pid:0}),onExit:(u,f)=>e.orchestrator.health.onExit(n,s,u,f)});if(!c)continue;let l=i?.ports??Object.fromEntries(o.ports.filter((u)=>!u.auto).map((u)=>[u.name,u.value]));e.ports.set(n,l),e.processMeta.set(n,{command:[...o.command.args],cwd:e.serviceWorkDir(o),startTime:i?.startTime?new Date(i.startTime):c.startTime}),xc(e,n,wc(e,i)),e.setState(n,je,Tt,0,"");let d=await bc(e,n,o,l);e.orchestrator.health.startHealth(n,o,0,l,e.serviceWorkDir(o),d,s),e.log(n,"INFO",`adopted ${a} container ${c.container?.id??""}`),r.push(n)}for(let n of t.processes){if(!e.cfg.services[n.name]||e.cfg.services[n.name]?.container||n.pid<=0||n.pid===process.pid||!e.processAliveFn(n.pid))continue;let o=await e.inspectProcessFn(n.pid);if(!(o!==void 0&&o.command!==""&&ss({args:n.command,workDir:n.cwd,startTime:n.startTime?new Date(n.startTime):void 0},o))){if(Object.values(n.ports).length>0&&await xa({ports:Object.entries(n.ports).map(([l,d])=>({name:l,value:d,auto:!1}))})!==void 0)e.log(n.name,"WARN",`pid ${n.pid} does not match stored command; leftover listener not adopted`);else e.log(n.name,"WARN",`pid ${n.pid} is a different process; not adopting`);continue}let s=dy(e,n.name,n.pid,n.command,n.cwd,new Date(n.startTime||e.clock.unixMs()))??e.orchestrator.health.bumpGeneration(n.name);if(Object.keys(n.ports).length>0)e.ports.set(n.name,n.ports);xc(e,n.name,wc(e,n)),e.setState(n.name,je,Tt,n.pid,"");let a=e.cfg.services[n.name];if(a){let c=n.cwd||e.serviceWorkDir(a),l=await bc(e,n.name,a,n.ports);e.orchestrator.health.startHealth(n.name,a,n.pid,n.ports,c,l,s)}e.log(n.name,"INFO","adopted leftover process; stdout/stderr from before adopt are not captured"),r.push(n.name)}if(r.length>0)e.bus.publish(Se(Hm,"",{services:r,session_id:t.session_id})),e.log("devctl","INFO",`recovered session processes: ${r.join(", ")}`)}function R0(e,t){e.profile=t||e.profile,e.profileEnv=lo(e.cfg,e.profile)}function xc(e,t,r){e.serviceProfile.set(t,r),e.serviceProfileEnv.set(t,lo(e.cfg,r))}function wc(e,t){return t?.profile??e.profile}import{statSync as U0,watch as W0}from"fs";import{isAbsolute as G0,join as K0,resolve as q0}from"path";function fy(e,t){return t}function Mu(e,t){let r={},n=new Set,o=new Set([...Object.keys(e.services),...Object.keys(t.services)]);for(let s of o){let a=e.services[s],c=t.services[s],l=[];if(!a||!c)l.push("presence"),n.add(s);else{if(a.command.args.join("\x00")!==c.command.args.join("\x00")||a.command.shell!==c.command.shell||a.shell!==c.shell)l.push("command"),n.add(s);if(a.working_dir!==c.working_dir)l.push("working_dir"),n.add(s);if(JSON.stringify(a.environment)!==JSON.stringify(c.environment))l.push("environment"),n.add(s);if(JSON.stringify(a.ports)!==JSON.stringify(c.ports))l.push("ports"),n.add(s);if(JSON.stringify(a.identity)!==JSON.stringify(c.identity))l.push("identity"),n.add(s);if(JSON.stringify(a.health)!==JSON.stringify(c.health))l.push("health"),n.add(s);if(JSON.stringify(a.restart)!==JSON.stringify(c.restart))l.push("restart"),n.add(s);if(JSON.stringify(a.startup)!==JSON.stringify(c.startup))l.push("startup"),n.add(s);if(JSON.stringify(a.logs)!==JSON.stringify(c.logs))l.push("logs"),n.add(s);if(JSON.stringify(a.container)!==JSON.stringify(c.container))l.push("container"),n.add(s);if(JSON.stringify(a.watch)!==JSON.stringify(c.watch))l.push("watch"),n.add(s)}if(l.length>0)r[s]=l}let i=[];if(JSON.stringify(e.logs)!==JSON.stringify(t.logs))i.push("logs");if(JSON.stringify(e.auth)!==JSON.stringify(t.auth))i.push("auth");return{restart_required:[...n].sort(),changes:r,supervisor_restart_required:i}}At();te();import{pathToFileURL as F0}from"url";import{isAbsolute as N0,resolve as H0}from"path";import{createServer as yy,request as E0}from"http";import{request as T0}from"https";import{randomBytes as my}from"crypto";import{Readable as A0}from"stream";te();jr();var ds="x-devctl-request-id",L0="x-devctl-internal-token",hy=100,Ou=new Set(["connection","keep-alive","proxy-authenticate","proxy-authorization","te","trailers","transfer-encoding","upgrade"]),D0=new Set(["gzip","deflate","br"]);class $u{cfg;tokens;logs;bus;detector;resolvePort;middleware;server;running=!1;addr="";upgradedSockets=new Set;recentRequests=[];constructor(e,t,r,n,o,i=[],s){this.cfg=e,this.tokens=t,this.logs=r,this.bus=n,this.detector=o,this.middleware=i,this.resolvePort=s}upstreamBase(e){let t=e.upstream.service??"";if(t==="")return e.upstream.url;if(!this.resolvePort)throw E(Nr,`route ${e.name} addresses service ${t} but this proxy has no port resolver configured`);let r=this.resolvePort(t,e.upstream.port??"");if(r===void 0)throw E(Nr,`upstream service ${t} is not running`);return`http://127.0.0.1:${r}`}address(){return this.addr||bn(this.cfg.listen)}setMiddleware(e){this.middleware=e}isRunning(){return this.running}stats(){let e=this.recentRequests.reduce((t,r)=>t+(r.status>=400||r.error?1:0),0);return{total:this.recentRequests.length,errors:e,recent:[...this.recentRequests].reverse()}}recordRequest(e){if(this.recentRequests.push(e),this.recentRequests.length>hy)this.recentRequests.splice(0,this.recentRequests.length-hy)}start(){let e=this.cfg.listen.host||"127.0.0.1";if(!Cn(e))return Promise.reject(E(Nr,`refusing to bind proxy to ${e}`));if(this.cfg.listen.port===0)return Promise.reject(E(Nr,"proxy.listen.port is required when the proxy is enabled"));let t=this.cfg.listen.port;return new Promise((r,n)=>{this.server=yy((o,i)=>{this.serve(o,i)}),this.server.on("upgrade",(o,i,s)=>{this.serveUpgrade(o,i,s)}),this.server.on("error",(o)=>n(Pe(Nr,`unable to listen on ${e}:${t}`,o))),this.server.listen(t,e,()=>{this.running=!0,this.addr=`${e}:${t}`,this.bus?.publish(Se(Fm,"",{address:this.addr})),r()})})}stop(){let e=this.server;if(!e)return Promise.resolve();for(let t of this.upgradedSockets)t.destroy();return this.upgradedSockets.clear(),new Promise((t)=>{e.close(()=>{this.running=!1,this.bus?.publish(Se(Nm,"",{})),t()})})}async serveUpgrade(e,t,r){let n=Date.now(),o=e.headers[ds]?.toString()||my(8).toString("hex"),i=e.method??"GET",s=e.url??"/",a=this.detector?this.detector.redactText(s):s,c=vy(this.cfg.routes,e);if(!c){t.end(`HTTP/1.1 404 Not Found\r
2186
2258
  Connection: close\r
2187
2259
  Content-Length: 0\r
2188
2260
  \r
2189
- `),this.recordRequest({timestamp:new Date().toISOString(),requestId:o,method:i,path:a,route:"",identity:"",status:404,durationMs:Date.now()-n});return}let l=oo(c.auth),d=Ko(l),u,f=!1,p=(h,v)=>{if(f)return;f=!0;let x=Date.now()-n;this.recordRequest({timestamp:new Date().toISOString(),requestId:o,method:i,path:a,route:c.name,identity:d,status:h,durationMs:x,error:v}),this.logs?.append({timestamp:new Date().toISOString(),service:"proxy",source:"proxy",level:h>=400||v?"ERROR":"INFO",message:`${i} ${s} route=${c.name} identity=${d} status=${h} duration=${x}ms upgrade=true${v?` error=${v}`:""}`,pid:0,request_id:o,identity:d}),this.bus?.publish(Se(ld,c.name,{status:h,request_id:o,duration:x,identity:d}))},m=()=>{t.destroy(),u?.destroy()};try{let h={};for(let[S,y]of Object.entries(e.headers)){let b=S.toLowerCase();if(Iu.has(b)&&b!=="connection"&&b!=="upgrade")continue;if(typeof y==="string")h[S]=y;else if(Array.isArray(y))h[S]=y.join(",")}let v=e.socket.remoteAddress??"",x=h.host??"",g=h["x-forwarded-for"];if(h["x-forwarded-for"]=g?`${g}, ${v}`:v,h["x-forwarded-host"]=x,h["x-forwarded-proto"]="http",delete h.host,h[cs]=o,this.middleware.length===0)await bc(c,h,this.tokens);for(let S of this.middleware)await S.apply({route:c,headers:h,tokens:this.tokens,req:e,method:i,path:s,upgrade:!0});let R=py(this.upstreamBase(c),s),_=E0(R)(R,{method:i,headers:h});_.on("upgrade",(S,y,b)=>{u=y,this.upgradedSockets.add(t),this.upgradedSockets.add(y);let w=()=>{this.upgradedSockets.delete(t),this.upgradedSockets.delete(y)};t.once("close",()=>{w(),y.destroy()}),y.once("close",()=>{w(),t.destroy()}),t.once("error",m),y.once("error",m);let k=`HTTP/${S.httpVersion} ${S.statusCode??101} ${S.statusMessage??"Switching Protocols"}\r
2190
- `,L=S.rawHeaders.map((O,F)=>`${F%2===0?O+":":" "+O+`\r
2191
- `}`).join("");if(t.write(`${k}${L}\r
2192
- `),r.length>0)y.write(r);if(b.length>0)t.write(b);t.pipe(y).pipe(t),p(S.statusCode??101)}),_.on("response",(S)=>{S.resume();let y=S.statusCode??502;t.end(`HTTP/1.1 ${y} ${S.statusMessage??"Bad Gateway"}\r
2261
+ `),this.recordRequest({timestamp:new Date().toISOString(),requestId:o,method:i,path:a,route:"",identity:"",status:404,durationMs:Date.now()-n});return}let l=no(c.auth),d=Ko(l),u,f=!1,p=(h,v)=>{if(f)return;f=!0;let b=Date.now()-n;this.recordRequest({timestamp:new Date().toISOString(),requestId:o,method:i,path:a,route:c.name,identity:d,status:h,durationMs:b,error:v}),this.logs?.append({timestamp:new Date().toISOString(),service:"proxy",source:"proxy",level:h>=400||v?"ERROR":"INFO",message:`${i} ${s} route=${c.name} identity=${d} status=${h} duration=${b}ms upgrade=true${v?` error=${v}`:""}`,pid:0,request_id:o,identity:d}),this.bus?.publish(Se(dd,c.name,{status:h,request_id:o,duration:b,identity:d}))},m=()=>{t.destroy(),u?.destroy()};try{let h={};for(let[S,y]of Object.entries(e.headers)){let x=S.toLowerCase();if(Ou.has(x)&&x!=="connection"&&x!=="upgrade")continue;if(typeof y==="string")h[S]=y;else if(Array.isArray(y))h[S]=y.join(",")}let v=e.socket.remoteAddress??"",b=h.host??"",g=h["x-forwarded-for"];if(h["x-forwarded-for"]=g?`${g}, ${v}`:v,h["x-forwarded-host"]=b,h["x-forwarded-proto"]="http",delete h.host,h[ds]=o,this.middleware.length===0)await Sc(c,h,this.tokens);for(let S of this.middleware)await S.apply({route:c,headers:h,tokens:this.tokens,req:e,method:i,path:s,upgrade:!0});let _=gy(this.upstreamBase(c),s),R=M0(_)(_,{method:i,headers:h});R.on("upgrade",(S,y,x)=>{u=y,this.upgradedSockets.add(t),this.upgradedSockets.add(y);let w=()=>{this.upgradedSockets.delete(t),this.upgradedSockets.delete(y)};t.once("close",()=>{w(),y.destroy()}),y.once("close",()=>{w(),t.destroy()}),t.once("error",m),y.once("error",m);let k=`HTTP/${S.httpVersion} ${S.statusCode??101} ${S.statusMessage??"Switching Protocols"}\r
2262
+ `,D=S.rawHeaders.map((O,F)=>`${F%2===0?O+":":" "+O+`\r
2263
+ `}`).join("");if(t.write(`${k}${D}\r
2264
+ `),r.length>0)y.write(r);if(x.length>0)t.write(x);t.pipe(y).pipe(t),p(S.statusCode??101)}),R.on("response",(S)=>{S.resume();let y=S.statusCode??502;t.end(`HTTP/1.1 ${y} ${S.statusMessage??"Bad Gateway"}\r
2193
2265
  Connection: close\r
2194
2266
  Content-Length: 0\r
2195
2267
  \r
2196
- `),p(y,"upstream refused protocol upgrade")}),_.on("error",(S)=>{p(502,S.message),t.end(`HTTP/1.1 502 Bad Gateway\r
2268
+ `),p(y,"upstream refused protocol upgrade")}),R.on("error",(S)=>{p(502,S.message),t.end(`HTTP/1.1 502 Bad Gateway\r
2197
2269
  Connection: close\r
2198
2270
  Content-Length: 0\r
2199
2271
  \r
2200
- `)}),t.once("error",()=>_.destroy()),_.end()}catch(h){let v=h instanceof Error?h.message:"proxy upgrade error";p(502,v),t.end(`HTTP/1.1 502 Bad Gateway\r
2272
+ `)}),t.once("error",()=>R.destroy()),R.end()}catch(h){let v=h instanceof Error?h.message:"proxy upgrade error";p(502,v),t.end(`HTTP/1.1 502 Bad Gateway\r
2201
2273
  Connection: close\r
2202
2274
  Content-Length: 0\r
2203
2275
  \r
2204
- `)}}async serve(e,t){let r=Date.now(),n=e.headers[cs]?.toString()||dy(8).toString("hex");t.setHeader(cs,n);let o=e.method??"GET",i=e.url??"/",s=this.detector?this.detector.redactText(i):i,a=fy(this.cfg.routes,e);if(!a){ei(t,404,"no matching proxy route"),this.recordRequest({timestamp:new Date().toISOString(),requestId:n,method:o,path:s,route:"",identity:"",status:404,durationMs:Date.now()-r});return}let c=oo(a.auth),l=Ko(c),d=0,u;try{let f={};for(let[y,b]of Object.entries(e.headers)){if(Iu.has(y.toLowerCase()))continue;if(typeof b==="string")f[y]=b;else if(Array.isArray(b))f[y]=b.join(",")}let p=e.socket.remoteAddress??"",m=f.host??"",h=f["x-forwarded-for"];if(f["x-forwarded-for"]=h?`${h}, ${p}`:p,f["x-forwarded-host"]=m,f["x-forwarded-proto"]="http",delete f.host,f["accept-encoding"]="gzip, deflate, br",f[cs]=n,this.middleware.length===0)await bc(a,f,this.tokens);for(let y of this.middleware)await y.apply({route:a,headers:f,tokens:this.tokens});let v=py(this.upstreamBase(a),i),x=o!=="GET"&&o!=="HEAD"?e:void 0,g=await fetch(v,{method:o,headers:f,body:x,redirect:"manual",duplex:x?"half":void 0});t.statusCode=g.status,d=g.status;let R=(g.headers.get("content-encoding")??"").split(",").map((y)=>y.trim().toLowerCase()).filter((y)=>y!==""),_=R.length>0&&R.every((y)=>R0.has(y));g.headers.forEach((y,b)=>{let w=b.toLowerCase();if(Iu.has(w))return;if(_&&(w==="content-encoding"||w==="content-length"))return;t.setHeader(b,this.detector?this.detector.redactText(y):y)}),await _0(g,t);let S=Date.now()-r;this.logs?.append({timestamp:new Date().toISOString(),service:"proxy",source:"proxy",level:g.status>=400?"WARN":"INFO",message:`${o} ${i} route=${a.name} identity=${l} status=${g.status} duration=${S}ms`,pid:0,request_id:n,identity:l}),this.bus?.publish(Se(ld,a.name,{status:g.status,request_id:n,duration:S,identity:l}))}catch(f){let p=f instanceof Error?f.message:"proxy error";u=p,d=502,this.logs?.append({timestamp:new Date().toISOString(),service:"proxy",source:"proxy",level:"ERROR",message:`${o} ${i} route=${a.name} identity=${l} error=${p}`,pid:0,request_id:n,identity:l}),ei(t,502,"proxy error")}finally{this.recordRequest({timestamp:new Date().toISOString(),requestId:n,method:o,path:s,route:a.name,identity:l,status:d,durationMs:Date.now()-r,error:u})}}}async function bc(e,t,r){let n=e.auth.type.toLowerCase();if(n===""||n==="none")return;if(!r)throw I(Nr,"token manager unavailable");let o=oo(e.auth),i=await r.get(Ko(o),e.auth.audience,[],Qa(e.auth));t.authorization=`Bearer ${i.accessToken}`}async function _0(e,t){if(!e.body){t.end();return}let r=C0.fromWeb(e.body);await new Promise((n,o)=>{r.on("error",o),t.on("error",o),t.on("finish",n),r.pipe(t)})}function E0(e){return e.protocol==="https:"?k0:S0}function py(e,t){let r=new URL(e),n=new URL(t||"/",r),o=new URL(r.href);if(o.pathname=T0(n.pathname),o.search=n.search,o.hash="",o.origin!==r.origin)throw I(Nr,"refusing to proxy to a different origin");return o}function T0(e){return(e.startsWith("/")?e:`/${e}`).replace(/^\/+/,"/")}function ei(e,t,r){e.statusCode=t,e.setHeader("content-type","text/plain; charset=utf-8"),e.setHeader("x-content-type-options","nosniff"),e.end(r)}function fy(e,t){let r=(t.headers.host??"").split(":")[0]??"",n=t.url??"/";return e.find((o)=>{let i=o.match.host===""||o.match.host===r,s=o.match.path===""||n.startsWith(o.match.path);return i&&s})}class Ou{host;port;secret;tokens;server;constructor(e,t,r,n){this.host=e;this.port=t;this.secret=r;this.tokens=n}listenPort(){let e=this.server?.address();if(typeof e==="object"&&e)return e.port;return this.port}start(){let e=this.host||"127.0.0.1";if(!Cn(e))return Promise.reject(I(Nr,`refusing to bind token endpoint to ${e}`));return new Promise((t,r)=>{this.server=my((n,o)=>{this.serve(n,o)}),this.server.on("error",(n)=>r(Pe(Nr,"token endpoint listen failed",n))),this.server.listen(this.port,e,()=>t())})}stop(){let e=this.server;if(!e)return Promise.resolve();return new Promise((t)=>e.close(()=>t()))}async serve(e,t){if(!A0(e.socket.remoteAddress)){ei(t,403,"forbidden");return}if((e.headers[P0]??"")!==this.secret){ei(t,401,"unauthorized");return}if(e.url?.startsWith("/token")!==!0){ei(t,404,"not found");return}let r=new URL(e.url,"http://127.0.0.1"),n=r.searchParams.get("identity")??"user",o=r.searchParams.get("audience")??"";try{let i=await this.tokens.get(n,o,[]);t.setHeader("content-type","application/json"),t.end(JSON.stringify({access_token:i.accessToken,token_type:i.tokenType,expires_at:i.expiresAt.toISOString(),identity:i.identity}))}catch{ei(t,500,"token error")}}}function A0(e){if(!e)return!0;return e==="127.0.0.1"||e==="::1"||e==="::ffff:127.0.0.1"}jr();var hy=1;class gy{tokenProviders=[];identityProviders=[];pluginIdentityProviders=[];environmentSources=[];healthChecks=[];logParsers=[];proxyMiddleware=[];loadErrors=[];pluginPaths=[];registerBuiltins(){this.tokenProviders.push(...Rn()),this.identityProviders.push(ou(),iu()),this.logParsers.push(jm()),this.proxyMiddleware.push(O0())}register(e){M0(e),fo(this.tokenProviders,e.tokenProviders),fo(this.identityProviders,e.identityProviders),fo(this.pluginIdentityProviders,e.identityProviders),fo(this.environmentSources,e.environmentSources),fo(this.healthChecks,e.healthChecks),fo(this.logParsers,e.logParsers),fo(this.proxyMiddleware,e.proxyMiddleware)}}async function wc(e,t=process.cwd()){let r=new gy;r.registerBuiltins();for(let n of e){if(n.trim()==="")continue;try{let o=n.startsWith("file:")?n:D0(n)?n:I0(t,n),s=await import(o.startsWith("file:")?o:L0(o).href);if(s.sdkVersion!==hy)throw Error(`plugin SDK version ${String(s.sdkVersion??"missing")} is incompatible; expected ${hy}`);r.register(s),r.pluginPaths.push(o)}catch(o){r.loadErrors.push({path:n,message:o instanceof Error?o.message:String(o)})}}return r}function M0(e){if(!e||typeof e!=="object")throw Error("plugin module must export an object");ti("tokenProviders",e.tokenProviders,["fetch"]),ti("identityProviders",e.identityProviders,["accepts","resolve"]),ti("environmentSources",e.environmentSources,["load"]),ti("healthChecks",e.healthChecks,["check"]),ti("logParsers",e.logParsers,["parse"]),ti("proxyMiddleware",e.proxyMiddleware,["apply"])}function ti(e,t,r){if(t===void 0)return;if(!Array.isArray(t))throw Error(`${e} must be an array`);for(let[n,o]of t.entries()){if(!o||typeof o!=="object"||typeof o.name!=="string"||o.name==="")throw Error(`${e}[${n}] must have a name`);for(let i of r)if(typeof o[i]!=="function")throw Error(`${e}[${n}].${i} must be a function`)}}function O0(){return{name:"identity_inject",apply:async(e)=>{await bc(e.route,e.headers,e.tokens)}}}function fo(e,t){if(!t)return;e.push(...t)}var B0=200;function $u(e){if(!e.registry)return;if(e.registry.tokenProviders.length>0)e.tokens.replaceProviders(e.registry.tokenProviders);e.logs.setParsers(e.registry.logParsers,e.registry.pluginPaths),e.proxy?.setMiddleware?.(e.registry.proxyMiddleware)}function Fu(e,t){let r=new Map;for(let n of e){let o=W0(n,t);try{r.set(o,$0(o).mtimeMs)}catch{}}return r}async function U0(e,t,r){let n=t.plugins.map((a)=>a.path),o=JSON.stringify(r)!==JSON.stringify(n),i=Fu(n,t.repoRoot),s=[];for(let[a,c]of i){let l=e.pluginMtimes.get(a);if(l!==void 0&&l!==c){s.push("plugins");break}}if(o){let a=await wc(n,t.repoRoot);e.registry=a;for(let c of a.loadErrors)e.log("devctl","ERROR",`plugin ${c.path} skipped: ${c.message}`);$u(e),e.log("devctl","INFO","plugin path list reloaded")}return e.pluginMtimes=i,s}function W0(e,t){if(e.startsWith("file:"))return e;return N0(e)?e:j0(t,e)}function Nu(e,t){let r=eu(t);if(r.length===0)return;let n=new Set((e?.healthChecks??[]).map((i)=>i.name.toLowerCase())),o=r.filter((i)=>!n.has(i.type.toLowerCase()));if(o.length>0)throw I(Q,`unknown health check type(s): ${o.map((i)=>`${i.service}.health.type=${i.type}`).join(", ")}`)}function Hu(e,t){let r=tu(t);if(r.length===0)return;let n=e?.pluginIdentityProviders??[],o=r.filter(({service:i})=>{let s=t.services[i];return!s||!n.some((a)=>a.accepts(s.identity))});if(o.length>0)throw I(Q,`unknown identity type(s): ${o.map((i)=>`${i.service}.identity.type=${i.type}`).join(", ")}`)}function ju(e,t){let r=new Set(Qi),n=new Set((e?.environmentSources??[]).map((i)=>i.name)),o=t.environment.sources.filter((i)=>!r.has(i)&&!n.has(i));if(o.length>0)throw I(Q,`unknown environment source(s): ${o.join(", ")}`)}function G0(e,t,r){for(let n of Object.keys(r))if(!t[n]&&!e.runtimes.has(n))e.runtimes.set(n,Ji(n));for(let n of Object.keys(t)){if(r[n])continue;if(e.orchestrator.serviceIsActive(n)){let o=e.runtimes.get(n);if(o)o.orphaned=!0;e.log(n,"WARN","removed from configuration while still running; now orphaned \u2014 stop it explicitly to clean it up");continue}e.forgetService(n)}}function Bu(e){let t=H0(e.cfg.repoRoot,".devctl");if(!e.fs.exists(t))return;try{e.configWatcher=F0(t,{recursive:!0},()=>{if(e.watchTimer)clearTimeout(e.watchTimer);e.watchTimer=setTimeout(()=>{e.reload().catch((r)=>e.log("devctl","WARN",A(r)))},B0)})}catch{e.log("devctl","WARN","unable to watch .devctl for configuration changes")}}async function vy(e){let t;try{t=nn(e.cfg.repoRoot,e.cfg.configPath)}catch(l){throw e.bus.publish(Se(_i,"",{error:A(l)})),e.log("devctl","ERROR",`configuration reload failed: ${A(l)}`),l}try{Nu(e.registry,t),Hu(e.registry,t),ju(e.registry,t)}catch(l){throw e.bus.publish(Se(_i,"",{error:A(l)})),e.log("devctl","ERROR",`configuration reload failed: ${A(l)}`),l}if(e.setupMode){if(e.setupMode=!1,e.log("devctl","INFO",`configuration created at ${e.cfg.configPath}; leaving setup mode`),!e.configWatcher)Bu(e)}let r=Du(e.cfg,t),n=JSON.stringify(e.cfg.proxy)!==JSON.stringify(t.proxy),o=JSON.stringify(e.cfg.secrets)!==JSON.stringify(t.secrets),i=e.cfg.plugins.map((l)=>l.path),s=e.cfg.services;e.cfg=ly(e.cfg,t),G0(e,s,t.services);let a=K0(e.restartRequired,r.restart_required,Object.keys(t.services));e.restartRequired=a,r.restart_required=a;let c=await U0(e,t,i);if(c.length>0)r.supervisor_restart_required=[...r.supervisor_restart_required??[],...c];if(e.syncServiceWatchers(),o)e.detector.update(t.secrets.extra_markers,t.secrets.extra_patterns),e.logs.setSecrets(t.secrets.extra_markers,t.secrets.extra_patterns);if(n){let l=e.proxy?.isRunning()??!1;if(await e.stopProxy(),l&&e.cfg.proxy.enabled)await e.startProxy();e.log("devctl","INFO","proxy configuration changed; proxy restarted")}if(e.bus.publish(Se(ca,"",{restart_required:r.restart_required,changes:r.changes,supervisor_restart_required:r.supervisor_restart_required})),e.log("devctl","INFO",r.restart_required.length===0?"configuration reloaded":`configuration reloaded; restart required: ${r.restart_required.join(", ")}`),r.supervisor_restart_required)e.log("devctl","WARN",oc(r.supervisor_restart_required));return e.persistState(),e.refreshIdentity(),r}function K0(e,t,r){let n=new Set(r),o=new Set(e.filter((i)=>n.has(i)));for(let i of t)if(n.has(i))o.add(i);return[...o].sort()}import{watch as V0}from"fs";import{join as xy,relative as z0}from"path";ht();function yy(e,t){if(!e?.enabled)return!1;let r=t.replaceAll("\\","/").replace(/^\.\//,"");if(r===""||r.includes(".."))return!1;if((e.ignore.length>0?e.ignore:Mi).some((o)=>q0(o,r)))return!1;if(e.paths.length===0)return!1;return e.paths.some((o)=>{let i=o.replaceAll("\\","/").replace(/\/$/,"");return r===i||r.startsWith(`${i}/`)})}function q0(e,t){let r=e.replace(/[.+^${}()|[\]\\]/g,"\\$&").replaceAll("**/","<<<DS>>>").replaceAll("**","<<<DS>>>").replaceAll("*","[^/]*").replaceAll("<<<DS>>>",".*");return new RegExp(`^${r}$`).test(t)}class Uu{host;entries=new Map;constructor(e){this.host=e}sync(e){let t=new Set;for(let[r,n]of Object.entries(e)){if(!n.watch.enabled||n.watch.paths.length===0){this.stop(r);continue}t.add(r);let o=JSON.stringify(n.watch);if(this.entries.get(r)?.signature===o)continue;this.stop(r),this.start(r,n.watch)}for(let r of[...this.entries.keys()])if(!t.has(r))this.stop(r)}watched(){return[...this.entries.keys()].sort()}close(){for(let e of[...this.entries.keys()])this.stop(e)}start(e,t){let r=[],n=this.host.repoRoot();for(let o of t.paths){let i=o.replaceAll("\\","/").replace(/\/$/,"");if(i===""||i==="."){this.host.log(e,"WARN","service.watch skipped a whole-repo path; list concrete directories");continue}let s=xy(n,i);try{let a=V0(s,{recursive:!0},(c,l)=>{let d=l?xy(s,l.toString()):s,u=z0(n,d).replaceAll("\\","/");if(!yy(t,u))return;this.schedule(e,t.debounce_ms)});r.push(a)}catch{this.host.log(e,"WARN",`unable to watch ${i}`)}}if(r.length===0)return;this.entries.set(e,{signature:JSON.stringify(t),watch:t,handles:r}),this.host.log(e,"INFO",`watching ${t.paths.join(", ")}`)}schedule(e,t){let r=this.entries.get(e);if(!r)return;if(r.timer)clearTimeout(r.timer);r.timer=setTimeout(()=>{if(r.timer=void 0,!this.host.isActive(e))return;this.host.log(e,"INFO","watch: restarting after file change"),this.host.restart(e).catch((n)=>{this.host.log(e,"WARN",n instanceof Error?n.message:String(n))})},t)}stop(e){let t=this.entries.get(e);if(!t)return;if(t.timer)clearTimeout(t.timer);for(let r of t.handles)r.close();this.entries.delete(e)}}import{isAbsolute as Wu,join as Gu}from"path";class Ku{clientEnv=new Map;serviceProfile=new Map;serviceProfileEnv=new Map;profile="";profileEnv={};deps;constructor(e){this.deps=e}forget(e){this.clientEnv.delete(e),this.serviceProfile.delete(e),this.serviceProfileEnv.delete(e)}serviceWorkDir(e){if(e.working_dir!==""&&!Wu(e.working_dir))return Gu(this.deps.cfg().repoRoot,e.working_dir);return e.working_dir}async resolveServiceExecution(e,t,r,n,o,i=!0){let s=this.deps.cfg(),a=this.deps.ports().get(e)??Object.fromEntries(t.ports.filter((p)=>!p.auto).map((p)=>[p.name,p.value])),c=this.deps.proxy(),l=c?.isRunning()?`http://${c.address()}`:s.proxy.enabled?`http://${xn(s.proxy.listen)}`:"",d=es(e,"127.0.0.1",a,l,s.project.name);if(!t.container){if(d.DEVCTL_INTERNAL_TOKEN=this.deps.internalTok(),s.proxy.token_endpoint.enabled)d.DEVCTL_TOKEN_URL=this.deps.boundTokenURL()||`http://127.0.0.1:${this.deps.tokenEndpoint()?.listenPort()||s.proxy.token_endpoint.port}/token`}let u=await Zi(s.repoRoot,{service:e,profile:r,serviceCfg:t,profileEnv:n,assignedPorts:a,runtime:d,cfg:s,fetchSecret:as(async()=>(await this.deps.tokens.get("user","",[])).accessToken),pluginSources:this.deps.environmentSources(),clientEnv:o,includeProcess:i}),f=t.working_dir&&!Wu(t.working_dir)?Gu(s.repoRoot,t.working_dir):t.working_dir;return{env:Qo(u),workDir:f}}async resolveTaskEnvironment(e,t,r){let n=this.deps.cfg(),o=await Zi(n.repoRoot,{service:`task:${e}`,profile:this.profile,serviceCfg:t,profileEnv:this.profileEnv,assignedPorts:{},runtime:es(`task:${e}`,"127.0.0.1",{},"",n.project.name),cfg:n,clientEnv:r,fetchSecret:as(async()=>(await this.deps.tokens.get("user","",[])).accessToken),pluginSources:this.deps.environmentSources()}),i=t.working_dir&&!Wu(t.working_dir)?Gu(n.repoRoot,t.working_dir):t.working_dir;return{env:Qo(o),workDir:i}}}jr();te();jr();Tt();import{cpus as oA,loadavg as iA,platform as sA,uptime as aA}from"os";import{readFileSync as Y0}from"fs";import{freemem as J0,totalmem as X0}from"os";import{spawnSync as Q0}from"child_process";var qu=1024,Z0=2000,eA=/page size of (\d+) bytes/i,ri;function tA(e){let t=e.match(/^MemAvailable:\s+(\d+)/m);if(!t)return;let r=Number(t[1]);return Number.isFinite(r)&&r>=0?r:void 0}function rA(e){let t=e.match(eA),r=t?Number(t[1]):0;if(!Number.isFinite(r)||r<=0)return;let n=Sc(e,"free")+Sc(e,"inactive")+Sc(e,"speculative")+Sc(e,"purgeable");return Math.round(n*r/qu)}function Sc(e,t){let r=e.match(new RegExp(`Pages ${t}:\\s+(\\d+)`,"i")),n=r?Number(r[1]):0;return Number.isFinite(n)&&n>0?n:0}function nA(){if(process.platform==="linux")try{return tA(Y0("/proc/meminfo","utf8"))}catch{return}if(process.platform==="darwin"){let e=Date.now();if(ri&&e-ri.at<Z0)return ri.leftoverKB;let t=Q0("vm_stat",{encoding:"utf8"});if(t.status!==0||!t.stdout)return ri?.leftoverKB;let r=rA(t.stdout);if(r===void 0)return ri?.leftoverKB;return ri={at:e,leftoverKB:r},r}return}function by(){let e=Math.max(0,Math.round(X0()/qu)),t=Math.max(0,Math.round(J0()/qu)),r=Math.max(0,Math.min(e,nA()??t));return{totalKB:e,unusedKB:t,leftoverKB:r}}function wy(e){return{user:"",project:e?.google.project_id??"",project_source:e?.google.project_id?"configuration":"",adc:!1,service_accounts:{},service_account_status:Object.fromEntries(e?Hr(e).map((t)=>[t,"unknown"]):[]),iap:e?.proxy.routes.some((t)=>t.auth.type.toLowerCase()==="iap")??!1}}function Vu(){let e=iA(),t=by();return{platform:sA(),cpuCount:oA().length,loadAvg1:e[0]??0,loadAvg5:e[1]??0,loadAvg15:e[2]??0,memTotalKB:t.totalKB,memFreeKB:t.unusedKB,memAvailableKB:t.leftoverKB,hostUptimeSec:aA()}}function zu(e,t){let r={},n={};for(let o of Hr(e)){let i=t.get(o)??"unknown";if(n[o]=i,i!=="unknown")r[o]=i==="available"}return{service_accounts:r,service_account_status:n}}function Sy(e){let t={};for(let[o,i]of e.runtimes)t[o]={...i,ports:e.ports.get(o)??i.ports,profile:e.serviceProfile.get(o)??i.profile,env_source:e.clientEnv.has(o)?"client":"daemon"};let r=e.proxy?.stats(),n={requestTotal:r?.total??0,requestErrors:r?.errors??0,recentRequests:r?.recent??[]};return{session_id:e.sessionID,repo_root:e.cfg.repoRoot,profile:e.profile,services:t,proxy:{running:e.proxy?.isRunning()??!1,address:e.proxy?.address(),routes:e.cfg.proxy.routes.map((o)=>{let i=o.match.host||"*",s=o.match.path,a=s===""?i:`${i}${s.startsWith("/")?s:`/${s}`}`;return{name:o.name,identity:o.auth.identity.service_account||o.auth.identity.type||o.auth.type,upstream:o.upstream.url,auth:o.auth.type,match:a,client_id:o.auth.client_id.trim()||void 0}}),...n},mcp:{running:e.mcp?.isRunning()??!1,address:e.mcp?.isRunning()?`http://${e.mcp.address()}/mcp`:void 0,port:e.mcp?.isRunning()?e.mcp.listenPort():void 0,token:e.mcpToken,disabled_tools:[...e.mcpDisabledTools]},identity:{...e.identityCache,...zu(e.cfg,e.serviceAccountStatus)},credentials:{backend:e.tokens.storeBackend(),entries:[...e.credentialEntries]},detached:e.detached,setup_mode:e.setupMode?!0:void 0,logs:e.logs.snapshot(),restart_required:[...e.restartRequired],system:Vu(),stats_series:e.statsSeries}}function ky(e){let t=[`PROFILE: ${e.profile||"(none)"}`,"","SERVICE\tSTATUS\tHEALTH\tPID"];for(let[r,n]of Object.entries(e.services))t.push(`${r} ${Jo(n)} ${n.health} ${n.pid}`);return t.push("",`PROXY ${e.proxy.running?"RUNNING":"STOPPED"} ${e.proxy.address??""}`),t.push(`MCP ${e.mcp?.running?"RUNNING":"STOPPED"} ${e.mcp?.address??""}`),t.join(`
2276
+ `)}}async serve(e,t){let r=Date.now(),n=e.headers[ds]?.toString()||my(8).toString("hex");t.setHeader(ds,n);let o=e.method??"GET",i=e.url??"/",s=this.detector?this.detector.redactText(i):i,a=vy(this.cfg.routes,e);if(!a){ei(t,404,"no matching proxy route"),this.recordRequest({timestamp:new Date().toISOString(),requestId:n,method:o,path:s,route:"",identity:"",status:404,durationMs:Date.now()-r});return}let c=no(a.auth),l=Ko(c),d=0,u;try{let f={};for(let[y,x]of Object.entries(e.headers)){if(Ou.has(y.toLowerCase()))continue;if(typeof x==="string")f[y]=x;else if(Array.isArray(x))f[y]=x.join(",")}let p=e.socket.remoteAddress??"",m=f.host??"",h=f["x-forwarded-for"];if(f["x-forwarded-for"]=h?`${h}, ${p}`:p,f["x-forwarded-host"]=m,f["x-forwarded-proto"]="http",delete f.host,f["accept-encoding"]="gzip, deflate, br",f[ds]=n,this.middleware.length===0)await Sc(a,f,this.tokens);for(let y of this.middleware)await y.apply({route:a,headers:f,tokens:this.tokens});let v=gy(this.upstreamBase(a),i),b=o!=="GET"&&o!=="HEAD"?e:void 0,g=await fetch(v,{method:o,headers:f,body:b,redirect:"manual",duplex:b?"half":void 0});t.statusCode=g.status,d=g.status;let _=(g.headers.get("content-encoding")??"").split(",").map((y)=>y.trim().toLowerCase()).filter((y)=>y!==""),R=_.length>0&&_.every((y)=>D0.has(y));g.headers.forEach((y,x)=>{let w=x.toLowerCase();if(Ou.has(w))return;if(R&&(w==="content-encoding"||w==="content-length"))return;t.setHeader(x,this.detector?this.detector.redactText(y):y)}),await I0(g,t);let S=Date.now()-r;this.logs?.append({timestamp:new Date().toISOString(),service:"proxy",source:"proxy",level:g.status>=400?"WARN":"INFO",message:`${o} ${i} route=${a.name} identity=${l} status=${g.status} duration=${S}ms`,pid:0,request_id:n,identity:l}),this.bus?.publish(Se(dd,a.name,{status:g.status,request_id:n,duration:S,identity:l}))}catch(f){let p=f instanceof Error?f.message:"proxy error";u=p,d=502,this.logs?.append({timestamp:new Date().toISOString(),service:"proxy",source:"proxy",level:"ERROR",message:`${o} ${i} route=${a.name} identity=${l} error=${p}`,pid:0,request_id:n,identity:l}),ei(t,502,"proxy error")}finally{this.recordRequest({timestamp:new Date().toISOString(),requestId:n,method:o,path:s,route:a.name,identity:l,status:d,durationMs:Date.now()-r,error:u})}}}async function Sc(e,t,r){let n=e.auth.type.toLowerCase();if(n===""||n==="none")return;if(!r)throw E(Nr,"token manager unavailable");let o=no(e.auth),i=await r.get(Ko(o),e.auth.audience,[],ec(e.auth));t.authorization=`Bearer ${i.accessToken}`}async function I0(e,t){if(!e.body){t.end();return}let r=A0.fromWeb(e.body);await new Promise((n,o)=>{r.on("error",o),t.on("error",o),t.on("finish",n),r.pipe(t)})}function M0(e){return e.protocol==="https:"?T0:E0}function gy(e,t){let r=new URL(e),n=new URL(t||"/",r),o=new URL(r.href);if(o.pathname=O0(n.pathname),o.search=n.search,o.hash="",o.origin!==r.origin)throw E(Nr,"refusing to proxy to a different origin");return o}function O0(e){return(e.startsWith("/")?e:`/${e}`).replace(/^\/+/,"/")}function ei(e,t,r){e.statusCode=t,e.setHeader("content-type","text/plain; charset=utf-8"),e.setHeader("x-content-type-options","nosniff"),e.end(r)}function vy(e,t){let r=(t.headers.host??"").split(":")[0]??"",n=t.url??"/";return e.find((o)=>{let i=o.match.host===""||o.match.host===r,s=o.match.path===""||n.startsWith(o.match.path);return i&&s})}class Fu{host;port;secret;tokens;server;constructor(e,t,r,n){this.host=e;this.port=t;this.secret=r;this.tokens=n}listenPort(){let e=this.server?.address();if(typeof e==="object"&&e)return e.port;return this.port}start(){let e=this.host||"127.0.0.1";if(!Cn(e))return Promise.reject(E(Nr,`refusing to bind token endpoint to ${e}`));return new Promise((t,r)=>{this.server=yy((n,o)=>{this.serve(n,o)}),this.server.on("error",(n)=>r(Pe(Nr,"token endpoint listen failed",n))),this.server.listen(this.port,e,()=>t())})}stop(){let e=this.server;if(!e)return Promise.resolve();return new Promise((t)=>e.close(()=>t()))}async serve(e,t){if(!$0(e.socket.remoteAddress)){ei(t,403,"forbidden");return}if((e.headers[L0]??"")!==this.secret){ei(t,401,"unauthorized");return}if(e.url?.startsWith("/token")!==!0){ei(t,404,"not found");return}let r=new URL(e.url,"http://127.0.0.1"),n=r.searchParams.get("identity")??"user",o=r.searchParams.get("audience")??"";try{let i=await this.tokens.get(n,o,[]);t.setHeader("content-type","application/json"),t.end(JSON.stringify({access_token:i.accessToken,token_type:i.tokenType,expires_at:i.expiresAt.toISOString(),identity:i.identity}))}catch{ei(t,500,"token error")}}}function $0(e){if(!e)return!0;return e==="127.0.0.1"||e==="::1"||e==="::ffff:127.0.0.1"}jr();var by=1;class xy{tokenProviders=[];identityProviders=[];pluginIdentityProviders=[];environmentSources=[];healthChecks=[];logParsers=[];proxyMiddleware=[];loadErrors=[];pluginPaths=[];registerBuiltins(){this.tokenProviders.push(..._n()),this.identityProviders.push(su(),au()),this.logParsers.push(Wm()),this.proxyMiddleware.push(B0())}register(e){j0(e),po(this.tokenProviders,e.tokenProviders),po(this.identityProviders,e.identityProviders),po(this.pluginIdentityProviders,e.identityProviders),po(this.environmentSources,e.environmentSources),po(this.healthChecks,e.healthChecks),po(this.logParsers,e.logParsers),po(this.proxyMiddleware,e.proxyMiddleware)}}async function kc(e,t=process.cwd()){let r=new xy;r.registerBuiltins();for(let n of e){if(n.trim()==="")continue;try{let o=n.startsWith("file:")?n:N0(n)?n:H0(t,n),s=await import(o.startsWith("file:")?o:F0(o).href);if(s.sdkVersion!==by)throw Error(`plugin SDK version ${String(s.sdkVersion??"missing")} is incompatible; expected ${by}`);r.register(s),r.pluginPaths.push(o)}catch(o){r.loadErrors.push({path:n,message:o instanceof Error?o.message:String(o)})}}return r}function j0(e){if(!e||typeof e!=="object")throw Error("plugin module must export an object");ti("tokenProviders",e.tokenProviders,["fetch"]),ti("identityProviders",e.identityProviders,["accepts","resolve"]),ti("environmentSources",e.environmentSources,["load"]),ti("healthChecks",e.healthChecks,["check"]),ti("logParsers",e.logParsers,["parse"]),ti("proxyMiddleware",e.proxyMiddleware,["apply"])}function ti(e,t,r){if(t===void 0)return;if(!Array.isArray(t))throw Error(`${e} must be an array`);for(let[n,o]of t.entries()){if(!o||typeof o!=="object"||typeof o.name!=="string"||o.name==="")throw Error(`${e}[${n}] must have a name`);for(let i of r)if(typeof o[i]!=="function")throw Error(`${e}[${n}].${i} must be a function`)}}function B0(){return{name:"identity_inject",apply:async(e)=>{await Sc(e.route,e.headers,e.tokens)}}}function po(e,t){if(!t)return;e.push(...t)}var V0=200;function Nu(e){if(!e.registry)return;if(e.registry.tokenProviders.length>0)e.tokens.replaceProviders(e.registry.tokenProviders);e.logs.setParsers(e.registry.logParsers,e.registry.pluginPaths),e.proxy?.setMiddleware?.(e.registry.proxyMiddleware)}function Hu(e,t){let r=new Map;for(let n of e){let o=Y0(n,t);try{r.set(o,U0(o).mtimeMs)}catch{}}return r}async function z0(e,t,r){let n=t.plugins.map((a)=>a.path),o=JSON.stringify(r)!==JSON.stringify(n),i=Hu(n,t.repoRoot),s=[];for(let[a,c]of i){let l=e.pluginMtimes.get(a);if(l!==void 0&&l!==c){s.push("plugins");break}}if(o){let a=await kc(n,t.repoRoot);e.registry=a;for(let c of a.loadErrors)e.log("devctl","ERROR",`plugin ${c.path} skipped: ${c.message}`);Nu(e),e.log("devctl","INFO","plugin path list reloaded")}return e.pluginMtimes=i,s}function Y0(e,t){if(e.startsWith("file:"))return e;return G0(e)?e:q0(t,e)}function ju(e,t){let r=ru(t);if(r.length===0)return;let n=new Set((e?.healthChecks??[]).map((i)=>i.name.toLowerCase())),o=r.filter((i)=>!n.has(i.type.toLowerCase()));if(o.length>0)throw E(Y,`unknown health check type(s): ${o.map((i)=>`${i.service}.health.type=${i.type}`).join(", ")}`)}function Bu(e,t){let r=nu(t);if(r.length===0)return;let n=e?.pluginIdentityProviders??[],o=r.filter(({service:i})=>{let s=t.services[i];return!s||!n.some((a)=>a.accepts(s.identity))});if(o.length>0)throw E(Y,`unknown identity type(s): ${o.map((i)=>`${i.service}.identity.type=${i.type}`).join(", ")}`)}function Uu(e,t){let r=new Set(es),n=new Set((e?.environmentSources??[]).map((i)=>i.name)),o=t.environment.sources.filter((i)=>!r.has(i)&&!n.has(i));if(o.length>0)throw E(Y,`unknown environment source(s): ${o.join(", ")}`)}function J0(e,t,r){for(let n of Object.keys(r))if(!t[n]&&!e.runtimes.has(n))e.runtimes.set(n,Qi(n));for(let n of Object.keys(t)){if(r[n])continue;if(e.orchestrator.serviceIsActive(n)){let o=e.runtimes.get(n);if(o)o.orphaned=!0;e.log(n,"WARN","removed from configuration while still running; now orphaned \u2014 stop it explicitly to clean it up");continue}e.forgetService(n)}}function Wu(e){let t=K0(e.cfg.repoRoot,".devctl");if(!e.fs.exists(t))return;try{e.configWatcher=W0(t,{recursive:!0},()=>{if(e.watchTimer)clearTimeout(e.watchTimer);e.watchTimer=setTimeout(()=>{e.reload().catch((r)=>e.log("devctl","WARN",L(r)))},V0)})}catch{e.log("devctl","WARN","unable to watch .devctl for configuration changes")}}async function wy(e){let t;try{t=nn(e.cfg.repoRoot,e.cfg.configPath)}catch(l){throw e.bus.publish(Se(Ti,"",{error:L(l)})),e.log("devctl","ERROR",`configuration reload failed: ${L(l)}`),l}try{ju(e.registry,t),Bu(e.registry,t),Uu(e.registry,t)}catch(l){throw e.bus.publish(Se(Ti,"",{error:L(l)})),e.log("devctl","ERROR",`configuration reload failed: ${L(l)}`),l}if(e.setupMode){if(e.setupMode=!1,e.log("devctl","INFO",`configuration created at ${e.cfg.configPath}; leaving setup mode`),!e.configWatcher)Wu(e)}let r=Mu(e.cfg,t),n=JSON.stringify(e.cfg.proxy)!==JSON.stringify(t.proxy),o=JSON.stringify(e.cfg.secrets)!==JSON.stringify(t.secrets),i=e.cfg.plugins.map((l)=>l.path),s=e.cfg.services;e.cfg=fy(e.cfg,t),J0(e,s,t.services);let a=X0(e.restartRequired,r.restart_required,Object.keys(t.services));e.restartRequired=a,r.restart_required=a;let c=await z0(e,t,i);if(c.length>0)r.supervisor_restart_required=[...r.supervisor_restart_required??[],...c];if(e.syncServiceWatchers(),o)e.detector.update(t.secrets.extra_markers,t.secrets.extra_patterns),e.logs.setSecrets(t.secrets.extra_markers,t.secrets.extra_patterns);if(n){let l=e.proxy?.isRunning()??!1;if(await e.stopProxy(),l&&e.cfg.proxy.enabled)await e.startProxy();e.log("devctl","INFO","proxy configuration changed; proxy restarted")}if(e.bus.publish(Se(da,"",{restart_required:r.restart_required,changes:r.changes,supervisor_restart_required:r.supervisor_restart_required})),e.log("devctl","INFO",r.restart_required.length===0?"configuration reloaded":`configuration reloaded; restart required: ${r.restart_required.join(", ")}`),r.supervisor_restart_required)e.log("devctl","WARN",sc(r.supervisor_restart_required));return e.persistState(),e.refreshIdentity(),r}function X0(e,t,r){let n=new Set(r),o=new Set(e.filter((i)=>n.has(i)));for(let i of t)if(n.has(i))o.add(i);return[...o].sort()}import{watch as Z0}from"fs";import{join as ky,relative as eA}from"path";ht();function Sy(e,t){if(!e?.enabled)return!1;let r=t.replaceAll("\\","/").replace(/^\.\//,"");if(r===""||r.includes(".."))return!1;if((e.ignore.length>0?e.ignore:$i).some((o)=>Q0(o,r)))return!1;if(e.paths.length===0)return!1;return e.paths.some((o)=>{let i=o.replaceAll("\\","/").replace(/\/$/,"");return r===i||r.startsWith(`${i}/`)})}function Q0(e,t){let r=e.replace(/[.+^${}()|[\]\\]/g,"\\$&").replaceAll("**/","<<<DS>>>").replaceAll("**","<<<DS>>>").replaceAll("*","[^/]*").replaceAll("<<<DS>>>",".*");return new RegExp(`^${r}$`).test(t)}class Gu{host;entries=new Map;constructor(e){this.host=e}sync(e){let t=new Set;for(let[r,n]of Object.entries(e)){if(!n.watch.enabled||n.watch.paths.length===0){this.stop(r);continue}t.add(r);let o=JSON.stringify(n.watch);if(this.entries.get(r)?.signature===o)continue;this.stop(r),this.start(r,n.watch)}for(let r of[...this.entries.keys()])if(!t.has(r))this.stop(r)}watched(){return[...this.entries.keys()].sort()}close(){for(let e of[...this.entries.keys()])this.stop(e)}start(e,t){let r=[],n=this.host.repoRoot();for(let o of t.paths){let i=o.replaceAll("\\","/").replace(/\/$/,"");if(i===""||i==="."){this.host.log(e,"WARN","service.watch skipped a whole-repo path; list concrete directories");continue}let s=ky(n,i);try{let a=Z0(s,{recursive:!0},(c,l)=>{let d=l?ky(s,l.toString()):s,u=eA(n,d).replaceAll("\\","/");if(!Sy(t,u))return;this.schedule(e,t.debounce_ms)});r.push(a)}catch{this.host.log(e,"WARN",`unable to watch ${i}`)}}if(r.length===0)return;this.entries.set(e,{signature:JSON.stringify(t),watch:t,handles:r}),this.host.log(e,"INFO",`watching ${t.paths.join(", ")}`)}schedule(e,t){let r=this.entries.get(e);if(!r)return;if(r.timer)clearTimeout(r.timer);r.timer=setTimeout(()=>{if(r.timer=void 0,!this.host.isActive(e))return;this.host.log(e,"INFO","watch: restarting after file change"),this.host.restart(e).catch((n)=>{this.host.log(e,"WARN",n instanceof Error?n.message:String(n))})},t)}stop(e){let t=this.entries.get(e);if(!t)return;if(t.timer)clearTimeout(t.timer);for(let r of t.handles)r.close();this.entries.delete(e)}}import{isAbsolute as Ku,join as qu}from"path";class Vu{clientEnv=new Map;serviceProfile=new Map;serviceProfileEnv=new Map;profile="";profileEnv={};deps;constructor(e){this.deps=e}forget(e){this.clientEnv.delete(e),this.serviceProfile.delete(e),this.serviceProfileEnv.delete(e)}serviceWorkDir(e){if(e.working_dir!==""&&!Ku(e.working_dir))return qu(this.deps.cfg().repoRoot,e.working_dir);return e.working_dir}async resolveServiceExecution(e,t,r,n,o,i=!0){let s=this.deps.cfg(),a=this.deps.ports().get(e)??Object.fromEntries(t.ports.filter((p)=>!p.auto).map((p)=>[p.name,p.value])),c=this.deps.proxy(),l=c?.isRunning()?`http://${c.address()}`:s.proxy.enabled?`http://${bn(s.proxy.listen)}`:"",d=rs(e,"127.0.0.1",a,l,s.project.name);if(!t.container){if(d.DEVCTL_INTERNAL_TOKEN=this.deps.internalTok(),s.proxy.token_endpoint.enabled)d.DEVCTL_TOKEN_URL=this.deps.boundTokenURL()||`http://127.0.0.1:${this.deps.tokenEndpoint()?.listenPort()||s.proxy.token_endpoint.port}/token`}let u=await ts(s.repoRoot,{service:e,profile:r,serviceCfg:t,profileEnv:n,assignedPorts:a,runtime:d,cfg:s,fetchSecret:ls(async()=>(await this.deps.tokens.get("user","",[])).accessToken),pluginSources:this.deps.environmentSources(),clientEnv:o,includeProcess:i}),f=t.working_dir&&!Ku(t.working_dir)?qu(s.repoRoot,t.working_dir):t.working_dir;return{env:Qo(u),workDir:f}}async resolveTaskEnvironment(e,t,r){let n=this.deps.cfg(),o=await ts(n.repoRoot,{service:`task:${e}`,profile:this.profile,serviceCfg:t,profileEnv:this.profileEnv,assignedPorts:{},runtime:rs(`task:${e}`,"127.0.0.1",{},"",n.project.name),cfg:n,clientEnv:r,fetchSecret:ls(async()=>(await this.deps.tokens.get("user","",[])).accessToken),pluginSources:this.deps.environmentSources()}),i=t.working_dir&&!Ku(t.working_dir)?qu(n.repoRoot,t.working_dir):t.working_dir;return{env:Qo(o),workDir:i}}}jr();te();jr();At();import{cpus as dA,loadavg as uA,platform as pA,uptime as fA}from"os";import{readFileSync as tA}from"fs";import{freemem as rA,totalmem as nA}from"os";import{spawnSync as oA}from"child_process";var zu=1024,iA=2000,sA=/page size of (\d+) bytes/i,ri;function aA(e){let t=e.match(/^MemAvailable:\s+(\d+)/m);if(!t)return;let r=Number(t[1]);return Number.isFinite(r)&&r>=0?r:void 0}function cA(e){let t=e.match(sA),r=t?Number(t[1]):0;if(!Number.isFinite(r)||r<=0)return;let n=Cc(e,"free")+Cc(e,"inactive")+Cc(e,"speculative")+Cc(e,"purgeable");return Math.round(n*r/zu)}function Cc(e,t){let r=e.match(new RegExp(`Pages ${t}:\\s+(\\d+)`,"i")),n=r?Number(r[1]):0;return Number.isFinite(n)&&n>0?n:0}function lA(){if(process.platform==="linux")try{return aA(tA("/proc/meminfo","utf8"))}catch{return}if(process.platform==="darwin"){let e=Date.now();if(ri&&e-ri.at<iA)return ri.leftoverKB;let t=oA("vm_stat",{encoding:"utf8"});if(t.status!==0||!t.stdout)return ri?.leftoverKB;let r=cA(t.stdout);if(r===void 0)return ri?.leftoverKB;return ri={at:e,leftoverKB:r},r}return}function Cy(){let e=Math.max(0,Math.round(nA()/zu)),t=Math.max(0,Math.round(rA()/zu)),r=Math.max(0,Math.min(e,lA()??t));return{totalKB:e,unusedKB:t,leftoverKB:r}}function Py(e){return{user:"",project:e?.google.project_id??"",project_source:e?.google.project_id?"configuration":"",adc:!1,service_accounts:{},service_account_status:Object.fromEntries(e?Hr(e).map((t)=>[t,"unknown"]):[]),iap:e?.proxy.routes.some((t)=>t.auth.type.toLowerCase()==="iap")??!1}}function Yu(){let e=uA(),t=Cy();return{platform:pA(),cpuCount:dA().length,loadAvg1:e[0]??0,loadAvg5:e[1]??0,loadAvg15:e[2]??0,memTotalKB:t.totalKB,memFreeKB:t.unusedKB,memAvailableKB:t.leftoverKB,hostUptimeSec:fA()}}function Ju(e,t){let r={},n={};for(let o of Hr(e)){let i=t.get(o)??"unknown";if(n[o]=i,i!=="unknown")r[o]=i==="available"}return{service_accounts:r,service_account_status:n}}function _y(e){let t={};for(let[o,i]of e.runtimes)t[o]={...i,ports:e.ports.get(o)??i.ports,profile:e.serviceProfile.get(o)??i.profile,env_source:e.clientEnv.has(o)?"client":"daemon"};let r=e.proxy?.stats(),n={requestTotal:r?.total??0,requestErrors:r?.errors??0,recentRequests:r?.recent??[]};return{session_id:e.sessionID,repo_root:e.cfg.repoRoot,profile:e.profile,services:t,proxy:{running:e.proxy?.isRunning()??!1,address:e.proxy?.address(),routes:e.cfg.proxy.routes.map((o)=>{let i=o.match.host||"*",s=o.match.path,a=s===""?i:`${i}${s.startsWith("/")?s:`/${s}`}`;return{name:o.name,identity:o.auth.identity.service_account||o.auth.identity.type||o.auth.type,upstream:o.upstream.url,auth:o.auth.type,match:a,client_id:o.auth.client_id.trim()||void 0}}),...n},mcp:{running:e.mcp?.isRunning()??!1,address:e.mcp?.isRunning()?`http://${e.mcp.address()}/mcp`:void 0,port:e.mcp?.isRunning()?e.mcp.listenPort():void 0,token:e.mcpToken,disabled_tools:[...e.mcpDisabledTools]},identity:{...e.identityCache,...Ju(e.cfg,e.serviceAccountStatus)},credentials:{backend:e.tokens.storeBackend(),entries:[...e.credentialEntries]},detached:e.detached,setup_mode:e.setupMode?!0:void 0,logs:e.logs.snapshot(),restart_required:[...e.restartRequired],system:Yu(),stats_series:e.statsSeries}}function Ry(e){let t=[`PROFILE: ${e.profile||"(none)"}`,"","SERVICE\tSTATUS\tHEALTH\tPID"];for(let[r,n]of Object.entries(e.services))t.push(`${r} ${Jo(n)} ${n.health} ${n.pid}`);return t.push("",`PROXY ${e.proxy.running?"RUNNING":"STOPPED"} ${e.proxy.address??""}`),t.push(`MCP ${e.mcp?.running?"RUNNING":"STOPPED"} ${e.mcp?.address??""}`),t.join(`
2205
2277
  `)+`
2206
- `}var cA=4000;class Yu{cache;accounts=new Map;entries=[];deps;constructor(e){this.deps=e,this.cache=wy(e.cfg()),this.deps.bus.subscribe((t)=>{let r=t.payload??{},n=t.type===Oo?`token refreshed identity=${String(r.identity??"")}`:t.type===Gn?`token refresh failed identity=${String(r.identity??"")} audience=${String(r.audience??"")}: ${String(r.error??"")}`:`authentication changed user=${String(r.user??"")}`;if(this.deps.logs.append({timestamp:this.deps.clock.isoNow(),service:"auth",source:"auth",level:t.type===Gn?"WARN":"INFO",message:n,pid:0}),t.type===Oo||t.type===Gn)this.syncCredentialEntries()},[Oo,Gn,cd])}get identityCache(){return this.cache}get serviceAccountStatus(){return this.accounts}get credentialEntries(){return this.entries}async prepareServiceIdentity(e,t){let r=Ga(t.identity);if(r.kind!=="none")try{if(r=await Tg(t.identity,()=>Ig(this.deps.cfg().google.project_id),this.deps.identityProviders()),r.kind==="service_account")await this.deps.tokens.get(Ko(r),"",[]),this.accounts.set(r.serviceAccount,"available")}catch(n){if(r.kind==="service_account")this.accounts.set(r.serviceAccount,"unavailable");if(dA(t)||r.kind==="service_account"||r.kind!=="user"&&r.kind!=="none")throw await this.deps.fail(e,n),n;this.deps.log(e,"WARN","cloud identity unavailable; starting service locally")}}async syncCredentialEntries(){this.entries=(await this.deps.tokens.listStatus()).map((e)=>({identity:e.identity,audience:e.audience,expires_at:e.expires_at,valid:e.valid}))}async refreshIdentity(e){try{let t=this.deps.cfg(),r=await this.deps.detectGoogle(t.google.project_id);if(e?.probeServiceAccounts)for(let i of Hr(t))await this.probeServiceAccount(i);let{service_accounts:n,service_account_status:o}=zu(t,this.accounts);this.cache={user:r.userEmail,project:r.projectID||t.google.project_id,project_source:r.projectSource||(t.google.project_id?"configuration":""),adc:r.adcAvailable,service_accounts:n,service_account_status:o,iap:t.proxy.routes.some((i)=>i.auth.type.toLowerCase()==="iap")},await this.syncCredentialEntries(),this.deps.bus.publish(Se(cd,"",{user:this.cache.user,adc:this.cache.adc})),this.deps.log("auth","INFO",`authentication changed user=${this.cache.user||"(unknown)"} adc=${this.cache.adc}`),this.deps.persistState()}catch(t){this.deps.log("devctl","WARN",`identity refresh failed: ${A(t)}`)}}async probeServiceAccount(e){let t;try{await lA(this.deps.tokens.refresh(`sa:${e}`,"",[]),cA),t="available"}catch{t="unavailable"}return this.accounts.set(e,t),t}}function lA(e,t){return new Promise((r,n)=>{let o=setTimeout(()=>n(Error("identity probe timed out")),t);e.then((i)=>{clearTimeout(o),r(i)},(i)=>{clearTimeout(o),n(i)})})}function dA(e){return e.capabilities.some((t)=>["google_api","iap","service_identity"].includes(t))}te();te();Xu();var hA=600,gA=65535;async function Ty(e,t){let r=Number.isInteger(t)&&(t??0)>0?t:mo(e);for(let n=0;n<hA;n+=1){let o=r+n;if(o>gA)break;if(await jo(o))return o}throw I(Q,`no free MCP port starting at ${r}`)}class Qu{listener;disabled=[];token;deps;constructor(e){this.deps=e,this.token=Rm(e.repoRoot())}get instance(){return this.listener}get disabledTools(){return this.disabled}async bootFromPreferences(){let e=Io(this.deps.repoRoot());if(this.setDisabledTools(e.mcp_disabled_tools??[]),e.mcp_enabled)await this.start(e.mcp_port).catch((t)=>this.deps.log("devctl","ERROR",A(t)))}async start(e){if(this.listener?.isRunning())return;let t=await Ty(this.deps.repoRoot(),e);this.listener=this.deps.createListener({port:t,token:this.token,hostApi:this.deps.hostApi(),onEvent:(r,n)=>this.deps.log("mcp",r,`mcp ${n}`),disabledTools:()=>this.disabled}),await this.listener.start(),this.deps.persistState()}setDisabledTools(e){let t=e.filter((n)=>this.deps.isKnownTool(n)),r=this.disabled.join(",");if(this.disabled=[...new Set(t)].sort(),this.disabled.join(",")===r)return;this.deps.log("devctl","INFO",this.disabled.length===0?"all MCP tools enabled":`MCP tools disabled: ${this.disabled.join(", ")}`)}async stop(){await this.listener?.stop(),this.listener=void 0,this.deps.persistState()}}class Zu{server;tokenEP;boundURL="";suppressed=!1;deps;constructor(e){this.deps=e}get instance(){return this.server}get tokenEndpoint(){return this.tokenEP}get boundTokenURL(){return this.boundURL}get isSuppressed(){return this.suppressed}setSuppressed(e){this.suppressed=e}async start(){if(this.server?.isRunning())return;this.server=new Mu(this.deps.cfg().proxy,this.deps.tokens,this.deps.logs,this.deps.bus,this.deps.detector,this.deps.middleware(),(t,r)=>this.deps.ports().get(t)?.[r||"http"]),await this.server.start();let e=this.deps.cfg();if(e.proxy.token_endpoint.enabled)this.tokenEP=new Ou(e.proxy.token_endpoint.host||"127.0.0.1",e.proxy.token_endpoint.port,this.deps.internalTok(),this.deps.tokens),await this.tokenEP.start(),this.boundURL=`http://127.0.0.1:${this.tokenEP.listenPort()}/token`;this.deps.persistState()}async stop(){await this.server?.stop(),await this.tokenEP?.stop(),this.deps.persistState()}}Tt();var vA=3000,Ay=5000,Ly=60;class ep{timer;cpu=[];mem=[];lastAt=0;deps;constructor(e){this.deps=e}start(){if(this.timer)return;this.timer=setInterval(()=>void this.poll(),vA)}stop(){if(this.timer)clearInterval(this.timer),this.timer=void 0}series(){return{interval_ms:Ay,cpu:[...this.cpu],mem:[...this.mem]}}async poll(){this.recordStatsSample();let e=[];for(let r of this.deps.runtimes().values())if(r.pid>0&&(r.state===je||r.state===pr||r.state===Br))e.push(r.pid);if(e.length===0)return;let t=await ty(e);for(let r of this.deps.runtimes().values()){let n=r.pid>0?t.get(r.pid):void 0;if(n)r.cpuPercent=n.cpuPercent,r.memoryKB=n.memoryKB}}recordStatsSample(){let e=this.deps.clock.unixMs();if(e-this.lastAt<Ay)return;this.lastAt=e;let t=Vu(),r=t.cpuCount>0?t.loadAvg1/t.cpuCount:0,n=t.memTotalKB>0?1-t.memAvailableKB/t.memTotalKB:0;if(this.cpu.push(r),this.mem.push(n),this.cpu.length>Ly)this.cpu.shift();if(this.mem.length>Ly)this.mem.shift()}}function tp(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}function En(e){if(!Array.isArray(e))return[];return e.filter((t)=>typeof t==="string")}function ls(e){if(!tp(e))return;let t={};for(let[r,n]of Object.entries(e))if(typeof n==="string")t[r]=n;return t}te();import{createServer as yA}from"net";var xA=2000;class rp{server;deps;constructor(e){this.deps=e}get nativeServer(){return this.server}removeStaleSocket(e){if(process.platform==="win32")return;if(this.deps.socketExists(e))try{this.deps.unlinkSocket(e)}catch{this.deps.log("devctl","WARN","unable to remove stale socket")}}listen(e){return new Promise((t,r)=>{this.server=yA((n)=>{this.handleConn(n)}),this.server.on("error",r),this.server.listen(e,()=>t())})}close(){this.server?.close(),this.server=void 0}handleConn(e){let t="",r=[],n=!1,o=()=>{while(r.length>0){let a=r[0],c=e.write(`${JSON.stringify(a)}
2207
- `);if(r.shift(),!c){n=!0,e.once("drain",()=>{n=!1,o()});return}}},i=(a)=>{if(a.id===void 0&&a.event!==void 0&&r.length>=xA){let l=r.findIndex((d)=>d.id===void 0&&d.event!==void 0);if(l>=0)r.splice(l,1)}if(r.push(a),!n)o()},s=this.deps.subscribe((a)=>i({event:a}));e.on("error",(a)=>{this.deps.log("devctl","WARN",`client connection error: ${A(a)}`)}),e.on("data",(a)=>{t+=a.toString("utf8");let c=t.split(`
2208
- `);t=c.pop()??"";for(let l of c){if(l.trim()==="")continue;this.dispatchLine(l,i)}}),e.on("close",()=>s())}async dispatchLine(e,t){let r;try{r=JSON.parse(e)}catch{t({error:"invalid json"});return}try{let n=await this.deps.dispatch(r.method??"",r.params);t({id:r.id,result:n})}catch(n){let o=lh(n);t({id:r.id,error:o.error,kind:o.kind,hint:o.hint,service:o.service})}}}te();jr();Tt();class np{cfg;sessionID;internalTok;bus;logs;procs;tokens;detector;env;proxy;mcp;resources;runtimes=new Map;ports=new Map;lock;shuttingDown=!1;detached=!1;identity;rpc;detectGoogleFn;inspectProcessFn;processAliveFn;acquireLockFn;socketExistsFn;unlinkSocketFn;registry;restartRequired=[];processMeta=new Map;configWatcher;watchTimer;setupMode;serviceWatchers;pluginMtimes=new Map;orchestrator;commands;clock;fs;healthCheckers;constructor(e,t){this.healthCheckers=t.healthCheckers,this.cfg=e,this.sessionID=t.sessionID,this.internalTok=od(),this.clock=t.clock,this.fs=t.fs,this.bus=t.bus,this.detectGoogleFn=t.detectGoogle,this.inspectProcessFn=t.inspectProcess,this.processAliveFn=t.processAlive,this.acquireLockFn=t.acquireLock,this.socketExistsFn=t.socketExists,this.unlinkSocketFn=t.unlinkSocket,this.detector=t.detector,this.logs=t.logs,this.procs=t.procs,this.orchestrator=t.orchestrator,this.tokens=t.tokens,this.proxy=new Zu({cfg:()=>this.cfg,ports:()=>this.ports,tokens:this.tokens,logs:this.logs,bus:this.bus,detector:this.detector,internalTok:()=>this.internalTok,middleware:()=>this.registry?.proxyMiddleware??[],persistState:()=>this.persistState()}),this.env=new Ku({cfg:()=>this.cfg,ports:()=>this.ports,proxy:()=>this.proxy.instance,tokenEndpoint:()=>this.proxy.tokenEndpoint,boundTokenURL:()=>this.proxy.boundTokenURL,internalTok:()=>this.internalTok,tokens:this.tokens,environmentSources:()=>this.registry?.environmentSources}),this.mcp=new Qu({repoRoot:()=>this.cfg.repoRoot,createListener:t.createMcpListener,hostApi:()=>this.asMcpHost(),isKnownTool:t.isKnownTool,log:(r,n,o)=>this.log(r,n,o),persistState:()=>this.persistState()}),this.resources=new ep({clock:this.clock,runtimes:()=>this.runtimes}),this.identity=new Yu({cfg:()=>this.cfg,tokens:this.tokens,detectGoogle:(r)=>this.detectGoogleFn(r),clock:this.clock,bus:this.bus,logs:this.logs,persistState:()=>this.persistState(),fail:(r,n)=>this.fail(r,n),log:(r,n,o)=>this.log(r,n,o),identityProviders:()=>this.registry?.identityProviders}),this.rpc=new rp({dispatch:(r,n)=>this.dispatch(r,n),subscribe:(r,n)=>this.bus.subscribe(r,n),log:(r,n,o)=>this.log(r,n,o),socketExists:(r)=>this.socketExistsFn(r),unlinkSocket:(r)=>this.unlinkSocketFn(r)}),this.setupMode=!this.fs.exists(e.configPath);for(let r of Object.keys(e.services))this.runtimes.set(r,Ji(r));this.orchestrator.bind(this.lifecycleSession()),this.commands=t.createCommands(this),this.serviceWatchers=new Uu({repoRoot:()=>this.cfg.repoRoot,log:(r,n,o)=>this.log(r,n,o),isActive:(r)=>this.orchestrator.serviceIsActive(r),restart:(r)=>this.restart([r],{auto:!0})})}get profile(){return this.env.profile}set profile(e){this.env.profile=e}get profileEnv(){return this.env.profileEnv}set profileEnv(e){this.env.profileEnv=e}get clientEnv(){return this.env.clientEnv}get serviceProfile(){return this.env.serviceProfile}get serviceProfileEnv(){return this.env.serviceProfileEnv}snapshotHost(){let e=this;return{get sessionID(){return e.sessionID},get cfg(){return e.cfg},get profile(){return e.profile},get runtimes(){return e.runtimes},get ports(){return e.ports},get serviceProfile(){return e.serviceProfile},get clientEnv(){return e.clientEnv},get proxy(){return e.proxy.instance},get mcp(){return e.mcp.instance},get mcpToken(){return e.mcp.token},get mcpDisabledTools(){return e.mcp.disabledTools},get identityCache(){return e.identity.identityCache},get serviceAccountStatus(){return e.identity.serviceAccountStatus},get credentialEntries(){return e.identity.credentialEntries},get detached(){return e.detached},get setupMode(){return e.setupMode},get restartRequired(){return e.restartRequired},get statsSeries(){return e.resources.series()},logs:{snapshot:()=>e.logs.snapshot()},tokens:{storeBackend:()=>e.tokens.storeBackend()}}}reloadHost(){let e=this;return{get cfg(){return e.cfg},set cfg(t){e.cfg=t},get setupMode(){return e.setupMode},set setupMode(t){e.setupMode=t},get configWatcher(){return e.configWatcher},set configWatcher(t){e.configWatcher=t},get watchTimer(){return e.watchTimer},set watchTimer(t){e.watchTimer=t},get restartRequired(){return e.restartRequired},set restartRequired(t){e.restartRequired=t},get fs(){return e.fs},get registry(){return e.registry},set registry(t){e.registry=t},get pluginMtimes(){return e.pluginMtimes},set pluginMtimes(t){e.pluginMtimes=t},get detector(){return e.detector},get bus(){return e.bus},get orchestrator(){return e.orchestrator},get runtimes(){return e.runtimes},get tokens(){return e.tokens},get logs(){return e.logs},get proxy(){return e.proxy.instance},persistState:()=>e.persistState(),log:(t,r,n)=>e.log(t,r,n),refreshIdentity:()=>e.refreshIdentity(),startProxy:()=>e.startProxy(),stopProxy:()=>e.stopProxy(),reload:()=>e.reload(),forgetService:(t)=>e.forgetService(t),syncServiceWatchers:()=>e.serviceWatchers.sync(e.cfg.services)}}recoverHost(){let e=this;return{get cfg(){return e.cfg},set cfg(t){e.cfg=t},get profile(){return e.profile},set profile(t){e.profile=t},get profileEnv(){return e.profileEnv},set profileEnv(t){e.profileEnv=t},get runtimes(){return e.runtimes},get ports(){return e.ports},get processMeta(){return e.processMeta},get serviceProfile(){return e.serviceProfile},get serviceProfileEnv(){return e.serviceProfileEnv},get orchestrator(){return e.orchestrator},get procs(){return e.procs},logs:{append:(t)=>e.logs.append(t)},get clock(){return e.clock},get tokens(){return e.tokens},get registry(){return e.registry},get proxy(){return e.proxy.instance},get tokenEP(){return e.proxy.tokenEndpoint},get boundTokenURL(){return e.proxy.boundTokenURL},get internalTok(){return e.internalTok},get bus(){return e.bus},inspectProcessFn:(t)=>e.inspectProcessFn(t),processAliveFn:(t)=>e.processAliveFn(t),serviceWorkDir:(t)=>e.serviceWorkDir(t),persistState:()=>e.persistState(),setState:(t,r,n,o,i)=>e.setState(t,r,n,o,i),log:(t,r,n)=>e.log(t,r,n)}}async run(){let e=Qs(this.cfg.repoRoot);this.lock=this.acquireLockFn(this.cfg.repoRoot,e),this.rpc.removeStaleSocket(e),this.registry=await wc(this.cfg.plugins.map((t)=>t.path),this.cfg.repoRoot),this.pluginMtimes=Fu(this.cfg.plugins.map((t)=>t.path),this.cfg.repoRoot);for(let t of this.registry.loadErrors)this.log("devctl","ERROR",`plugin ${t.path} skipped: ${t.message}`);$u(this.reloadHost()),Nu(this.registry,this.cfg),Hu(this.registry,this.cfg),ju(this.registry,this.cfg),await this.recoverSession(),this.serviceWatchers.sync(this.cfg.services),Bu(this.reloadHost()),this.persistState(),this.log("devctl","INFO",`supervisor started session=${this.sessionID}`),this.refreshIdentity(),this.resources.start(),await this.mcp.bootFromPreferences(),this.rpc.removeStaleSocket(e),await this.rpc.listen(e)}async dispatch(e,t){let r=tp(t)?t:{};switch(e){case"ping":return{session:this.sessionID,version:jt,protocol:Vi};case"start":return this.commands.startService.execute({services:En(r.services),profile:typeof r.profile==="string"&&r.profile!==""?r.profile:void 0,detach:r.detach===!0,client_env:ls(r.client_env)});case"stop":return await this.commands.stopService.execute(En(r.services)),null;case"restart":return await this.commands.restartService.execute(En(r.services),{cascade:r.cascade===!0,clientEnv:ls(r.client_env)}),null;case"run_task":return this.runTask(typeof r.name==="string"?r.name:"",ls(r.client_env)??{});case"exec":return this.execService(typeof r.service==="string"?r.service:"",En(r.command),ls(r.client_env)??{},r.print_env===!0);case"auth_refresh":return await this.commands.refreshIdentity.execute(),this.snapshot().identity;case"status":return this.snapshot();case"logs":return await this.queryLogs({services:En(r.services),level:typeof r.level==="string"?r.level:"",search:typeof r.search==="string"?r.search:"",regex:r.regex===!0,source:typeof r.source==="string"?r.source:"",since:typeof r.since==="string"?r.since:"",until:typeof r.until==="string"?r.until:"",export:typeof r.export==="string"?r.export:""});case"logs_page":return await this.queryLogsPage({services:En(r.services),level:typeof r.level==="string"?r.level:"",search:typeof r.search==="string"?r.search:"",regex:r.regex===!0,source:typeof r.source==="string"?r.source:"",since:typeof r.since==="string"?r.since:"",until:typeof r.until==="string"?r.until:"",cursor:typeof r.cursor==="string"?r.cursor:void 0,direction:r.direction==="forward"?"forward":"backward",limit:typeof r.limit==="number"?r.limit:void 0});case"logs_stats":return await this.queryLogsFacets({services:En(r.services),level:typeof r.level==="string"?r.level:"",search:typeof r.search==="string"?r.search:"",regex:r.regex===!0,source:typeof r.source==="string"?r.source:"",since:typeof r.since==="string"?r.since:"",until:typeof r.until==="string"?r.until:""});case"proxy_start":return this.proxy.setSuppressed(!1),await this.commands.startProxy.execute(),null;case"proxy_stop":return this.proxy.setSuppressed(!0),await this.commands.stopProxy.execute(),null;case"mcp_start":{let o=typeof r.port==="number"?r.port:void 0;return await this.startMcp(o??Io(this.cfg.repoRoot).mcp_port),null}case"mcp_stop":return await this.stopMcp(),null;case"mcp_set_tools":{let o=Array.isArray(r.disabled)?r.disabled.filter((i)=>typeof i==="string"):[];return this.setMcpDisabledTools(o),{disabled_tools:[...this.mcp.disabledTools]}}case"reload":return this.commands.reloadConfig.execute();case"config_snapshot":return this.cfg;case"auth_invalidate":return this.tokens.invalidate(),null;case"shutdown":let n=typeof r.stop_services==="boolean"?r.stop_services:_a(this.cfg.shutdown);return setTimeout(()=>{this.shutdown(n)},50),null;default:throw I(lt,`unknown method ${e}`)}}isDetached(){return this.detached}async start(e){return this.orchestrator.start(e)}lifecycleSession(){let e=this;return{get cfg(){return e.cfg},get profile(){return e.profile},set profile(t){e.profile=t},get profileEnv(){return e.profileEnv},set profileEnv(t){e.profileEnv=t},get detached(){return e.detached},set detached(t){e.detached=t},get proxySuppressed(){return e.proxy.isSuppressed},get runtimes(){return e.runtimes},get ports(){return e.ports},get clientEnv(){return e.clientEnv},get serviceProfile(){return e.serviceProfile},get serviceProfileEnv(){return e.serviceProfileEnv},healthCheckers:{lookup:(t)=>{let r=e.registry?.healthChecks.find((n)=>n.name.toLowerCase()===t.toLowerCase());return r?{check:async(n,o)=>{let i=await r.check(n,o);return{...i,status:i.status}}}:e.healthCheckers.lookup(t)}},logs:{append:(t)=>e.logs.append(t)},bus:e.bus,processMeta:e.processMeta,get containerPrefix(){return`devctl-${ar(e.cfg.repoRoot)}-`},prepareServiceIdentity:(t,r)=>e.identity.prepareServiceIdentity(t,r),resolveServiceExecution:(t,r,n,o,i,s)=>e.env.resolveServiceExecution(t,r,n,o,i,s),detectGoogle:(t)=>e.detectGoogleFn(t),startProxy:()=>e.startProxy(),fail:(t,r)=>e.fail(t,r),claimIfAlreadyUp:(t)=>e.claimIfAlreadyUp(t),assignPendingPorts:(t)=>e.assignPendingPorts(t),setState:(t,r,n,o,i)=>e.setState(t,r,n,o,i),persistState:()=>e.persistState(),log:(t,r,n)=>e.log(t,r,n),releasePorts:(t)=>e.releasePorts(t),forgetService:(t)=>e.forgetService(t),clearRestartRequired:(t)=>e.dropRestartRequired(t)}}async assignPendingPorts(e){try{let t=await ph(this.cfg,e,Object.fromEntries(this.ports));for(let[r,n]of Object.entries(t)){this.ports.set(r,n);let o=Object.entries(n).map(([i,s])=>`${i}=${s}`).join(", ");this.log(r,"INFO",`assigned ports: ${o||"none"}`)}}catch(t){if(t instanceof Ve&&t.service!=="")await this.fail(t.service,t);else this.log("devctl","ERROR",A(t));throw t}}async runTask(e,t){let r=this.cfg.tasks[e];if(!r)throw I(lt,`unknown task ${e}`);if(r.dependencies.length>0)await this.start({services:r.dependencies,client_env:t});let n={...vn(),command:r.command,shell:r.shell,working_dir:r.working_dir,dependencies:r.dependencies,environment:r.environment},{env:o,workDir:i}=await this.env.resolveTaskEnvironment(e,n,t),s=await this.runTransient(`task:${e}`,r.command,r.shell,i,o);return{task:e,...s}}async execService(e,t,r,n=!1){let o=this.cfg.services[e];if(!o)throw I(Ho,`unknown service ${e}`);let i=this.env.serviceProfile.get(e)??this.env.profile,s=i!==""?uo(this.cfg,i):this.env.profileEnv,{env:a,workDir:c}=await this.env.resolveServiceExecution(e,o,i,s,r,!o.container);if(n)return{service:e,code:0,stdout:"",stderr:"",environment:a};if(t.length===0)throw I(lt,"exec command is required");let l=await this.runTransient(`${e}:exec`,{args:t,shell:!1},!1,c,a);return{service:e,...l}}async runTransient(e,t,r,n,o){if(yn(t))return{code:0,stdout:"",stderr:""};this.log(e,"INFO",`running ${t.args.join(" ")}`);let i=await this.procs.runOnce({name:e,args:[...t.args],shell:r||t.shell,workDir:n,env:o,graceMs:bn(this.cfg.shutdown)*1000,onLine:(s,a)=>this.logs.append({timestamp:this.clock.isoNow(),service:e,source:s,stream:s,level:"",message:a,pid:0})});if(i.code!==0)throw I(He,`${e} exited with code ${i.code}`);return i}serviceWorkDir(e){return this.env.serviceWorkDir(e)}async claimIfAlreadyUp(e){return ay(this.recoverHost(),e)}async recoverSession(){return cy(this.recoverHost())}async stop(e){return this.orchestrator.stop(e)}forgetService(e){this.orchestrator.health.forget(e),this.runtimes.delete(e),this.ports.delete(e),this.processMeta.delete(e),this.env.forget(e)}async restart(e,t){let r=t?.cascade===!0?lo(this.cfg,e):e;await this.orchestrator.restart(e,t),this.dropRestartRequired(r)}dropRestartRequired(e){if(e.length===0)return;let t=new Set(e);this.restartRequired=this.restartRequired.filter((r)=>!t.has(r))}async startProxy(){return this.proxy.start()}async stopProxy(){return this.proxy.stop()}async startMcp(e){return this.mcp.start(e)}setMcpDisabledTools(e){this.mcp.setDisabledTools(e)}async stopMcp(){return this.mcp.stop()}asMcpHost(){return{status:()=>this.commands.getServiceStatus.execute(),logsPage:(e)=>this.queryLogsPage(e),config:()=>this.cfg,validateConfigText:(e)=>ji(this.cfg.repoRoot,this.cfg.configPath,e),start:(e)=>this.commands.startService.execute(e),stop:(e)=>this.commands.stopService.execute(e),restart:(e,t)=>this.commands.restartService.execute(e,{cascade:t}),reload:()=>this.commands.reloadConfig.execute(),doctor:async()=>{for(let e of Hr(this.cfg))await this.identity.probeServiceAccount(e);return this.commands.runDoctor.execute(this.cfg)},exec:(e,t,r)=>this.execService(e,t,void 0,r),runTask:(e)=>this.runTask(e,{}),startProxy:()=>this.startProxy(),stopProxy:()=>this.stopProxy()}}async reload(){return vy(this.reloadHost())}async shutdown(e){if(this.shuttingDown)return;if(this.shuttingDown=!0,this.persistState(),e)await this.stop([]);if(this.orchestrator.health.dispose(),await this.stopProxy(),await this.stopMcp(),this.configWatcher?.close(),this.serviceWatchers.close(),this.watchTimer)clearTimeout(this.watchTimer);this.resources.stop(),this.rpc.close(),this.lock?.release(),await this.logs.close()}snapshot(){return Sy(this.snapshotHost())}async refreshIdentity(e){return this.identity.refreshIdentity(e)}async queryLogs(e){let t=await this.logs.query({services:e.services,level:e.level,search:e.search,regex:e.regex,source:e.source,since:e.since,until:e.until});if(e.export)await this.logs.exportTo(e.export,{services:e.services,level:e.level,search:e.search,regex:e.regex,source:e.source,since:e.since,until:e.until});return{events:t}}async queryLogsPage(e){return this.logs.queryPage({services:e.services,level:e.level,search:e.search,regex:e.regex,source:e.source,since:e.since,until:e.until},{cursor:e.cursor,direction:e.direction,limit:e.limit})}async queryLogsFacets(e){return this.logs.queryFacets({services:e.services,level:e.level,search:e.search,regex:e.regex,source:e.source,since:e.since,until:e.until})}subscribe(e){return this.bus.subscribe(e)}formatStatus(){return ky(this.snapshot())}setState(e,t,r,n,o){let i=this.runtimes.get(e)??Ji(e);if(!ss(i.state,t))this.log(e,"WARN",`unusual lifecycle ${i.state} \u2192 ${t}`);i.state=t,i.health=r,i.pid=n,i.last_error=o;let s=t===je||t===pr||t===Br;if(i.startTime=s?this.processMeta.get(e)?.startTime.toISOString():void 0,!s)i.cpuPercent=void 0,i.memoryKB=void 0;this.runtimes.set(e,i),this.bus.publish(Se(Dm,e,{state:t,health:r,pid:n}))}async releasePorts(e){let t=this.ports.get(e);if(!t)return;let r=this.cfg.services[e];if(r?.container){this.ports.delete(e);return}let n=this.processMeta.get(e);for(let o of Object.values(t)){let i=await Yn(o);if(!i||i.pid===process.pid)continue;if(r){let s=await this.inspectProcessFn(i.pid);if(!(s!==void 0&&s.command!==""&&os({args:n?.command??[...r.command.args],workDir:n?.cwd??this.serviceWorkDir(r),startTime:n?.startTime},s))){this.log(e,"WARN",`port ${o} is held by pid ${i.pid}, which does not match ${e}; leaving it running`);continue}}try{await xa(i),this.log(e,"INFO",`released port ${o} (pid ${i.pid})`)}catch(s){this.log(e,"WARN",A(s))}}this.ports.delete(e)}async fail(e,t){this.orchestrator.health.clearHealthWatch(e),this.orchestrator.health.clearRestartTimer(e),this.orchestrator.health.bumpGeneration(e),this.setState(e,dt,Et,0,A(t));try{await this.procs.stop(e,bn(this.cfg.shutdown)*1000)}catch(r){this.log(e,"WARN",A(r))}this.bus.publish(Se(aa,e,{error:A(t)})),this.log(e,"ERROR",A(t)),this.persistState()}log(e,t,r){this.logs.append({timestamp:this.clock.isoNow(),service:e,source:"devctl",level:t,message:r,pid:0})}persistState(){let e=[];for(let t of this.procs.all()){let r=this.processMeta.get(t.name);e.push({name:t.name,pid:t.pid,command:r?.command??t.args,cwd:r?.cwd??t.workDir,startTime:(r?.startTime??t.startTime).toISOString(),ports:this.ports.get(t.name)??{},profile:this.serviceProfile.get(t.name)??this.profile})}Cm(this.cfg.repoRoot,{session_id:this.sessionID,repo_root:this.cfg.repoRoot,profile:this.profile,processes:e})}}var Iy=500,bA=1e4,wA=2000,SA=new URL("./log-worker.ts",import.meta.url);class My{worker;bus;pending=new Map;nextId=1;stats={total:0,errors:0,counts:{}};dead=!1;readySettled=!1;ready;resolveReady=()=>{return};rejectReady=()=>{return};constructor(e,t,r={}){this.bus=t,this.ready=new Promise((n,o)=>{this.resolveReady=n,this.rejectReady=o}),this.worker=new Worker(r.script??SA,{name:"devctl-logs"}),this.worker.addEventListener("message",(n)=>{this.onMessage(n.data)}),this.worker.addEventListener("error",(n)=>{this.markDead(Error(n.message||"log worker failed"))}),this.post({type:"init",config:e})}waitUntilReady(e=Iy){if(this.dead)return Promise.reject(Error("log worker is not running"));if(this.readySettled)return Promise.resolve();return new Promise((t,r)=>{let n=setTimeout(()=>{this.markDead(Error("log worker init timed out"))},e);this.ready.then(()=>{clearTimeout(n),t()},(o)=>{clearTimeout(n),r(o)})})}append(e){if(this.dead)return;try{this.post({type:"append",event:e})}catch{}}async query(e){let t=await this.rpc({type:"query",filter:e});if(!Array.isArray(t))throw Error("log worker query returned a non-array");return t}async queryPage(e,t){let r=await this.rpc({type:"queryPage",filter:e,page:t});if(!kA(r))throw Error("log worker queryPage returned an unexpected payload");return r}async queryFacets(e){let t=await this.rpc({type:"queryFacets",filter:e});if(!CA(t))throw Error("log worker queryFacets returned an unexpected payload");return t}snapshot(){return this.stats}async exportTo(e,t){await this.rpc({type:"exportTo",path:e,filter:t})}setParsers(e,t){if(this.dead)return;this.post({type:"setPluginPaths",paths:[...t??[]]})}setSecrets(e,t){if(this.dead)return;this.post({type:"setSecrets",extraMarkers:e,extraPatterns:t})}async close(){if(this.dead)return;try{await this.rpc({type:"close"},wA)}catch{}finally{this.markDead(Error("log worker closed"))}}rpc(e,t=bA){this.assertAlive();let r=this.nextId;this.nextId+=1;let n={...e,id:r};return new Promise((o,i)=>{let s=setTimeout(()=>{this.markDead(Error(`log worker timed out (${e.type})`))},t);this.pending.set(r,{resolve:o,reject:i,timer:s});try{this.post(n)}catch(a){clearTimeout(s),this.pending.delete(r),i(a instanceof Error?a:Error(String(a)))}})}post(e){this.assertAlive(),this.worker.postMessage(e)}onMessage(e){if(e.type==="ready"){this.settleReady();return}if(e.type==="appended"){this.stats=e.stats,this.bus?.publish(Se(Mo,e.event.service,{event:e.event,level:e.event.level}));return}let t=this.pending.get(e.id);if(!t)return;if(this.pending.delete(e.id),clearTimeout(t.timer),e.type==="error"){t.reject(Error(e.error));return}t.resolve(e.result)}settleReady(){if(this.readySettled||this.dead)return;this.readySettled=!0,this.resolveReady()}assertAlive(){if(this.dead)throw Error("log worker is not running")}markDead(e){if(this.dead)return;if(this.dead=!0,!this.readySettled)this.readySettled=!0,this.rejectReady(e);this.rejectAll(e);try{this.worker.terminate()}catch{}}rejectAll(e){for(let t of this.pending.values())clearTimeout(t.timer),t.reject(e);this.pending.clear()}}function kA(e){if(e===null||Array.isArray(e)||!("events"in e))return!1;return Array.isArray(e.events)}function CA(e){return e!==null&&!Array.isArray(e)&&"byService"in e}function Dy(e,t,r){return eh(new Rd(e.max,t,r,e.persist,e.directory,e.sessionID,e.retentionDays,e.maxSessionLogs))}async function Oy(e,t,r,n={}){if(n.standalone??Bun.isStandaloneExecutable===!0)return{logs:Dy(e,t,r),usingWorker:!1};try{let i=new My(e,t,{script:n.script});return await i.waitUntilReady(n.initTimeoutMs??Iy),{logs:i,usingWorker:!0}}catch{return{logs:Dy(e,t,r),usingWorker:!1}}}ni();import{createServer as YA}from"http";te();us();var JA="2025-03-26",rx="2.0",Qy=-32700,Zy=-32600,XA=-32601,ex=-32602,QA=-32603;function ZA(e){return e!==""&&Cn(e)}class ap{server;running=!1;addr="";boundPort=0;opts;constructor(e){this.opts=e}disabled(){return this.opts.disabledTools?.()??[]}address(){return this.addr}listenPort(){return this.boundPort}isRunning(){return this.running}emit(e,t){this.opts.onEvent?.(e,t)}start(){if(!ZA(this.opts.host))return Promise.reject(I(lt,`refusing to bind MCP to ${this.opts.host}`));return new Promise((e,t)=>{this.server=YA((r,n)=>{this.serve(r,n)}),this.server.on("error",(r)=>t(Pe(lt,`unable to listen on ${this.opts.host}:${this.opts.port}`,r))),this.server.listen(this.opts.port,this.opts.host,()=>{let r=this.server?.address();this.boundPort=typeof r==="object"&&r?r.port:this.opts.port,this.addr=`${this.opts.host}:${this.boundPort}`,this.running=!0,this.emit("INFO",`listening on ${this.addr}`),e()})})}stop(){let e=this.server,t=this.running;if(!e)return this.running=!1,Promise.resolve();return new Promise((r)=>{e.close(()=>{if(this.running=!1,this.addr="",this.boundPort=0,this.server=void 0,t)this.emit("INFO","stopped");r()})})}async serve(e,t){if(e.method==="OPTIONS"){t.writeHead(204,ms()),t.end();return}if(!this.authorized(e)){this.emit("WARN",`rejected unauthorized request from ${tx(e)}`),oi(t,401,{error:"unauthorized"});return}if(tL(e)!=="/mcp"){oi(t,404,{error:"not found"});return}if(e.method==="GET"||e.method==="DELETE"){t.writeHead(405,{Allow:"POST, OPTIONS",...ms()}),t.end();return}if(e.method!=="POST"){t.writeHead(405,{Allow:"POST, OPTIONS",...ms()}),t.end();return}let n;try{n=await rL(e)}catch(a){oi(t,400,ps(null,Qy,a instanceof Error?a.message:"invalid body"));return}let o;try{o=JSON.parse(n)}catch{oi(t,400,ps(null,Qy,"parse error"));return}let i=tx(e);if(Array.isArray(o)){let a=[];for(let c of o){let l=await this.dispatch(c,i);if(l!==void 0)a.push(l)}oi(t,200,a);return}let s=await this.dispatch(o,i);if(s===void 0){t.writeHead(202,ms()),t.end();return}oi(t,200,s)}authorized(e){if(this.opts.token==="")return!0;return(e.headers.authorization??"")===`Bearer ${this.opts.token}`}async dispatch(e,t){if(!_c(e))return ps(null,Zy,"invalid request");let r=e,n=r.id??null,o=r.id===void 0;if(r.method===void 0||r.method==="")return o?void 0:ps(n,Zy,"missing method");try{let i=await this.handleMethod(r.method,_c(r.params)?r.params:{},t);if(o)return;return{jsonrpc:rx,id:n,result:i}}catch(i){if(o)return;let s=i instanceof fs?i.code:QA,a=i instanceof Error?i.message:"internal error";return this.emit("WARN",`rpc error method=${r.method} addr=${t}: ${a}`),ps(n,s,a)}}async handleMethod(e,t,r){switch(e){case"initialize":{let n=eL(t);return this.emit("INFO",`client connected addr=${r}${n?` (${n})`:""}`),{protocolVersion:JA,capabilities:{tools:{},resources:{}},serverInfo:{name:"devctl",version:jt}}}case"notifications/initialized":case"ping":return{};case"tools/list":return{tools:Gy(this.disabled()).map((n)=>({name:n.name,description:n.description,inputSchema:n.inputSchema}))};case"tools/call":return this.callTool(t);case"resources/list":return{resources:ip.map((n)=>({uri:n,name:n.replace("devctl://",""),mimeType:"application/json"}))};case"resources/read":return this.readResource(t);default:throw new fs(XA,`method not found: ${e}`)}}async callTool(e){let t=typeof e.name==="string"?e.name:"";if(t==="")throw new fs(ex,"tool name is required");if(Rc(t)&&!An(t,this.disabled())){let o=`tool ${t} is disabled in devctl's MCP settings`;return this.emit("WARN",`tool call rejected name=${t}: disabled`),{content:[{type:"text",text:o}],isError:!0}}let r=_c(e.arguments)?e.arguments:{},n=Date.now();try{let o=await Yy(this.opts.hostApi,t,r);return this.emit("INFO",`tool call name=${t} duration=${Date.now()-n}ms`),{content:[{type:"text",text:JSON.stringify(o,null,2)}]}}catch(o){let i=o instanceof Error?o.message:"tool failed";return this.emit("WARN",`tool call failed name=${t} duration=${Date.now()-n}ms: ${i}`),{content:[{type:"text",text:i}],isError:!0}}}async readResource(e){let t=typeof e.uri==="string"?e.uri:"";if(!Jy(t))throw new fs(ex,`unknown resource ${t}`);let r=await Xy(this.opts.hostApi,t);return this.emit("INFO",`resource read uri=${t}`),{contents:[{uri:t,mimeType:"application/json",text:JSON.stringify(r,null,2)}]}}}function eL(e){let t=e.clientInfo;if(!_c(t))return"";let r=typeof t.name==="string"?t.name:"",n=typeof t.version==="string"?t.version:"";if(r==="")return"";return n===""?r:`${r} ${n}`}function tx(e){return e.socket.remoteAddress??"unknown"}class fs extends Error{code;constructor(e,t){super(t);this.code=e}}function ms(){return{"Access-Control-Allow-Origin":"*","Access-Control-Allow-Headers":"Authorization, Content-Type, Accept, MCP-Protocol-Version, Mcp-Session-Id","Access-Control-Allow-Methods":"POST, OPTIONS"}}function oi(e,t,r){let n=JSON.stringify(r);e.writeHead(t,{"Content-Type":"application/json","Content-Length":Buffer.byteLength(n),...ms()}),e.end(n)}function ps(e,t,r){return{jsonrpc:rx,id:e,error:{code:t,message:r}}}function tL(e){let t=e.url??"/",r=t.indexOf("?");return r>=0?t.slice(0,r):t}function rL(e){return new Promise((t,r)=>{let n=[];e.on("data",(o)=>{n.push(o)}),e.on("end",()=>{t(Buffer.concat(n).toString("utf8"))}),e.on("error",r)})}function _c(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}us();var iL=(e)=>new ap({host:"127.0.0.1",...e});async function sL(e,t={}){let r=t.clock??Ja,n=t.fs??ry,o=t.processes??new Ru,i=t.bus??new dd(2048),s=t.tokens??new co(e.auth.refresh_threshold_seconds*1000,Rn(),i,void 0,r),a=new Lu(o,r),c=td(),l=new mr(e.secrets.extra_markers,e.secrets.extra_patterns),d=Bun.isStandaloneExecutable===!0,{logs:u,usingWorker:f}=await Oy({max:e.logs.max_memory_events,persist:e.logs.persistence.enabled,directory:e.logs.persistence.directory,sessionID:c,retentionDays:e.logs.persistence.retention_days,maxSessionLogs:e.logs.persistence.max_session_logs,extraMarkers:e.secrets.extra_markers,extraPatterns:e.secrets.extra_patterns},i,l,{standalone:d});if(!f&&!d)u.append({timestamp:r.isoNow(),service:"devctl",source:"devctl",level:"WARN",message:"log worker failed to start; using in-process log store",pid:0});return{supervisor:new np(e,{healthCheckers:t.healthCheckers??Mv([]),detectGoogle:t.detectGoogle??so,tokens:s,inspectProcess:ey,processAlive:Qt,acquireLock:Pm,socketExists:nL,unlinkSocket:oL,procs:o,orchestrator:a,clock:r,fs:n,bus:i,logs:u,detector:l,sessionID:c,createMcpListener:t.createMcpListener??iL,isKnownTool:Rc,createCommands:(m)=>lv(m,ec(Yo({tokens:s})),a)}),orchestrator:a,clock:r,fs:n}}async function nx(e,t){let r=ro(e,t),{supervisor:n}=await sL(r),o=!1,i=()=>{if(o)return;o=!0,n.shutdown(_a(r.shutdown)).finally(()=>process.exit(0))};process.on("SIGINT",i),process.on("SIGTERM",i),await n.run()}ht();te();Hf();import{Command as NO}from"commander";var Gk=["zsh","bash","fish"];function Kk(e){if(e==="zsh")return`#compdef devctl
2278
+ `}var mA=4000;class Xu{cache;accounts=new Map;entries=[];deps;constructor(e){this.deps=e,this.cache=Py(e.cfg()),this.deps.bus.subscribe((t)=>{let r=t.payload??{},n=t.type===Mo?`token refreshed identity=${String(r.identity??"")}`:t.type===Gn?`token refresh failed identity=${String(r.identity??"")} audience=${String(r.audience??"")}: ${String(r.error??"")}`:`authentication changed user=${String(r.user??"")}`;if(this.deps.logs.append({timestamp:this.deps.clock.isoNow(),service:"auth",source:"auth",level:t.type===Gn?"WARN":"INFO",message:n,pid:0}),t.type===Mo||t.type===Gn)this.syncCredentialEntries()},[Mo,Gn,ld])}get identityCache(){return this.cache}get serviceAccountStatus(){return this.accounts}get credentialEntries(){return this.entries}async prepareServiceIdentity(e,t){let r=qa(t.identity);if(r.kind!=="none")try{if(r=await Ig(t.identity,()=>Fg(this.deps.cfg().google.project_id),this.deps.identityProviders()),r.kind==="service_account")await this.deps.tokens.get(Ko(r),"",[]),this.accounts.set(r.serviceAccount,"available")}catch(n){if(r.kind==="service_account")this.accounts.set(r.serviceAccount,"unavailable");if(gA(t)||r.kind==="service_account"||r.kind!=="user"&&r.kind!=="none")throw await this.deps.fail(e,n),n;this.deps.log(e,"WARN","cloud identity unavailable; starting service locally")}}async syncCredentialEntries(){this.entries=(await this.deps.tokens.listStatus()).map((e)=>({identity:e.identity,audience:e.audience,expires_at:e.expires_at,valid:e.valid}))}async refreshIdentity(e){try{let t=this.deps.cfg(),r=await this.deps.detectGoogle(t.google.project_id);if(e?.probeServiceAccounts)for(let i of Hr(t))await this.probeServiceAccount(i);let{service_accounts:n,service_account_status:o}=Ju(t,this.accounts);this.cache={user:r.userEmail,project:r.projectID||t.google.project_id,project_source:r.projectSource||(t.google.project_id?"configuration":""),adc:r.adcAvailable,service_accounts:n,service_account_status:o,iap:t.proxy.routes.some((i)=>i.auth.type.toLowerCase()==="iap")},await this.syncCredentialEntries(),this.deps.bus.publish(Se(ld,"",{user:this.cache.user,adc:this.cache.adc})),this.deps.log("auth","INFO",`authentication changed user=${this.cache.user||"(unknown)"} adc=${this.cache.adc}`),this.deps.persistState()}catch(t){this.deps.log("devctl","WARN",`identity refresh failed: ${L(t)}`)}}async probeServiceAccount(e){let t;try{await hA(this.deps.tokens.refresh(`sa:${e}`,"",[]),mA),t="available"}catch{t="unavailable"}return this.accounts.set(e,t),t}}function hA(e,t){return new Promise((r,n)=>{let o=setTimeout(()=>n(Error("identity probe timed out")),t);e.then((i)=>{clearTimeout(o),r(i)},(i)=>{clearTimeout(o),n(i)})})}function gA(e){return e.capabilities.some((t)=>["google_api","iap","service_identity"].includes(t))}te();te();Zu();var wA=600,SA=65535;async function Iy(e,t){let r=Number.isInteger(t)&&(t??0)>0?t:fo(e);for(let n=0;n<wA;n+=1){let o=r+n;if(o>SA)break;if(await jo(o))return o}throw E(Y,`no free MCP port starting at ${r}`)}class ep{listener;disabled=[];token;deps;constructor(e){this.deps=e,this.token=Tm(e.repoRoot())}get instance(){return this.listener}get disabledTools(){return this.disabled}async bootFromPreferences(){let e=Do(this.deps.repoRoot());if(this.setDisabledTools(e.mcp_disabled_tools??[]),e.mcp_enabled)await this.start(e.mcp_port).catch((t)=>this.deps.log("devctl","ERROR",L(t)))}async start(e){if(this.listener?.isRunning())return;let t=await Iy(this.deps.repoRoot(),e);this.listener=this.deps.createListener({port:t,token:this.token,hostApi:this.deps.hostApi(),onEvent:(r,n)=>this.deps.log("mcp",r,`mcp ${n}`),disabledTools:()=>this.disabled}),await this.listener.start(),this.deps.persistState()}setDisabledTools(e){let t=e.filter((n)=>this.deps.isKnownTool(n)),r=this.disabled.join(",");if(this.disabled=[...new Set(t)].sort(),this.disabled.join(",")===r)return;this.deps.log("devctl","INFO",this.disabled.length===0?"all MCP tools enabled":`MCP tools disabled: ${this.disabled.join(", ")}`)}async stop(){await this.listener?.stop(),this.listener=void 0,this.deps.persistState()}}class tp{server;tokenEP;boundURL="";suppressed=!1;deps;constructor(e){this.deps=e}get instance(){return this.server}get tokenEndpoint(){return this.tokenEP}get boundTokenURL(){return this.boundURL}get isSuppressed(){return this.suppressed}setSuppressed(e){this.suppressed=e}async start(){if(this.server?.isRunning())return;this.server=new $u(this.deps.cfg().proxy,this.deps.tokens,this.deps.logs,this.deps.bus,this.deps.detector,this.deps.middleware(),(t,r)=>this.deps.ports().get(t)?.[r||"http"]),await this.server.start();let e=this.deps.cfg();if(e.proxy.token_endpoint.enabled)this.tokenEP=new Fu(e.proxy.token_endpoint.host||"127.0.0.1",e.proxy.token_endpoint.port,this.deps.internalTok(),this.deps.tokens),await this.tokenEP.start(),this.boundURL=`http://127.0.0.1:${this.tokenEP.listenPort()}/token`;this.deps.persistState()}async stop(){await this.server?.stop(),await this.tokenEP?.stop(),this.deps.persistState()}}At();var kA=3000,My=5000,Oy=60;class rp{timer;cpu=[];mem=[];lastAt=0;deps;constructor(e){this.deps=e}start(){if(this.timer)return;this.timer=setInterval(()=>void this.poll(),kA)}stop(){if(this.timer)clearInterval(this.timer),this.timer=void 0}series(){return{interval_ms:My,cpu:[...this.cpu],mem:[...this.mem]}}async poll(){this.recordStatsSample();let e=[];for(let r of this.deps.runtimes().values())if(r.pid>0&&(r.state===je||r.state===pr||r.state===Br))e.push(r.pid);if(e.length===0)return;let t=await iy(e);for(let r of this.deps.runtimes().values()){let n=r.pid>0?t.get(r.pid):void 0;if(n)r.cpuPercent=n.cpuPercent,r.memoryKB=n.memoryKB}}recordStatsSample(){let e=this.deps.clock.unixMs();if(e-this.lastAt<My)return;this.lastAt=e;let t=Yu(),r=t.cpuCount>0?t.loadAvg1/t.cpuCount:0,n=t.memTotalKB>0?1-t.memAvailableKB/t.memTotalKB:0;if(this.cpu.push(r),this.mem.push(n),this.cpu.length>Oy)this.cpu.shift();if(this.mem.length>Oy)this.mem.shift()}}function np(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}function En(e){if(!Array.isArray(e))return[];return e.filter((t)=>typeof t==="string")}function us(e){if(!np(e))return;let t={};for(let[r,n]of Object.entries(e))if(typeof n==="string")t[r]=n;return t}te();import{createServer as CA}from"net";var PA=2000;class op{server;deps;constructor(e){this.deps=e}get nativeServer(){return this.server}removeStaleSocket(e){if(process.platform==="win32")return;if(this.deps.socketExists(e))try{this.deps.unlinkSocket(e)}catch{this.deps.log("devctl","WARN","unable to remove stale socket")}}listen(e){return new Promise((t,r)=>{this.server=CA((n)=>{this.handleConn(n)}),this.server.on("error",r),this.server.listen(e,()=>t())})}close(){this.server?.close(),this.server=void 0}handleConn(e){let t="",r=[],n=!1,o=()=>{while(r.length>0){let a=r[0],c=e.write(`${JSON.stringify(a)}
2279
+ `);if(r.shift(),!c){n=!0,e.once("drain",()=>{n=!1,o()});return}}},i=(a)=>{if(a.id===void 0&&a.event!==void 0&&r.length>=PA){let l=r.findIndex((d)=>d.id===void 0&&d.event!==void 0);if(l>=0)r.splice(l,1)}if(r.push(a),!n)o()},s=this.deps.subscribe((a)=>i({event:a}));e.on("error",(a)=>{this.deps.log("devctl","WARN",`client connection error: ${L(a)}`)}),e.on("data",(a)=>{t+=a.toString("utf8");let c=t.split(`
2280
+ `);t=c.pop()??"";for(let l of c){if(l.trim()==="")continue;this.dispatchLine(l,i)}}),e.on("close",()=>s())}async dispatchLine(e,t){let r;try{r=JSON.parse(e)}catch{t({error:"invalid json"});return}try{let n=await this.deps.dispatch(r.method??"",r.params);t({id:r.id,result:n})}catch(n){let o=ph(n);t({id:r.id,error:o.error,kind:o.kind,hint:o.hint,service:o.service})}}}te();jr();At();class ip{cfg;sessionID;internalTok;bus;logs;procs;tokens;detector;env;proxy;mcp;resources;runtimes=new Map;ports=new Map;lock;shuttingDown=!1;detached=!1;identity;rpc;detectGoogleFn;inspectProcessFn;processAliveFn;acquireLockFn;socketExistsFn;unlinkSocketFn;registry;restartRequired=[];processMeta=new Map;configWatcher;watchTimer;setupMode;serviceWatchers;pluginMtimes=new Map;orchestrator;commands;clock;fs;healthCheckers;constructor(e,t){this.healthCheckers=t.healthCheckers,this.cfg=e,this.sessionID=t.sessionID,this.internalTok=id(),this.clock=t.clock,this.fs=t.fs,this.bus=t.bus,this.detectGoogleFn=t.detectGoogle,this.inspectProcessFn=t.inspectProcess,this.processAliveFn=t.processAlive,this.acquireLockFn=t.acquireLock,this.socketExistsFn=t.socketExists,this.unlinkSocketFn=t.unlinkSocket,this.detector=t.detector,this.logs=t.logs,this.procs=t.procs,this.orchestrator=t.orchestrator,this.tokens=t.tokens,this.proxy=new tp({cfg:()=>this.cfg,ports:()=>this.ports,tokens:this.tokens,logs:this.logs,bus:this.bus,detector:this.detector,internalTok:()=>this.internalTok,middleware:()=>this.registry?.proxyMiddleware??[],persistState:()=>this.persistState()}),this.env=new Vu({cfg:()=>this.cfg,ports:()=>this.ports,proxy:()=>this.proxy.instance,tokenEndpoint:()=>this.proxy.tokenEndpoint,boundTokenURL:()=>this.proxy.boundTokenURL,internalTok:()=>this.internalTok,tokens:this.tokens,environmentSources:()=>this.registry?.environmentSources}),this.mcp=new ep({repoRoot:()=>this.cfg.repoRoot,createListener:t.createMcpListener,hostApi:()=>this.asMcpHost(),isKnownTool:t.isKnownTool,log:(r,n,o)=>this.log(r,n,o),persistState:()=>this.persistState()}),this.resources=new rp({clock:this.clock,runtimes:()=>this.runtimes}),this.identity=new Xu({cfg:()=>this.cfg,tokens:this.tokens,detectGoogle:(r)=>this.detectGoogleFn(r),clock:this.clock,bus:this.bus,logs:this.logs,persistState:()=>this.persistState(),fail:(r,n)=>this.fail(r,n),log:(r,n,o)=>this.log(r,n,o),identityProviders:()=>this.registry?.identityProviders}),this.rpc=new op({dispatch:(r,n)=>this.dispatch(r,n),subscribe:(r,n)=>this.bus.subscribe(r,n),log:(r,n,o)=>this.log(r,n,o),socketExists:(r)=>this.socketExistsFn(r),unlinkSocket:(r)=>this.unlinkSocketFn(r)}),this.setupMode=!this.fs.exists(e.configPath);for(let r of Object.keys(e.services))this.runtimes.set(r,Qi(r));this.orchestrator.bind(this.lifecycleSession()),this.commands=t.createCommands(this),this.serviceWatchers=new Gu({repoRoot:()=>this.cfg.repoRoot,log:(r,n,o)=>this.log(r,n,o),isActive:(r)=>this.orchestrator.serviceIsActive(r),restart:(r)=>this.restart([r],{auto:!0})})}get profile(){return this.env.profile}set profile(e){this.env.profile=e}get profileEnv(){return this.env.profileEnv}set profileEnv(e){this.env.profileEnv=e}get clientEnv(){return this.env.clientEnv}get serviceProfile(){return this.env.serviceProfile}get serviceProfileEnv(){return this.env.serviceProfileEnv}snapshotHost(){let e=this;return{get sessionID(){return e.sessionID},get cfg(){return e.cfg},get profile(){return e.profile},get runtimes(){return e.runtimes},get ports(){return e.ports},get serviceProfile(){return e.serviceProfile},get clientEnv(){return e.clientEnv},get proxy(){return e.proxy.instance},get mcp(){return e.mcp.instance},get mcpToken(){return e.mcp.token},get mcpDisabledTools(){return e.mcp.disabledTools},get identityCache(){return e.identity.identityCache},get serviceAccountStatus(){return e.identity.serviceAccountStatus},get credentialEntries(){return e.identity.credentialEntries},get detached(){return e.detached},get setupMode(){return e.setupMode},get restartRequired(){return e.restartRequired},get statsSeries(){return e.resources.series()},logs:{snapshot:()=>e.logs.snapshot()},tokens:{storeBackend:()=>e.tokens.storeBackend()}}}reloadHost(){let e=this;return{get cfg(){return e.cfg},set cfg(t){e.cfg=t},get setupMode(){return e.setupMode},set setupMode(t){e.setupMode=t},get configWatcher(){return e.configWatcher},set configWatcher(t){e.configWatcher=t},get watchTimer(){return e.watchTimer},set watchTimer(t){e.watchTimer=t},get restartRequired(){return e.restartRequired},set restartRequired(t){e.restartRequired=t},get fs(){return e.fs},get registry(){return e.registry},set registry(t){e.registry=t},get pluginMtimes(){return e.pluginMtimes},set pluginMtimes(t){e.pluginMtimes=t},get detector(){return e.detector},get bus(){return e.bus},get orchestrator(){return e.orchestrator},get runtimes(){return e.runtimes},get tokens(){return e.tokens},get logs(){return e.logs},get proxy(){return e.proxy.instance},persistState:()=>e.persistState(),log:(t,r,n)=>e.log(t,r,n),refreshIdentity:()=>e.refreshIdentity(),startProxy:()=>e.startProxy(),stopProxy:()=>e.stopProxy(),reload:()=>e.reload(),forgetService:(t)=>e.forgetService(t),syncServiceWatchers:()=>e.serviceWatchers.sync(e.cfg.services)}}recoverHost(){let e=this;return{get cfg(){return e.cfg},set cfg(t){e.cfg=t},get profile(){return e.profile},set profile(t){e.profile=t},get profileEnv(){return e.profileEnv},set profileEnv(t){e.profileEnv=t},get runtimes(){return e.runtimes},get ports(){return e.ports},get processMeta(){return e.processMeta},get serviceProfile(){return e.serviceProfile},get serviceProfileEnv(){return e.serviceProfileEnv},get orchestrator(){return e.orchestrator},get procs(){return e.procs},logs:{append:(t)=>e.logs.append(t)},get clock(){return e.clock},get tokens(){return e.tokens},get registry(){return e.registry},get proxy(){return e.proxy.instance},get tokenEP(){return e.proxy.tokenEndpoint},get boundTokenURL(){return e.proxy.boundTokenURL},get internalTok(){return e.internalTok},get bus(){return e.bus},inspectProcessFn:(t)=>e.inspectProcessFn(t),processAliveFn:(t)=>e.processAliveFn(t),serviceWorkDir:(t)=>e.serviceWorkDir(t),persistState:()=>e.persistState(),setState:(t,r,n,o,i)=>e.setState(t,r,n,o,i),log:(t,r,n)=>e.log(t,r,n)}}async run(){let e=ea(this.cfg.repoRoot);this.lock=this.acquireLockFn(this.cfg.repoRoot,e),this.rpc.removeStaleSocket(e),this.registry=await kc(this.cfg.plugins.map((t)=>t.path),this.cfg.repoRoot),this.pluginMtimes=Hu(this.cfg.plugins.map((t)=>t.path),this.cfg.repoRoot);for(let t of this.registry.loadErrors)this.log("devctl","ERROR",`plugin ${t.path} skipped: ${t.message}`);Nu(this.reloadHost()),ju(this.registry,this.cfg),Bu(this.registry,this.cfg),Uu(this.registry,this.cfg),await this.recoverSession(),this.serviceWatchers.sync(this.cfg.services),Wu(this.reloadHost()),this.persistState(),this.log("devctl","INFO",`supervisor started session=${this.sessionID}`),this.refreshIdentity(),this.resources.start(),await this.mcp.bootFromPreferences(),this.rpc.removeStaleSocket(e),await this.rpc.listen(e)}async dispatch(e,t){let r=np(t)?t:{};switch(e){case"ping":return{session:this.sessionID,version:jt,protocol:Yi};case"start":return this.commands.startService.execute({services:En(r.services),profile:typeof r.profile==="string"&&r.profile!==""?r.profile:void 0,detach:r.detach===!0,client_env:us(r.client_env)});case"stop":return await this.commands.stopService.execute(En(r.services)),null;case"restart":return await this.commands.restartService.execute(En(r.services),{cascade:r.cascade===!0,clientEnv:us(r.client_env)}),null;case"run_task":return this.runTask(typeof r.name==="string"?r.name:"",us(r.client_env)??{});case"exec":return this.execService(typeof r.service==="string"?r.service:"",En(r.command),us(r.client_env)??{},r.print_env===!0);case"auth_refresh":return await this.commands.refreshIdentity.execute(),this.snapshot().identity;case"status":return this.snapshot();case"logs":return await this.queryLogs({services:En(r.services),level:typeof r.level==="string"?r.level:"",search:typeof r.search==="string"?r.search:"",regex:r.regex===!0,source:typeof r.source==="string"?r.source:"",since:typeof r.since==="string"?r.since:"",until:typeof r.until==="string"?r.until:"",export:typeof r.export==="string"?r.export:""});case"logs_page":return await this.queryLogsPage({services:En(r.services),level:typeof r.level==="string"?r.level:"",search:typeof r.search==="string"?r.search:"",regex:r.regex===!0,source:typeof r.source==="string"?r.source:"",since:typeof r.since==="string"?r.since:"",until:typeof r.until==="string"?r.until:"",cursor:typeof r.cursor==="string"?r.cursor:void 0,direction:r.direction==="forward"?"forward":"backward",limit:typeof r.limit==="number"?r.limit:void 0});case"logs_stats":return await this.queryLogsFacets({services:En(r.services),level:typeof r.level==="string"?r.level:"",search:typeof r.search==="string"?r.search:"",regex:r.regex===!0,source:typeof r.source==="string"?r.source:"",since:typeof r.since==="string"?r.since:"",until:typeof r.until==="string"?r.until:""});case"proxy_start":return this.proxy.setSuppressed(!1),await this.commands.startProxy.execute(),null;case"proxy_stop":return this.proxy.setSuppressed(!0),await this.commands.stopProxy.execute(),null;case"mcp_start":{let o=typeof r.port==="number"?r.port:void 0;return await this.startMcp(o??Do(this.cfg.repoRoot).mcp_port),null}case"mcp_stop":return await this.stopMcp(),null;case"mcp_set_tools":{let o=Array.isArray(r.disabled)?r.disabled.filter((i)=>typeof i==="string"):[];return this.setMcpDisabledTools(o),{disabled_tools:[...this.mcp.disabledTools]}}case"reload":return this.commands.reloadConfig.execute();case"config_snapshot":return this.cfg;case"auth_invalidate":return this.tokens.invalidate(),null;case"shutdown":let n=typeof r.stop_services==="boolean"?r.stop_services:Ta(this.cfg.shutdown);return setTimeout(()=>{this.shutdown(n)},50),null;default:throw E(lt,`unknown method ${e}`)}}isDetached(){return this.detached}async start(e){return this.orchestrator.start(e)}lifecycleSession(){let e=this;return{get cfg(){return e.cfg},get profile(){return e.profile},set profile(t){e.profile=t},get profileEnv(){return e.profileEnv},set profileEnv(t){e.profileEnv=t},get detached(){return e.detached},set detached(t){e.detached=t},get proxySuppressed(){return e.proxy.isSuppressed},get runtimes(){return e.runtimes},get ports(){return e.ports},get clientEnv(){return e.clientEnv},get serviceProfile(){return e.serviceProfile},get serviceProfileEnv(){return e.serviceProfileEnv},healthCheckers:{lookup:(t)=>{let r=e.registry?.healthChecks.find((n)=>n.name.toLowerCase()===t.toLowerCase());return r?{check:async(n,o)=>{let i=await r.check(n,o);return{...i,status:i.status}}}:e.healthCheckers.lookup(t)}},logs:{append:(t)=>e.logs.append(t)},bus:e.bus,processMeta:e.processMeta,get containerPrefix(){return`devctl-${ar(e.cfg.repoRoot)}-`},prepareServiceIdentity:(t,r)=>e.identity.prepareServiceIdentity(t,r),resolveServiceExecution:(t,r,n,o,i,s)=>e.env.resolveServiceExecution(t,r,n,o,i,s),detectGoogle:(t)=>e.detectGoogleFn(t),startProxy:()=>e.startProxy(),fail:(t,r)=>e.fail(t,r),claimIfAlreadyUp:(t)=>e.claimIfAlreadyUp(t),assignPendingPorts:(t)=>e.assignPendingPorts(t),setState:(t,r,n,o,i)=>e.setState(t,r,n,o,i),persistState:()=>e.persistState(),log:(t,r,n)=>e.log(t,r,n),releasePorts:(t)=>e.releasePorts(t),forgetService:(t)=>e.forgetService(t),clearRestartRequired:(t)=>e.dropRestartRequired(t)}}async assignPendingPorts(e){try{let t=await hh(this.cfg,e,Object.fromEntries(this.ports));for(let[r,n]of Object.entries(t)){this.ports.set(r,n);let o=Object.entries(n).map(([i,s])=>`${i}=${s}`).join(", ");this.log(r,"INFO",`assigned ports: ${o||"none"}`)}}catch(t){if(t instanceof Ve&&t.service!=="")await this.fail(t.service,t);else this.log("devctl","ERROR",L(t));throw t}}async runTask(e,t){let r=this.cfg.tasks[e];if(!r)throw E(lt,`unknown task ${e}`);if(r.dependencies.length>0)await this.start({services:r.dependencies,client_env:t});let n={...vn(),command:r.command,shell:r.shell,working_dir:r.working_dir,dependencies:r.dependencies,environment:r.environment},{env:o,workDir:i}=await this.env.resolveTaskEnvironment(e,n,t),s=await this.runTransient(`task:${e}`,r.command,r.shell,i,o);return{task:e,...s}}async execService(e,t,r,n=!1){let o=this.cfg.services[e];if(!o)throw E(Ho,`unknown service ${e}`);let i=this.env.serviceProfile.get(e)??this.env.profile,s=i!==""?lo(this.cfg,i):this.env.profileEnv,{env:a,workDir:c}=await this.env.resolveServiceExecution(e,o,i,s,r,!o.container);if(n)return{service:e,code:0,stdout:"",stderr:"",environment:a};if(t.length===0)throw E(lt,"exec command is required");let l=await this.runTransient(`${e}:exec`,{args:t,shell:!1},!1,c,a);return{service:e,...l}}async runTransient(e,t,r,n,o){if(yn(t))return{code:0,stdout:"",stderr:""};this.log(e,"INFO",`running ${t.args.join(" ")}`);let i=await this.procs.runOnce({name:e,args:[...t.args],shell:r||t.shell,workDir:n,env:o,graceMs:xn(this.cfg.shutdown)*1000,onLine:(s,a)=>this.logs.append({timestamp:this.clock.isoNow(),service:e,source:s,stream:s,level:"",message:a,pid:0})});if(i.code!==0)throw E(He,`${e} exited with code ${i.code}`);return i}serviceWorkDir(e){return this.env.serviceWorkDir(e)}async claimIfAlreadyUp(e){return uy(this.recoverHost(),e)}async recoverSession(){return py(this.recoverHost())}async stop(e){return this.orchestrator.stop(e)}forgetService(e){this.orchestrator.health.forget(e),this.runtimes.delete(e),this.ports.delete(e),this.processMeta.delete(e),this.env.forget(e)}async restart(e,t){let r=t?.cascade===!0?co(this.cfg,e):e;await this.orchestrator.restart(e,t),this.dropRestartRequired(r)}dropRestartRequired(e){if(e.length===0)return;let t=new Set(e);this.restartRequired=this.restartRequired.filter((r)=>!t.has(r))}async startProxy(){return this.proxy.start()}async stopProxy(){return this.proxy.stop()}async startMcp(e){return this.mcp.start(e)}setMcpDisabledTools(e){this.mcp.setDisabledTools(e)}async stopMcp(){return this.mcp.stop()}asMcpHost(){return{status:()=>this.commands.getServiceStatus.execute(),logsPage:(e)=>this.queryLogsPage(e),config:()=>this.cfg,validateConfigText:(e)=>Ui(this.cfg.repoRoot,this.cfg.configPath,e),start:(e)=>this.commands.startService.execute(e),stop:(e)=>this.commands.stopService.execute(e),restart:(e,t)=>this.commands.restartService.execute(e,{cascade:t}),reload:()=>this.commands.reloadConfig.execute(),doctor:async()=>{for(let e of Hr(this.cfg))await this.identity.probeServiceAccount(e);return this.commands.runDoctor.execute(this.cfg)},exec:(e,t,r)=>this.execService(e,t,void 0,r),runTask:(e)=>this.runTask(e,{}),startProxy:()=>this.startProxy(),stopProxy:()=>this.stopProxy()}}async reload(){return wy(this.reloadHost())}async shutdown(e){if(this.shuttingDown)return;if(this.shuttingDown=!0,this.persistState(),e)await this.stop([]);if(this.orchestrator.health.dispose(),await this.stopProxy(),await this.stopMcp(),this.configWatcher?.close(),this.serviceWatchers.close(),this.watchTimer)clearTimeout(this.watchTimer);this.resources.stop(),this.rpc.close(),this.lock?.release(),await this.logs.close()}snapshot(){return _y(this.snapshotHost())}async refreshIdentity(e){return this.identity.refreshIdentity(e)}async queryLogs(e){let t=await this.logs.query({services:e.services,level:e.level,search:e.search,regex:e.regex,source:e.source,since:e.since,until:e.until});if(e.export)await this.logs.exportTo(e.export,{services:e.services,level:e.level,search:e.search,regex:e.regex,source:e.source,since:e.since,until:e.until});return{events:t}}async queryLogsPage(e){return this.logs.queryPage({services:e.services,level:e.level,search:e.search,regex:e.regex,source:e.source,since:e.since,until:e.until},{cursor:e.cursor,direction:e.direction,limit:e.limit})}async queryLogsFacets(e){return this.logs.queryFacets({services:e.services,level:e.level,search:e.search,regex:e.regex,source:e.source,since:e.since,until:e.until})}subscribe(e){return this.bus.subscribe(e)}formatStatus(){return Ry(this.snapshot())}setState(e,t,r,n,o){let i=this.runtimes.get(e)??Qi(e);if(!cs(i.state,t))this.log(e,"WARN",`unusual lifecycle ${i.state} \u2192 ${t}`);i.state=t,i.health=r,i.pid=n,i.last_error=o;let s=t===je||t===pr||t===Br;if(i.startTime=s?this.processMeta.get(e)?.startTime.toISOString():void 0,!s)i.cpuPercent=void 0,i.memoryKB=void 0;this.runtimes.set(e,i),this.bus.publish(Se(Om,e,{state:t,health:r,pid:n}))}async releasePorts(e){let t=this.ports.get(e);if(!t)return;let r=this.cfg.services[e];if(r?.container){this.ports.delete(e);return}let n=this.processMeta.get(e);for(let o of Object.values(t)){let i=await zn(o);if(!i||i.pid===process.pid)continue;if(r){let s=await this.inspectProcessFn(i.pid);if(!(s!==void 0&&s.command!==""&&ss({args:n?.command??[...r.command.args],workDir:n?.cwd??this.serviceWorkDir(r),startTime:n?.startTime},s))){this.log(e,"WARN",`port ${o} is held by pid ${i.pid}, which does not match ${e}; leaving it running`);continue}}try{await wa(i),this.log(e,"INFO",`released port ${o} (pid ${i.pid})`)}catch(s){this.log(e,"WARN",L(s))}}this.ports.delete(e)}async fail(e,t){this.orchestrator.health.clearHealthWatch(e),this.orchestrator.health.clearRestartTimer(e),this.orchestrator.health.bumpGeneration(e),this.setState(e,dt,Tt,0,L(t));try{await this.procs.stop(e,xn(this.cfg.shutdown)*1000)}catch(r){this.log(e,"WARN",L(r))}this.bus.publish(Se(la,e,{error:L(t)})),this.log(e,"ERROR",L(t)),this.persistState()}log(e,t,r){this.logs.append({timestamp:this.clock.isoNow(),service:e,source:"devctl",level:t,message:r,pid:0})}persistState(){let e=[];for(let t of this.procs.all()){let r=this.processMeta.get(t.name);e.push({name:t.name,pid:t.pid,command:r?.command??t.args,cwd:r?.cwd??t.workDir,startTime:(r?.startTime??t.startTime).toISOString(),ports:this.ports.get(t.name)??{},profile:this.serviceProfile.get(t.name)??this.profile})}Rm(this.cfg.repoRoot,{session_id:this.sessionID,repo_root:this.cfg.repoRoot,profile:this.profile,processes:e})}}var Fy=500,_A=1e4,RA=2000,EA=new URL("./log-worker.ts",import.meta.url);class Ny{worker;bus;pending=new Map;nextId=1;stats={total:0,errors:0,counts:{}};dead=!1;readySettled=!1;ready;resolveReady=()=>{return};rejectReady=()=>{return};constructor(e,t,r={}){this.bus=t,this.ready=new Promise((n,o)=>{this.resolveReady=n,this.rejectReady=o}),this.worker=new Worker(r.script??EA,{name:"devctl-logs"}),this.worker.addEventListener("message",(n)=>{this.onMessage(n.data)}),this.worker.addEventListener("error",(n)=>{this.markDead(Error(n.message||"log worker failed"))}),this.post({type:"init",config:e})}waitUntilReady(e=Fy){if(this.dead)return Promise.reject(Error("log worker is not running"));if(this.readySettled)return Promise.resolve();return new Promise((t,r)=>{let n=setTimeout(()=>{this.markDead(Error("log worker init timed out"))},e);this.ready.then(()=>{clearTimeout(n),t()},(o)=>{clearTimeout(n),r(o)})})}append(e){if(this.dead)return;try{this.post({type:"append",event:e})}catch{}}async query(e){let t=await this.rpc({type:"query",filter:e});if(!Array.isArray(t))throw Error("log worker query returned a non-array");return t}async queryPage(e,t){let r=await this.rpc({type:"queryPage",filter:e,page:t});if(!TA(r))throw Error("log worker queryPage returned an unexpected payload");return r}async queryFacets(e){let t=await this.rpc({type:"queryFacets",filter:e});if(!AA(t))throw Error("log worker queryFacets returned an unexpected payload");return t}snapshot(){return this.stats}async exportTo(e,t){await this.rpc({type:"exportTo",path:e,filter:t})}setParsers(e,t){if(this.dead)return;this.post({type:"setPluginPaths",paths:[...t??[]]})}setSecrets(e,t){if(this.dead)return;this.post({type:"setSecrets",extraMarkers:e,extraPatterns:t})}async close(){if(this.dead)return;try{await this.rpc({type:"close"},RA)}catch{}finally{this.markDead(Error("log worker closed"))}}rpc(e,t=_A){this.assertAlive();let r=this.nextId;this.nextId+=1;let n={...e,id:r};return new Promise((o,i)=>{let s=setTimeout(()=>{this.markDead(Error(`log worker timed out (${e.type})`))},t);this.pending.set(r,{resolve:o,reject:i,timer:s});try{this.post(n)}catch(a){clearTimeout(s),this.pending.delete(r),i(a instanceof Error?a:Error(String(a)))}})}post(e){this.assertAlive(),this.worker.postMessage(e)}onMessage(e){if(e.type==="ready"){this.settleReady();return}if(e.type==="appended"){this.stats=e.stats,this.bus?.publish(Se(Io,e.event.service,{event:e.event,level:e.event.level}));return}let t=this.pending.get(e.id);if(!t)return;if(this.pending.delete(e.id),clearTimeout(t.timer),e.type==="error"){t.reject(Error(e.error));return}t.resolve(e.result)}settleReady(){if(this.readySettled||this.dead)return;this.readySettled=!0,this.resolveReady()}assertAlive(){if(this.dead)throw Error("log worker is not running")}markDead(e){if(this.dead)return;if(this.dead=!0,!this.readySettled)this.readySettled=!0,this.rejectReady(e);this.rejectAll(e);try{this.worker.terminate()}catch{}}rejectAll(e){for(let t of this.pending.values())clearTimeout(t.timer),t.reject(e);this.pending.clear()}}function TA(e){if(e===null||Array.isArray(e)||!("events"in e))return!1;return Array.isArray(e.events)}function AA(e){return e!==null&&!Array.isArray(e)&&"byService"in e}function $y(e,t,r){return nh(new Rd(e.max,t,r,e.persist,e.directory,e.sessionID,e.retentionDays,e.maxSessionLogs))}async function Hy(e,t,r,n={}){if(n.standalone??Bun.isStandaloneExecutable===!0)return{logs:$y(e,t,r),usingWorker:!1};try{let i=new Ny(e,t,{script:n.script});return await i.waitUntilReady(n.initTimeoutMs??Fy),{logs:i,usingWorker:!0}}catch{return{logs:$y(e,t,r),usingWorker:!1}}}ni();import{createServer as tL}from"http";te();fs();var rL="2025-03-26",cb="2.0",ob=-32700,ib=-32600,nL=-32601,sb=-32602,oL=-32603;function iL(e){return e!==""&&Cn(e)}class lp{server;running=!1;addr="";boundPort=0;opts;constructor(e){this.opts=e}disabled(){return this.opts.disabledTools?.()??[]}address(){return this.addr}listenPort(){return this.boundPort}isRunning(){return this.running}emit(e,t){this.opts.onEvent?.(e,t)}start(){if(!iL(this.opts.host))return Promise.reject(E(lt,`refusing to bind MCP to ${this.opts.host}`));return new Promise((e,t)=>{this.server=tL((r,n)=>{this.serve(r,n)}),this.server.on("error",(r)=>t(Pe(lt,`unable to listen on ${this.opts.host}:${this.opts.port}`,r))),this.server.listen(this.opts.port,this.opts.host,()=>{let r=this.server?.address();this.boundPort=typeof r==="object"&&r?r.port:this.opts.port,this.addr=`${this.opts.host}:${this.boundPort}`,this.running=!0,this.emit("INFO",`listening on ${this.addr}`),e()})})}stop(){let e=this.server,t=this.running;if(!e)return this.running=!1,Promise.resolve();return new Promise((r)=>{e.close(()=>{if(this.running=!1,this.addr="",this.boundPort=0,this.server=void 0,t)this.emit("INFO","stopped");r()})})}async serve(e,t){if(e.method==="OPTIONS"){t.writeHead(204,gs()),t.end();return}if(!this.authorized(e)){this.emit("WARN",`rejected unauthorized request from ${ab(e)}`),ii(t,401,{error:"unauthorized"});return}if(aL(e)!=="/mcp"){ii(t,404,{error:"not found"});return}if(e.method==="GET"||e.method==="DELETE"){t.writeHead(405,{Allow:"POST, OPTIONS",...gs()}),t.end();return}if(e.method!=="POST"){t.writeHead(405,{Allow:"POST, OPTIONS",...gs()}),t.end();return}let n;try{n=await cL(e)}catch(a){ii(t,400,ms(null,ob,a instanceof Error?a.message:"invalid body"));return}let o;try{o=JSON.parse(n)}catch{ii(t,400,ms(null,ob,"parse error"));return}let i=ab(e);if(Array.isArray(o)){let a=[];for(let c of o){let l=await this.dispatch(c,i);if(l!==void 0)a.push(l)}ii(t,200,a);return}let s=await this.dispatch(o,i);if(s===void 0){t.writeHead(202,gs()),t.end();return}ii(t,200,s)}authorized(e){if(this.opts.token==="")return!0;return(e.headers.authorization??"")===`Bearer ${this.opts.token}`}async dispatch(e,t){if(!Ec(e))return ms(null,ib,"invalid request");let r=e,n=r.id??null,o=r.id===void 0;if(r.method===void 0||r.method==="")return o?void 0:ms(n,ib,"missing method");try{let i=await this.handleMethod(r.method,Ec(r.params)?r.params:{},t);if(o)return;return{jsonrpc:cb,id:n,result:i}}catch(i){if(o)return;let s=i instanceof hs?i.code:oL,a=i instanceof Error?i.message:"internal error";return this.emit("WARN",`rpc error method=${r.method} addr=${t}: ${a}`),ms(n,s,a)}}async handleMethod(e,t,r){switch(e){case"initialize":{let n=sL(t);return this.emit("INFO",`client connected addr=${r}${n?` (${n})`:""}`),{protocolVersion:rL,capabilities:{tools:{},resources:{}},serverInfo:{name:"devctl",version:jt}}}case"notifications/initialized":case"ping":return{};case"tools/list":return{tools:Jy(this.disabled()).map((n)=>({name:n.name,description:n.description,inputSchema:n.inputSchema}))};case"tools/call":return this.callTool(t);case"resources/list":return{resources:ap.map((n)=>({uri:n,name:n.replace("devctl://",""),mimeType:"application/json"}))};case"resources/read":return this.readResource(t);default:throw new hs(nL,`method not found: ${e}`)}}async callTool(e){let t=typeof e.name==="string"?e.name:"";if(t==="")throw new hs(sb,"tool name is required");if(Rc(t)&&!An(t,this.disabled())){let o=`tool ${t} is disabled in devctl's MCP settings`;return this.emit("WARN",`tool call rejected name=${t}: disabled`),{content:[{type:"text",text:o}],isError:!0}}let r=Ec(e.arguments)?e.arguments:{},n=Date.now();try{let o=await tb(this.opts.hostApi,t,r);return this.emit("INFO",`tool call name=${t} duration=${Date.now()-n}ms`),{content:[{type:"text",text:JSON.stringify(o,null,2)}]}}catch(o){let i=o instanceof Error?o.message:"tool failed";return this.emit("WARN",`tool call failed name=${t} duration=${Date.now()-n}ms: ${i}`),{content:[{type:"text",text:i}],isError:!0}}}async readResource(e){let t=typeof e.uri==="string"?e.uri:"";if(!rb(t))throw new hs(sb,`unknown resource ${t}`);let r=await nb(this.opts.hostApi,t);return this.emit("INFO",`resource read uri=${t}`),{contents:[{uri:t,mimeType:"application/json",text:JSON.stringify(r,null,2)}]}}}function sL(e){let t=e.clientInfo;if(!Ec(t))return"";let r=typeof t.name==="string"?t.name:"",n=typeof t.version==="string"?t.version:"";if(r==="")return"";return n===""?r:`${r} ${n}`}function ab(e){return e.socket.remoteAddress??"unknown"}class hs extends Error{code;constructor(e,t){super(t);this.code=e}}function gs(){return{"Access-Control-Allow-Origin":"*","Access-Control-Allow-Headers":"Authorization, Content-Type, Accept, MCP-Protocol-Version, Mcp-Session-Id","Access-Control-Allow-Methods":"POST, OPTIONS"}}function ii(e,t,r){let n=JSON.stringify(r);e.writeHead(t,{"Content-Type":"application/json","Content-Length":Buffer.byteLength(n),...gs()}),e.end(n)}function ms(e,t,r){return{jsonrpc:cb,id:e,error:{code:t,message:r}}}function aL(e){let t=e.url??"/",r=t.indexOf("?");return r>=0?t.slice(0,r):t}function cL(e){return new Promise((t,r)=>{let n=[];e.on("data",(o)=>{n.push(o)}),e.on("end",()=>{t(Buffer.concat(n).toString("utf8"))}),e.on("error",r)})}function Ec(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}fs();var uL=(e)=>new lp({host:"127.0.0.1",...e});async function pL(e,t={}){let r=t.clock??Qa,n=t.fs??sy,o=t.processes??new Eu,i=t.bus??new ud(2048),s=t.tokens??new ao(e.auth.refresh_threshold_seconds*1000,_n(),i,void 0,r),a=new Iu(o,r),c=rd(),l=new mr(e.secrets.extra_markers,e.secrets.extra_patterns),d=Bun.isStandaloneExecutable===!0,{logs:u,usingWorker:f}=await Hy({max:e.logs.max_memory_events,persist:e.logs.persistence.enabled,directory:e.logs.persistence.directory,sessionID:c,retentionDays:e.logs.persistence.retention_days,maxSessionLogs:e.logs.persistence.max_session_logs,extraMarkers:e.secrets.extra_markers,extraPatterns:e.secrets.extra_patterns},i,l,{standalone:d});if(!f&&!d)u.append({timestamp:r.isoNow(),service:"devctl",source:"devctl",level:"WARN",message:"log worker failed to start; using in-process log store",pid:0});return{supervisor:new ip(e,{healthCheckers:t.healthCheckers??Nv([]),detectGoogle:t.detectGoogle??io,tokens:s,inspectProcess:oy,processAlive:Qt,acquireLock:Em,socketExists:lL,unlinkSocket:dL,procs:o,orchestrator:a,clock:r,fs:n,bus:i,logs:u,detector:l,sessionID:c,createMcpListener:t.createMcpListener??uL,isKnownTool:Rc,createCommands:(m)=>fv(m,rc(Yo({tokens:s})),a)}),orchestrator:a,clock:r,fs:n}}async function lb(e,t){let r=to(e,t),{supervisor:n}=await pL(r),o=!1,i=()=>{if(o)return;o=!0,n.shutdown(Ta(r.shutdown)).finally(()=>process.exit(0))};process.on("SIGINT",i),process.on("SIGTERM",i),await n.run()}ht();te();Bf();import{Command as GO}from"commander";var Jk=["zsh","bash","fish"];function Xk(e){if(e==="zsh")return`#compdef devctl
2209
2281
  _devctl() {
2210
2282
  local -a opts
2211
2283
  local line="\${words[*]}"
@@ -2225,91 +2297,91 @@ complete -F _devctl devctl
2225
2297
  devctl __complete (commandline -cp)
2226
2298
  end
2227
2299
  complete -c devctl -f -a "(__devctl_complete)"
2228
- `;throw Error(`unsupported shell "${e}"; use ${Gk.join(", ")}`)}function jf(e,t){let r=e.trim().split(/\s+/).filter((l)=>l!==""),n=e.endsWith(" ")?"":r[r.length-1]??"",o=e.endsWith(" ")?r:r.slice(0,-1),i=o[1]??"",s=Object.keys(t.services).sort(),a=Object.keys(t.profiles).sort(),c=["start","run","exec","down","daemon","stop","restart","status","logs","doctor","setup","auth","proxy","mcp","config","reload","attach","completion","update","version"];if(i===""||o.length<2)return zr(c,n);if(i==="start"||i==="stop"||i==="restart"||i==="logs"){if(n==="--profile"||o[o.length-1]==="--profile")return zr(a,n==="--profile"?"":n);return zr([...s,"--profile","--detach","--json","--since","--until"],n)}if(i==="completion")return zr([...Gk].sort(),n);if(i==="run")return zr([...Object.keys(t.tasks).sort(),"--json"],n);if(i==="exec")return zr([...s,"--print-env","--reveal","--json"],n);if(i==="auth")return zr(["status","login","logout","refresh"],n);if(i==="config")return zr(["validate","show","diff"],n);if(i==="proxy")return zr(["status","start","stop"],n);return zr(c,n)}function zr(e,t){if(t==="")return e;return e.filter((r)=>r.startsWith(t))}function be(e){return e.optsWithGlobals().config??""}function E(e){try{process.stdout.write(e)}catch(t){if(t.code==="EPIPE")process.exit(0);throw t}}function qk(e,t){let r=e.command("auth").description("Google authentication");r.command("status").option("--json","machine-readable output").action(async(n)=>{let o="";try{o=t.load("",be(e)).google.project_id}catch{o=""}let i=await t.detectGoogle(o);if(n.json){E(JSON.stringify(i,null,2)+`
2229
- `);return}if(E(`User: ${i.userEmail||"(unknown)"}
2230
- `),E(`Project: ${i.projectID||"(unset)"}
2231
- `),i.projectID!=="")E(`Source: ${i.projectSource}
2232
- `);E(`ADC: ${i.adcAvailable}
2233
- `),E(`gcloud: ${i.gcloudInstalled}
2234
- `)}),r.command("login").action(async()=>{await t.loginGoogle()}),r.command("logout").action(async()=>{await t.logoutGoogle()}),r.command("refresh").option("--json","machine-readable output").action(async(n)=>{let o=await t.openController("",be(e),!1);try{let i=await t.refreshUserToken("user"),s=o.client?await o.refreshAuth():void 0;if(n.json){E(JSON.stringify({identity:i.identity,expires_at:i.expiresAt.toISOString(),service_account_status:s?.service_account_status??{}},null,2)+`
2235
- `);return}if(E(`refreshed credentials expire ${i.expiresAt.toISOString()}
2236
- `),s)for(let[a,c]of Object.entries(s.service_account_status).sort(([l],[d])=>l.localeCompare(d)))E(`service account ${a}: ${c}
2237
- `)}finally{await o.close()}})}te();Tt();ff();import{mkdirSync as AO}from"fs";import{basename as LO,dirname as DO}from"path";import{stringify as IO}from"yaml";Da();function Vk(e,t){e.command("reload").description("reload configuration").action(async()=>{let r=await t.openController("",be(e),!0);try{let n=await r.reload();if(n.restart_required.length===0)E(`configuration reloaded
2238
- `);else E(`configuration reloaded; restart required: ${n.restart_required.join(", ")}
2239
- `);if(n.supervisor_restart_required&&n.supervisor_restart_required.length>0)E(`note: ${oc(n.supervisor_restart_required)}
2240
- `)}finally{await r.close()}})}function zk(e,t){let r=e.command("config");r.command("validate").option("--json").action((n)=>{try{let o=t.load("",be(e)),i=t.validate(o);if(n.json){E(JSON.stringify({valid:i.length===0,issues:i},null,2)+`
2300
+ `;throw Error(`unsupported shell "${e}"; use ${Jk.join(", ")}`)}function Uf(e,t){let r=e.trim().split(/\s+/).filter((l)=>l!==""),n=e.endsWith(" ")?"":r[r.length-1]??"",o=e.endsWith(" ")?r:r.slice(0,-1),i=o[1]??"",s=Object.keys(t.services).sort(),a=Object.keys(t.profiles).sort(),c=["start","run","exec","down","daemon","stop","restart","status","logs","doctor","setup","auth","proxy","mcp","config","reload","attach","completion","update","version"];if(i===""||o.length<2)return zr(c,n);if(i==="start"||i==="stop"||i==="restart"||i==="logs"){if(n==="--profile"||o[o.length-1]==="--profile")return zr(a,n==="--profile"?"":n);return zr([...s,"--profile","--detach","--json","--since","--until"],n)}if(i==="completion")return zr([...Jk].sort(),n);if(i==="run")return zr([...Object.keys(t.tasks).sort(),"--json"],n);if(i==="exec")return zr([...s,"--print-env","--reveal","--json"],n);if(i==="auth")return zr(["status","login","logout","refresh"],n);if(i==="config")return zr(["validate","show","diff"],n);if(i==="proxy")return zr(["status","start","stop"],n);return zr(c,n)}function zr(e,t){if(t==="")return e;return e.filter((r)=>r.startsWith(t))}function xe(e){return e.optsWithGlobals().config??""}function T(e){try{process.stdout.write(e)}catch(t){if(t.code==="EPIPE")process.exit(0);throw t}}function Qk(e,t){let r=e.command("auth").description("Google authentication");r.command("status").option("--json","machine-readable output").action(async(n)=>{let o="";try{o=t.load("",xe(e)).google.project_id}catch{o=""}let i=await t.detectGoogle(o);if(n.json){T(JSON.stringify(i,null,2)+`
2301
+ `);return}if(T(`User: ${i.userEmail||"(unknown)"}
2302
+ `),T(`Project: ${i.projectID||"(unset)"}
2303
+ `),i.projectID!=="")T(`Source: ${i.projectSource}
2304
+ `);T(`ADC: ${i.adcAvailable}
2305
+ `),T(`gcloud: ${i.gcloudInstalled}
2306
+ `)}),r.command("login").action(async()=>{await t.loginGoogle()}),r.command("logout").action(async()=>{await t.logoutGoogle()}),r.command("refresh").option("--json","machine-readable output").action(async(n)=>{let o=await t.openController("",xe(e),!1);try{let i=await t.refreshUserToken("user"),s=o.client?await o.refreshAuth():void 0;if(n.json){T(JSON.stringify({identity:i.identity,expires_at:i.expiresAt.toISOString(),service_account_status:s?.service_account_status??{}},null,2)+`
2307
+ `);return}if(T(`refreshed credentials expire ${i.expiresAt.toISOString()}
2308
+ `),s)for(let[a,c]of Object.entries(s.service_account_status).sort(([l],[d])=>l.localeCompare(d)))T(`service account ${a}: ${c}
2309
+ `)}finally{await o.close()}})}te();At();hf();import{mkdirSync as $O}from"fs";import{basename as FO,dirname as NO}from"path";import{stringify as HO}from"yaml";Ma();function Zk(e,t){e.command("reload").description("reload configuration").action(async()=>{let r=await t.openController("",xe(e),!0);try{let n=await r.reload();if(n.restart_required.length===0)T(`configuration reloaded
2310
+ `);else T(`configuration reloaded; restart required: ${n.restart_required.join(", ")}
2311
+ `);if(n.supervisor_restart_required&&n.supervisor_restart_required.length>0)T(`note: ${sc(n.supervisor_restart_required)}
2312
+ `)}finally{await r.close()}})}function eC(e,t){let r=e.command("config");r.command("validate").option("--json").action((n)=>{try{let o=t.load("",xe(e)),i=t.validate(o);if(n.json){T(JSON.stringify({valid:i.length===0,issues:i},null,2)+`
2241
2313
  `);return}if(i.length>0)throw Error(i.join(`
2242
- `));E(`configuration is valid
2243
- `)}catch(o){if(n.json){E(JSON.stringify({valid:!1,error:A(o)},null,2)+`
2244
- `);return}throw o}}),r.command("diff").description("show where effective configuration values came from").option("--json").action((n)=>{let o=t.load("",be(e)),i=t.configDiff(o);if(n.json){E(JSON.stringify({entries:i},null,2)+`
2245
- `);return}for(let s of i){E(`${s.path} = ${JSON.stringify(s.value)}
2246
- `),E(` winner: ${s.layer} (${s.source})
2247
- `);for(let a of s.shadowed)E(` shadowed: ${a.layer} (${a.source})
2248
- `)}}),r.command("show").option("--json").action((n)=>{let o=t.load("",be(e));if(n.json){E(JSON.stringify(o,null,2)+`
2249
- `);return}E(IO(o))}),r.command("import").command("compose").argument("<file>","docker-compose.yml or compose.yaml").option("--write","write mapped fields under .devctl/config.yaml").action((n,o)=>{let i=t.readTextFile(n),{repo:s,cfgPath:a}=Hd("",be(e)),c=bl(i,LO(s)),l=t.validateConfigText(s,a,c.yaml);if(E(wl(c)),l.length>0){if(E(`validation:
2314
+ `));T(`configuration is valid
2315
+ `)}catch(o){if(n.json){T(JSON.stringify({valid:!1,error:L(o)},null,2)+`
2316
+ `);return}throw o}}),r.command("diff").description("show where effective configuration values came from").option("--json").action((n)=>{let o=t.load("",xe(e)),i=t.configDiff(o);if(n.json){T(JSON.stringify({entries:i},null,2)+`
2317
+ `);return}for(let s of i){T(`${s.path} = ${JSON.stringify(s.value)}
2318
+ `),T(` winner: ${s.layer} (${s.source})
2319
+ `);for(let a of s.shadowed)T(` shadowed: ${a.layer} (${a.source})
2320
+ `)}}),r.command("show").option("--json").action((n)=>{let o=t.load("",xe(e));if(n.json){T(JSON.stringify(o,null,2)+`
2321
+ `);return}T(HO(o))}),r.command("import").command("compose").argument("<file>","docker-compose.yml or compose.yaml").option("--write","write mapped fields under .devctl/config.yaml").action((n,o)=>{let i=t.readTextFile(n),{repo:s,cfgPath:a}=jd("",xe(e)),c=wl(i,FO(s)),l=t.validateConfigText(s,a,c.yaml);if(T(Sl(c)),l.length>0){if(T(`validation:
2250
2322
  ${l.map((d)=>` ${d}`).join(`
2251
2323
  `)}
2252
- `),o.write===!0)throw Error("refusing to write an invalid mapping");return}if(o.write!==!0){E(`dry-run; pass --write to save under .devctl/config.yaml
2253
- `);return}if(t.fileExists(a))throw Error(`configuration already exists at ${a}; not overwriting`);AO(DO(a),{recursive:!0}),t.writeTextFile(a,c.yaml),E(`wrote ${a}
2254
- `)})}ni();Tt();import{setTimeout as MO}from"timers/promises";function Jk(e,t){e.command("exec").argument("<service>","service whose execution context to use").argument("[command...]","command and arguments").option("--print-env","print the resolved environment instead of running a command").option("--reveal","show secret values with --print-env").option("--json","machine-readable output").action(async(r,n,o)=>{if(!o.printEnv&&n.length===0)throw Error("exec command is required (or use --print-env)");let i=await t.openController("",be(e),!0);try{let s=await i.execService(r,n,o.printEnv===!0);if(s.environment){let a=o.reveal?s.environment:new mr(i.cfg.secrets.extra_markers,i.cfg.secrets.extra_patterns).redactMap(s.environment);if(o.json)E(JSON.stringify({...s,environment:a},null,2)+`
2255
- `);else for(let c of Object.keys(a).sort())E(`${c}=${a[c]}
2256
- `)}else if(o.json)E(JSON.stringify(s,null,2)+`
2257
- `);else{if(s.stdout)E(s.stdout);if(s.stderr)process.stderr.write(s.stderr)}}finally{await i.close()}})}function Xk(e,t){e.command("run").argument("<task>","task to run").option("--json","machine-readable output").action(async(r,n)=>{let o=await t.openController("",be(e),!0);try{let i=await o.runTask(r);if(n.json)E(JSON.stringify(i,null,2)+`
2258
- `);else{if(i.stdout)E(i.stdout);if(i.stderr)process.stderr.write(i.stderr)}}finally{await o.close()}})}function Qk(e,t){e.command("start").argument("[services...]","services to start").option("--profile <name>","profile to start").option("--detach","deprecated, no longer changes behavior: the daemon already outlives this command; use `devctl down` to stop it").option("--json","machine-readable output").action(async(r,n)=>{if(n.detach)process.stderr.write("warning: --detach is deprecated and no longer changes behavior \u2014 the daemon already keeps running after `start` exits; use `devctl down` to stop it\n");let o=await t.openController("",be(e),!0);try{let i=await o.start({services:r,profile:n.profile,detach:n.detach===!0});if(n.json){E(JSON.stringify(i,null,2)+`
2259
- `);return}if(E(Zg(i)),n.detach)E(`detached; services continue running
2260
- `)}finally{await o.close()}})}function Zk(e,t){e.command("stop").argument("[services...]","services to stop").option("--json","machine-readable output").action(async(r,n)=>{let o=await t.openController("",be(e),!0);try{if(await o.stop(r),n.json)E(JSON.stringify({stopped:r},null,2)+`
2261
- `)}finally{await o.close()}})}function eC(e,t){e.command("restart").argument("[services...]").option("--cascade","also restart transitive dependents (default: only the named services)").option("--json","machine-readable output").action(async(r,n)=>{let o=await t.openController("",be(e),!0);try{if(await o.restart(r,n.cascade===!0),n.json)E(JSON.stringify({restarted:r,cascade:n.cascade===!0},null,2)+`
2262
- `)}finally{await o.close()}})}async function Yk(e,t,r){let{repoRoot:n,client:o}=await e.findDaemon("",r.repo??"",be(t));try{if(!o){let s=e.readPersistedState(n);if(r.json){E(JSON.stringify({running:!1,persisted:s},null,2)+`
2263
- `);return}if(E(`supervisor is not running
2264
- `),s&&s.processes.length>0){E(`last session ${s.session_id} profile ${s.profile||"(none)"}
2265
- `);for(let a of s.processes)E(`${a.name} stopped UNKNOWN ${a.pid}
2266
- `)}return}e.assertMethodAllowed(o,"status");let i=await o.call("status",null);if(r.json){E(JSON.stringify(i,null,2)+`
2267
- `);return}E(`PROFILE: ${i.profile||"(none)"}
2324
+ `),o.write===!0)throw Error("refusing to write an invalid mapping");return}if(o.write!==!0){T(`dry-run; pass --write to save under .devctl/config.yaml
2325
+ `);return}if(t.fileExists(a))throw Error(`configuration already exists at ${a}; not overwriting`);$O(NO(a),{recursive:!0}),t.writeTextFile(a,c.yaml),T(`wrote ${a}
2326
+ `)})}ni();At();import{setTimeout as jO}from"timers/promises";function rC(e,t){e.command("exec").argument("<service>","service whose execution context to use").argument("[command...]","command and arguments").option("--print-env","print the resolved environment instead of running a command").option("--reveal","show secret values with --print-env").option("--json","machine-readable output").action(async(r,n,o)=>{if(!o.printEnv&&n.length===0)throw Error("exec command is required (or use --print-env)");let i=await t.openController("",xe(e),!0);try{let s=await i.execService(r,n,o.printEnv===!0);if(s.environment){let a=o.reveal?s.environment:new mr(i.cfg.secrets.extra_markers,i.cfg.secrets.extra_patterns).redactMap(s.environment);if(o.json)T(JSON.stringify({...s,environment:a},null,2)+`
2327
+ `);else for(let c of Object.keys(a).sort())T(`${c}=${a[c]}
2328
+ `)}else if(o.json)T(JSON.stringify(s,null,2)+`
2329
+ `);else{if(s.stdout)T(s.stdout);if(s.stderr)process.stderr.write(s.stderr)}}finally{await i.close()}})}function nC(e,t){e.command("run").argument("<task>","task to run").option("--json","machine-readable output").action(async(r,n)=>{let o=await t.openController("",xe(e),!0);try{let i=await o.runTask(r);if(n.json)T(JSON.stringify(i,null,2)+`
2330
+ `);else{if(i.stdout)T(i.stdout);if(i.stderr)process.stderr.write(i.stderr)}}finally{await o.close()}})}function oC(e,t){e.command("start").argument("[services...]","services to start").option("--profile <name>","profile to start").option("--detach","deprecated, no longer changes behavior: the daemon already outlives this command; use `devctl down` to stop it").option("--json","machine-readable output").action(async(r,n)=>{if(n.detach)process.stderr.write("warning: --detach is deprecated and no longer changes behavior \u2014 the daemon already keeps running after `start` exits; use `devctl down` to stop it\n");let o=await t.openController("",xe(e),!0);try{let i=await o.start({services:r,profile:n.profile,detach:n.detach===!0});if(n.json){T(JSON.stringify(i,null,2)+`
2331
+ `);return}if(T(nv(i)),n.detach)T(`detached; services continue running
2332
+ `)}finally{await o.close()}})}function iC(e,t){e.command("stop").argument("[services...]","services to stop").option("--json","machine-readable output").action(async(r,n)=>{let o=await t.openController("",xe(e),!0);try{if(await o.stop(r),n.json)T(JSON.stringify({stopped:r},null,2)+`
2333
+ `)}finally{await o.close()}})}function sC(e,t){e.command("restart").argument("[services...]").option("--cascade","also restart transitive dependents (default: only the named services)").option("--json","machine-readable output").action(async(r,n)=>{let o=await t.openController("",xe(e),!0);try{if(await o.restart(r,n.cascade===!0),n.json)T(JSON.stringify({restarted:r,cascade:n.cascade===!0},null,2)+`
2334
+ `)}finally{await o.close()}})}async function tC(e,t,r){let{repoRoot:n,client:o}=await e.findDaemon("",r.repo??"",xe(t));try{if(!o){let s=e.readPersistedState(n);if(r.json){T(JSON.stringify({running:!1,persisted:s},null,2)+`
2335
+ `);return}if(T(`supervisor is not running
2336
+ `),s&&s.processes.length>0){T(`last session ${s.session_id} profile ${s.profile||"(none)"}
2337
+ `);for(let a of s.processes)T(`${a.name} stopped UNKNOWN ${a.pid}
2338
+ `)}return}e.assertMethodAllowed(o,"status");let i=await o.call("status",null);if(r.json){T(JSON.stringify(i,null,2)+`
2339
+ `);return}T(`PROFILE: ${i.profile||"(none)"}
2268
2340
 
2269
2341
  SERVICE STATUS HEALTH PID
2270
- `);for(let[s,a]of Object.entries(i.services))E(`${s} ${Jo(a)} ${a.health} ${a.pid}
2271
- `);E(`
2342
+ `);for(let[s,a]of Object.entries(i.services))T(`${s} ${Jo(a)} ${a.health} ${a.pid}
2343
+ `);T(`
2272
2344
  PROXY ${i.proxy.running?"RUNNING":"STOPPED"} ${i.proxy.address??""}
2273
- `),E(`MCP ${i.mcp?.running?"RUNNING":"STOPPED"} ${i.mcp?.address??""}
2274
- `),E(`IDENTITY ${i.identity.user||"(unknown)"}
2275
- `),E(`CLOUD ${i.identity.project||"(unset)"}
2276
- `)}finally{o?.close()}}var OO=2000;function tC(e,t){e.command("status").option("--repo <path>","target a repository directly, even without a loadable configuration").option("--json","machine-readable output").option("--watch","keep refreshing until interrupted").action(async(r)=>{if(!r.watch){await Yk(t,e,r);return}let n=new AbortController,o=()=>n.abort();process.on("SIGINT",o),process.on("SIGTERM",o);try{while(!n.signal.aborted){E(`--- ${new Date().toISOString()} ---
2277
- `),await Yk(t,e,r),E(`
2278
- `);try{await MO(OO,void 0,{signal:n.signal})}catch{break}}}finally{process.off("SIGINT",o),process.off("SIGTERM",o)}})}function rC(e,t){e.command("down").description("stop the daemon (and, by default, its services)").option("--repo <path>","target a repository directly, even without a loadable configuration").option("--keep-services","stop only the daemon; its services keep running, detached").action(async(r)=>{let{repoRoot:n,client:o}=await t.findDaemon("",r.repo??"",be(e));if(!o){E(`no supervisor is running for ${n}
2279
- `);return}let i=await FO(o);try{let s=r.keepServices!==!0;await o.call("shutdown",{stop_services:s},i)}finally{o.close()}await $O(t,n,i),E(r.keepServices!==!0?`stopped services and the supervisor for ${n}
2345
+ `),T(`MCP ${i.mcp?.running?"RUNNING":"STOPPED"} ${i.mcp?.address??""}
2346
+ `),T(`IDENTITY ${i.identity.user||"(unknown)"}
2347
+ `),T(`CLOUD ${i.identity.project||"(unset)"}
2348
+ `)}finally{o?.close()}}var BO=2000;function aC(e,t){e.command("status").option("--repo <path>","target a repository directly, even without a loadable configuration").option("--json","machine-readable output").option("--watch","keep refreshing until interrupted").action(async(r)=>{if(!r.watch){await tC(t,e,r);return}let n=new AbortController,o=()=>n.abort();process.on("SIGINT",o),process.on("SIGTERM",o);try{while(!n.signal.aborted){T(`--- ${new Date().toISOString()} ---
2349
+ `),await tC(t,e,r),T(`
2350
+ `);try{await jO(BO,void 0,{signal:n.signal})}catch{break}}}finally{process.off("SIGINT",o),process.off("SIGTERM",o)}})}function cC(e,t){e.command("down").description("stop the daemon (and, by default, its services)").option("--repo <path>","target a repository directly, even without a loadable configuration").option("--keep-services","stop only the daemon; its services keep running, detached").action(async(r)=>{let{repoRoot:n,client:o}=await t.findDaemon("",r.repo??"",xe(e));if(!o){T(`no supervisor is running for ${n}
2351
+ `);return}let i=await WO(o);try{let s=r.keepServices!==!0;await o.call("shutdown",{stop_services:s},i)}finally{o.close()}await UO(t,n,i),T(r.keepServices!==!0?`stopped services and the supervisor for ${n}
2280
2352
  `:`stopped the supervisor for ${n}; its services keep running
2281
- `)})}async function $O(e,t,r){let n=Date.now()+r;while(Date.now()<n){let o=await e.tryDial(t);if(!o)return;o.close()}}async function FO(e){try{let r=await e.call("config_snapshot",null),n=typeof r.shutdown?.grace_seconds==="number"?r.shutdown.grace_seconds:0;return Math.max(5000,n*1000+2000)}catch{return 30000}}function nC(e,t){e.command("attach").action(async()=>{let r=await t.openAttach("",be(e)),{runTuiWithController:n}=await Promise.resolve().then(() => (Hf(),Wk));await n(t,r)})}import{setTimeout as oC}from"timers/promises";function Bf(e){return`${e.timestamp.slice(11,19)} ${e.service.padEnd(10)} ${String(e.level).padEnd(6)} ${e.message}
2282
- `}async function iC(e,t,r,n=1000){let o=await e(void 0);o.events.forEach(t);let i=o.nextCursor;while(!r.aborted){try{await oC(n,void 0,{signal:r})}catch{return}o=await e(i),o.events.forEach(t),i=o.nextCursor}}function sC(e,t){let r=e.command("logs").argument("[services...]").enablePositionalOptions();r.option("--level <level>","minimum level").option("--search <text>","substring or regex search").option("--regex","treat search as regular expression").option("--source <source>","filter by source").option("--since <timestamp>","only events at or after this ISO timestamp").option("--until <timestamp>","only events at or before this ISO timestamp").option("--output <path>","export path").option("--json","machine-readable output").option("-f, --follow","keep printing new matching events until interrupted").option("--all","print the full matching history instead of the latest page").action(async(n,o)=>{let i=await t.openController("",be(e),!0);try{let s=o.output?t.resolveExportPath(o.output):void 0;if(o.follow&&!s){let c=new AbortController,l=()=>c.abort();process.on("SIGINT",l),process.on("SIGTERM",l);try{await iC((d)=>i.logsPage({services:n,level:o.level,search:o.search,regex:o.regex,source:o.source,since:o.since,until:o.until,cursor:d,direction:"forward"}),(d)=>E(o.json?JSON.stringify(d)+`
2283
- `:Bf(d)),c.signal)}finally{process.off("SIGINT",l),process.off("SIGTERM",l)}return}if(s||o.all===!0){let c=await i.logs({services:n,level:o.level,search:o.search,regex:o.regex,source:o.source,since:o.since,until:o.until,export:s});if(s){E(`exported ${s}
2284
- `);return}if(o.json){E(JSON.stringify(c,null,2)+`
2285
- `);return}for(let l of c)E(Bf(l));return}let a=await i.logsPage({services:n,level:o.level,search:o.search,regex:o.regex,source:o.source,since:o.since,until:o.until,direction:"backward"});if(o.json){E(JSON.stringify(a.events,null,2)+`
2286
- `);return}for(let c of a.events)E(Bf(c));if(a.hasPrev)E(`\u2026 older matching events omitted; pass --all for the full history
2287
- `)}finally{await i.close()}}),r.command("export").argument("[services...]").requiredOption("--output <path>","export path").option("--level <level>","minimum level").option("--search <text>","substring or regex search").option("--regex","treat search as regular expression").option("--source <source>","filter by source").action(async(n,o)=>{let i=await t.openController("",be(e),!0);try{let s=t.resolveExportPath(o.output);await i.logs({services:n,level:o.level,search:o.search,regex:o.regex,source:o.source,export:s}),E(`exported ${s}
2288
- `)}finally{await i.close()}})}function aC(e,t){e.command("daemon").command("logs").option("-f, --follow","keep printing new lines until interrupted").action(async(r)=>{let{repoRoot:n}=t.discover("",be(e)),o=t.bootstrapLogPath(n),i=0,s=()=>{if(!t.fileExists(o))return;let l=t.readTextFile(o);if(l.length>i)E(l.slice(i)),i=l.length};if(!t.fileExists(o))E(`no daemon bootstrap log yet for this repository
2289
- `);else s();if(!r.follow)return;let a=new AbortController,c=()=>a.abort();process.on("SIGINT",c),process.on("SIGTERM",c);try{while(!a.signal.aborted){try{await oC(500,void 0,{signal:a.signal})}catch{break}s()}}finally{process.off("SIGINT",c),process.off("SIGTERM",c)}})}vl();function cC(e,t){let r=e.command("proxy");r.command("status").option("--json").action(async(n)=>{let o=await t.openController("",be(e),!1);try{if(!o.client){E(`PROXY STOPPED
2290
- `);return}let i=await o.status();if(n.json){E(JSON.stringify(i.proxy,null,2)+`
2291
- `);return}E(`PROXY ${i.proxy.running?"RUNNING":"STOPPED"} ${i.proxy.address??""}
2292
- `);for(let s of i.proxy.routes??[]){let a=s.match?` match=${s.match}`:"",c=s.client_id?` client_id=${s.client_id}`:"";E(` ${s.name.padEnd(16)} identity=${s.identity}${a}${c} ${s.upstream}
2293
- `)}}finally{await o.close()}}),r.command("start").action(async()=>{let n=await t.openController("",be(e),!0);try{await n.proxyStart()}finally{await n.close()}}),r.command("stop").action(async()=>{let n=await t.openController("",be(e),!0);try{await n.proxyStop()}finally{await n.close()}})}function lC(e,t){e.command("mcp").description("Local MCP server for coding agents").option("--on","start the MCP listener").option("--off","stop the MCP listener").option("--port <port>","listen port").option("--json","machine-readable output").action(async(r)=>{let n=r.port===void 0?void 0:Number(r.port);if(r.port!==void 0&&(!Number.isInteger(n)||(n??0)<=0))throw Error(`invalid --port ${r.port}`);let o=await t.openController("",be(e),r.on===!0,{allowMissingConfig:!0});try{if(r.off===!0&&o.client)await o.mcpStop();else if(r.on===!0)await o.mcpStart({port:n});let i=o.client?await o.status():void 0,s=t.loadTuiConfig(o.cfg.repoRoot,o.cfg.ui.keymap),a=i?.mcp?.port??n??s.mcp_port??mo(o.cfg.repoRoot),c=i?.mcp?.address??fi(a),l=i?.mcp?.token??"";if(r.json){E(JSON.stringify({running:i?.mcp?.running===!0,setup_mode:i?.setup_mode===!0,url:c,port:a,snippets:{claude:JSON.parse(Vp(c,l)),cursor:JSON.parse(zp(c,l)),kilo:JSON.parse(Yp(c,l)),codex:Jp(c,l)}},null,2)+`
2294
- `);return}if(E(`MCP ${i?.mcp?.running?"RUNNING":"STOPPED"} ${c}
2295
-
2296
- `),i?.setup_mode===!0)E(`This repository has no .devctl yet, so the daemon is in setup mode.
2297
- `),E(`Connect an agent with the config below and ask it to set devctl up for this repository.
2298
- `),E(`It can call get_setup_guide, search_docs, and validate_config; nothing will run until a configuration exists.
2299
-
2300
- `);E(iw(c,l))}finally{await o.close()}})}te();function dC(e,t){e.command("update").description("check GitHub Releases and install when the install method is known").option("--json","machine-readable output").option("--check","report only; do not install").action(async(r)=>{let n=await t.checkUpdate();if(r.json){E(JSON.stringify(n,null,2)+`
2301
- `);return}if(E(`current ${n.current}
2302
- `),E(`latest ${n.latest||"(unavailable)"}
2303
- `),E(`channel ${n.kind}
2304
- `),!n.newer){if(n.latest!=="")E(`up to date
2305
- `);return}if(E(`install ${n.hint}
2306
- `),r.check||!n.command)return;if(E(`installing via ${n.kind}\u2026
2307
- `),(await t.applyUpdate(n.command,!0)).code!==0){process.exitCode=sh;return}E(`updated to ${n.latest}; ${pc}
2308
- `)})}function HO(e,t){let r=new NO;return r.name("devctl").description("Local development orchestrator").version(er(),"-V, --version","print version").option("--config <path>","path to config file or .devctl directory").enablePositionalOptions(),r.command("version").description("print version").action(()=>{E(`${er()}
2309
- `)}),r.action(async()=>{let n=r.opts();await Ff(e,n.config??"")}),Qk(r,e),Zk(r,e),eC(r,e),Xk(r,e),Jk(r,e),tC(r,e),rC(r,e),sC(r,e),aC(r,e),jO(r,e),BO(r,e),qk(r,e),cC(r,e),lC(r,e),zk(r,e),Vk(r,e),nC(r,e),UO(r,e),dC(r,e),WO(r,t),r}function jO(e,t){e.command("doctor").option("--json","machine-readable output").action(async(r)=>{let n=t.load("",be(e)),o=await t.runDoctor.execute(n);if(r.json)E(JSON.stringify(o,null,2)+`
2310
- `);else E(t.formatDoctor(o));if(o.issues>0)process.exitCode=2})}function BO(e,t){e.command("setup").option("--force","overwrite an existing configuration").action(async(r)=>{await t.runSetup("",be(e),r.force===!0)})}function UO(e,t){e.command("completion").argument("[shell]","zsh, bash, or fish").description("print a shell completion script").action((r)=>{E(Kk(r||"zsh"))}),e.command("__complete",{hidden:!0}).argument("[line...]").description("internal completion helper").action((r)=>{let n=r.join(" "),o=n===""?"devctl ":n;try{let i=t.load("",be(e));E(jf(o,i).join(`
2353
+ `)})}async function UO(e,t,r){let n=Date.now()+r;while(Date.now()<n){let o=await e.tryDial(t);if(!o)return;o.close()}}async function WO(e){try{let r=await e.call("config_snapshot",null),n=typeof r.shutdown?.grace_seconds==="number"?r.shutdown.grace_seconds:0;return Math.max(5000,n*1000+2000)}catch{return 30000}}function lC(e,t){e.command("attach").action(async()=>{let r=await t.openAttach("",xe(e)),{runTuiWithController:n}=await Promise.resolve().then(() => (Bf(),Yk));await n(t,r)})}import{setTimeout as dC}from"timers/promises";function Wf(e){return`${e.timestamp.slice(11,19)} ${e.service.padEnd(10)} ${String(e.level).padEnd(6)} ${e.message}
2354
+ `}async function uC(e,t,r,n=1000){let o=await e(void 0);o.events.forEach(t);let i=o.nextCursor;while(!r.aborted){try{await dC(n,void 0,{signal:r})}catch{return}o=await e(i),o.events.forEach(t),i=o.nextCursor}}function pC(e,t){let r=e.command("logs").argument("[services...]").enablePositionalOptions();r.option("--level <level>","minimum level").option("--search <text>","substring or regex search").option("--regex","treat search as regular expression").option("--source <source>","filter by source").option("--since <timestamp>","only events at or after this ISO timestamp").option("--until <timestamp>","only events at or before this ISO timestamp").option("--output <path>","export path").option("--json","machine-readable output").option("-f, --follow","keep printing new matching events until interrupted").option("--all","print the full matching history instead of the latest page").action(async(n,o)=>{let i=await t.openController("",xe(e),!0);try{let s=o.output?t.resolveExportPath(o.output):void 0;if(o.follow&&!s){let c=new AbortController,l=()=>c.abort();process.on("SIGINT",l),process.on("SIGTERM",l);try{await uC((d)=>i.logsPage({services:n,level:o.level,search:o.search,regex:o.regex,source:o.source,since:o.since,until:o.until,cursor:d,direction:"forward"}),(d)=>T(o.json?JSON.stringify(d)+`
2355
+ `:Wf(d)),c.signal)}finally{process.off("SIGINT",l),process.off("SIGTERM",l)}return}if(s||o.all===!0){let c=await i.logs({services:n,level:o.level,search:o.search,regex:o.regex,source:o.source,since:o.since,until:o.until,export:s});if(s){T(`exported ${s}
2356
+ `);return}if(o.json){T(JSON.stringify(c,null,2)+`
2357
+ `);return}for(let l of c)T(Wf(l));return}let a=await i.logsPage({services:n,level:o.level,search:o.search,regex:o.regex,source:o.source,since:o.since,until:o.until,direction:"backward"});if(o.json){T(JSON.stringify(a.events,null,2)+`
2358
+ `);return}for(let c of a.events)T(Wf(c));if(a.hasPrev)T(`\u2026 older matching events omitted; pass --all for the full history
2359
+ `)}finally{await i.close()}}),r.command("export").argument("[services...]").requiredOption("--output <path>","export path").option("--level <level>","minimum level").option("--search <text>","substring or regex search").option("--regex","treat search as regular expression").option("--source <source>","filter by source").action(async(n,o)=>{let i=await t.openController("",xe(e),!0);try{let s=t.resolveExportPath(o.output);await i.logs({services:n,level:o.level,search:o.search,regex:o.regex,source:o.source,export:s}),T(`exported ${s}
2360
+ `)}finally{await i.close()}})}function fC(e,t){e.command("daemon").command("logs").option("-f, --follow","keep printing new lines until interrupted").action(async(r)=>{let{repoRoot:n}=t.discover("",xe(e)),o=t.bootstrapLogPath(n),i=0,s=()=>{if(!t.fileExists(o))return;let l=t.readTextFile(o);if(l.length>i)T(l.slice(i)),i=l.length};if(!t.fileExists(o))T(`no daemon bootstrap log yet for this repository
2361
+ `);else s();if(!r.follow)return;let a=new AbortController,c=()=>a.abort();process.on("SIGINT",c),process.on("SIGTERM",c);try{while(!a.signal.aborted){try{await dC(500,void 0,{signal:a.signal})}catch{break}s()}}finally{process.off("SIGINT",c),process.off("SIGTERM",c)}})}yl();function mC(e,t){let r=e.command("proxy");r.command("status").option("--json").action(async(n)=>{let o=await t.openController("",xe(e),!1);try{if(!o.client){T(`PROXY STOPPED
2362
+ `);return}let i=await o.status();if(n.json){T(JSON.stringify(i.proxy,null,2)+`
2363
+ `);return}T(`PROXY ${i.proxy.running?"RUNNING":"STOPPED"} ${i.proxy.address??""}
2364
+ `);for(let s of i.proxy.routes??[]){let a=s.match?` match=${s.match}`:"",c=s.client_id?` client_id=${s.client_id}`:"";T(` ${s.name.padEnd(16)} identity=${s.identity}${a}${c} ${s.upstream}
2365
+ `)}}finally{await o.close()}}),r.command("start").action(async()=>{let n=await t.openController("",xe(e),!0);try{await n.proxyStart()}finally{await n.close()}}),r.command("stop").action(async()=>{let n=await t.openController("",xe(e),!0);try{await n.proxyStop()}finally{await n.close()}})}function hC(e,t){e.command("mcp").description("Local MCP server for coding agents").option("--on","start the MCP listener").option("--off","stop the MCP listener").option("--port <port>","listen port").option("--json","machine-readable output").action(async(r)=>{let n=r.port===void 0?void 0:Number(r.port);if(r.port!==void 0&&(!Number.isInteger(n)||(n??0)<=0))throw Error(`invalid --port ${r.port}`);let o=await t.openController("",xe(e),r.on===!0,{allowMissingConfig:!0});try{if(r.off===!0&&o.client)await o.mcpStop();else if(r.on===!0)await o.mcpStart({port:n});let i=o.client?await o.status():void 0,s=t.loadTuiConfig(o.cfg.repoRoot,o.cfg.ui.keymap),a=i?.mcp?.port??n??s.mcp_port??fo(o.cfg.repoRoot),c=i?.mcp?.address??mi(a),l=i?.mcp?.token??"";if(r.json){T(JSON.stringify({running:i?.mcp?.running===!0,setup_mode:i?.setup_mode===!0,url:c,port:a,snippets:{claude:JSON.parse(Yp(c,l)),cursor:JSON.parse(Jp(c,l)),kilo:JSON.parse(Xp(c,l)),codex:Qp(c,l)}},null,2)+`
2366
+ `);return}if(T(`MCP ${i?.mcp?.running?"RUNNING":"STOPPED"} ${c}
2367
+
2368
+ `),i?.setup_mode===!0)T(`This repository has no .devctl yet, so the daemon is in setup mode.
2369
+ `),T(`Connect an agent with the config below and ask it to set devctl up for this repository.
2370
+ `),T(`It can call get_setup_guide, search_docs, and validate_config; nothing will run until a configuration exists.
2371
+
2372
+ `);T(uw(c,l))}finally{await o.close()}})}te();function gC(e,t){e.command("update").description("check GitHub Releases and install when the install method is known").option("--json","machine-readable output").option("--check","report only; do not install").action(async(r)=>{let n=await t.checkUpdate();if(r.json){T(JSON.stringify(n,null,2)+`
2373
+ `);return}if(T(`current ${n.current}
2374
+ `),T(`latest ${n.latest||"(unavailable)"}
2375
+ `),T(`channel ${n.kind}
2376
+ `),!n.newer){if(n.latest!=="")T(`up to date
2377
+ `);return}if(T(`install ${n.hint}
2378
+ `),r.check||!n.command)return;if(T(`installing via ${n.kind}\u2026
2379
+ `),(await t.applyUpdate(n.command,!0)).code!==0){process.exitCode=lh;return}T(`updated to ${n.latest}; ${mc}
2380
+ `)})}function KO(e,t){let r=new GO;return r.name("devctl").description("Local development orchestrator").version(er(),"-V, --version","print version").option("--config <path>","path to config file or .devctl directory").enablePositionalOptions(),r.command("version").description("print version").action(()=>{T(`${er()}
2381
+ `)}),r.action(async()=>{let n=r.opts();await Hf(e,n.config??"")}),oC(r,e),iC(r,e),sC(r,e),nC(r,e),rC(r,e),aC(r,e),cC(r,e),pC(r,e),fC(r,e),qO(r,e),VO(r,e),Qk(r,e),mC(r,e),hC(r,e),eC(r,e),Zk(r,e),lC(r,e),zO(r,e),gC(r,e),YO(r,t),r}function qO(e,t){e.command("doctor").option("--json","machine-readable output").action(async(r)=>{let n=t.load("",xe(e)),o=await t.runDoctor.execute(n);if(r.json)T(JSON.stringify(o,null,2)+`
2382
+ `);else T(t.formatDoctor(o));if(o.issues>0)process.exitCode=2})}function VO(e,t){e.command("setup").option("--force","overwrite an existing configuration").action(async(r)=>{await t.runSetup("",xe(e),r.force===!0)})}function zO(e,t){e.command("completion").argument("[shell]","zsh, bash, or fish").description("print a shell completion script").action((r)=>{T(Xk(r||"zsh"))}),e.command("__complete",{hidden:!0}).argument("[line...]").description("internal completion helper").action((r)=>{let n=r.join(" "),o=n===""?"devctl ":n;try{let i=t.load("",xe(e));T(Uf(o,i).join(`
2311
2383
  `)+`
2312
- `)}catch{E(jf(o,Jn()).join(`
2384
+ `)}catch{T(Uf(o,Yn()).join(`
2313
2385
  `)+`
2314
- `)}})}function WO(e,t){e.command("_supervisor").option("--repo <path>","repository root").action(async(r)=>{await t(r.repo??"",be(e))})}async function uC(e,t){process.stdout.on("error",(r)=>{if(r.code==="EPIPE")process.exit(0)});try{await HO(e,t).parseAsync(process.argv)}catch(r){process.stderr.write(A(r)+`
2315
- `),process.exit(ch(r))}}zs();await uC(Iv(),nx);
2386
+ `)}})}function YO(e,t){e.command("_supervisor").option("--repo <path>","repository root").action(async(r)=>{await t(r.repo??"",xe(e))})}async function vC(e,t){process.stdout.on("error",(r)=>{if(r.code==="EPIPE")process.exit(0)});try{await KO(e,t).parseAsync(process.argv)}catch(r){process.stderr.write(L(r)+`
2387
+ `),process.exit(uh(r))}}Js();await vC(Fv(),lb);