@amr-m-abdelgawad/devctl 0.3.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/devctl.js +208 -136
- 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 QC=Object.defineProperty;var ZC=(e)=>e;function eP(e,t){this[e]=ZC.bind(null,t)}var tP=(e,t)=>{for(var r in t)QC(e,r,{get:t[r],enumerable:!0,configurable:!0,set:eP.bind(t,r)})};var I=(e,t)=>()=>(e&&(t=e(e=0)),t);var rP=import.meta.require;function xm(e,t=""){if(t==="MetadataLookupWarning")return!0;return e.includes("MetadataLookupWarning")||e.startsWith("received unexpected error =")}function Ys(){if(bm)return;bm=!0;let e=process.emitWarning.bind(process);process.emitWarning=(t,...r)=>{let n=oP(r[0]),o=nP(t);if(xm(o,n))return;e(t,...r)}}function wm(){let e=process.stderr.write.bind(process.stderr);return process.stderr.write=(t,r,n)=>{let o=iP(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 nP(e){if(typeof e==="string")return e;if(e instanceof Error)return e.message;return String(e)}function oP(e){if(typeof e==="string")return e;if(e&&typeof e==="object"&&"type"in e&&typeof e.type==="string")return e.type;return""}function iP(e){if(typeof e==="string")return e;if(e instanceof Uint8Array)return new TextDecoder().decode(e);return""}var bm=!1;import{createHash as sP}from"crypto";import{resolve as aP}from"path";function ar(e){let t=aP(e),r=process.platform==="win32"?t.toLowerCase():t;return sP("sha256").update(r).digest("hex").slice(0,cP)}var cP=16;var Js=()=>{};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 RP(e=process.platform){return ad(e)?"cmd":"ctrl"}function Ao(e,t=process.platform){return`${RP(t)}+${e}`}function EP(e=process.platform){return ad(e)?"command":"ctrl"}function It(e,t=process.platform){return`${EP(t)}+${e}`}function Ri(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 TP(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 Am(){return{theme:"devctl",leader_timeout:2000,font_size:14,keybinds:{...AP},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 LP(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 LP(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,Tm="DEVCTL_TUI_CONFIG",AP;var ea=I(()=>{AP=TP()});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 Im="ServiceStarted",aa="ServiceStopped",ca="ServiceFailed",Mm="ServiceStateChanged",Om="ServiceHealthChanged",Io="LogReceived",Mo="TokenRefreshed",Gn="TokenRefreshFailed",ld="AuthenticationChanged",$m="ProxyStarted",Fm="ProxyStopped",dd="ProxyRequest",la="ConfigurationChanged",Ei="ConfigurationReloadFailed",Nm="SessionRecovered";var Hm=5000,jm=500,pd=16384,Bm=65536,da="TRACE",ua="DEBUG",pa="INFO",Ti="WARN",Kn="ERROR",Oo="FATAL",fa="UNKNOWN",fd;var Ai=I(()=>{fd={["TRACE"]:0,["DEBUG"]:1,["INFO"]:2,["WARN"]:3,["ERROR"]:4,["FATAL"]:5,["UNKNOWN"]:2}});function Um(){return{name:"default",parse:(e)=>{let t=vd(e);if(t)return t;return{level:yd(e),request_id:bd(e)||void 0}}}}function Li(e,t){for(let r of t){let n=e[r];if(typeof n==="string"&&n.trim()!=="")return n}return}function Gm(e){for(let t of HP){let r=e[t];if(typeof r==="number"){let n=zP[r];if(n)return n;continue}if(typeof r==="string"&&r.trim()!=="")return r.toUpperCase()}return}function Di(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 YP(e){if(e>=BP)return Kn;if(e>=jP)return Ti;return}function JP(e){if(Di(e))return e;return{}}function Km(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 qm(e){if(!Di(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 XP(e){if(typeof e==="string"&&e.trim()!=="")return e;return qm(e)}function Vm(e){if(!Array.isArray(e)||e.length===0)return!1;return e.every((t)=>Di(t)&&typeof t.key==="string"&&t.value!==void 0)}function QP(e){let t={};if(!Vm(e))return t;for(let r of e){let n=r.key;if(typeof n==="string"&&n!==""){let o=qm(r.value);if(o!==void 0)t[n]=o}}return t}function ZP(e){let t=Gm(e);if(t)return t;let r=$o(e.severityNumber);if(r===void 0)return;if(r>=VP)return Oo;if(r>=qP)return Kn;if(r>=KP)return Ti;if(r>=GP)return pa;if(r>=WP)return ua;if(r>=UP)return da;return}function e_(e){if(e.timeUnixNano!==void 0||e.observedTimeUnixNano!==void 0)return!0;if(Di(e.body)&&(e.body.stringValue!==void 0||e.body.intValue!==void 0||e.body.doubleValue!==void 0))return!0;return Vm(e.attributes)}function t_(e,t){if(!e_(e))return;let r=QP(e.attributes),n=XP(e.body)??Km(r)??"log",o=Li(e,md)??Zt(r.trace_id??r.traceId);return{message:n,level:ZP(e),request_id:o,raw:t}}function r_(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,Km(JP(e.attributes))]),raw:t}}function n_(e){let t=e.trim().split(/\s+/);return{method:t[0],uri:t[1]}}function o_(e,t){if(Li(e,Wm)!==void 0)return;let r=Zt(e.request),n=r===void 0?{}:n_(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:YP(s),request_id:Li(e,md),raw:t}}function i_(e,t){let r=Li(e,Wm),n=Gm(e),o=Li(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>Bm)return;let r;try{r=JSON.parse(t)}catch{return}if(!Di(r))return;return t_(r,t)??r_(r,t)??o_(r,t)??i_(r,t)}function yd(e){return a_.find((r)=>r.re.test(e))?.level??fa}function bd(e){return s_.exec(e)?.[1]??""}var Wm,HP,md,jP=400,BP=500,UP=1,WP=5,GP=9,KP=13,qP=17,VP=21,zP,s_,a_;var zm=I(()=>{Ai();Wm=["message","msg","text","log","event"],HP=["level","severity","severityText","levelname","loglevel","lvl"],md=["request_id","requestId","trace_id","traceId","correlation_id","correlationId"],zP={10:da,20:ua,30:pa,40:Ti,50:Kn,60:Oo};s_=/(?:x-devctl-request-id|request[_-]?id)[=: ]+([A-Za-z0-9-]+)/i,a_=[{re:/\b(fatal|critical)\b/i,level:Oo},{re:/\b(error|err)\b/i,level:Kn},{re:/\b(warn|warning)\b/i,level:Ti},{re:/\b(debug|dbg)\b/i,level:ua},{re:/\b(trace)\b/i,level:da},{re:/\b(info|information)\b/i,level:pa}]});function Ii(e){let t=c_(e);if(t===void 0)return;return new RegExp(t)}function c_(e){if(e.length===0||e.length>200)return;let t={raw:e,i:0,out:[],quantifiers:0,depth:0,lastAtom:"none",groupQuant:[]};if(!Ym(t)||t.i!==e.length||t.depth!==0)return;return t.out.join("")}function Ym(e){while(e.i<e.raw.length){if(e.raw[e.i]===")")return!0;if(!l_(e))return!1}return!0}function l_(e){let t=e.raw[e.i]??"";if(t==="|")return ma(e,"|","none");if(t==="^")return ma(e,"^","none");if(t==="$")return ma(e,"$","none");if(t===".")return ma(e,".","atom");if(t==="(")return d_(e);if(t==="[")return p_(e);if(t==="*"||t==="+"||t==="?"||t==="{")return u_(e);if(t==="\\")return Jm(e);return e.out.push(RegExp.escape(t)),e.i+=1,e.lastAtom="atom",!0}function ma(e,t,r){return e.out.push(t),e.i+=1,e.lastAtom=r,!0}function d_(e){if(e.i+=1,e.depth+=1,e.depth>2)return!1;if(e.groupQuant.push(!1),e.out.push("(?:"),!Ym(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 u_(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 p_(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(!Jm(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 Jm(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=Ii(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 Xm=I(()=>{Ai()});function Qm(e){if(!Number.isInteger(e)||(e??0)<=0)return jm;return Math.min(e,Hm)}var Zm=I(()=>{Ai()});var ha=I(()=>{Ai();zm();Xm();Zm()});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 ya(e,t){return e instanceof Ve&&e.kind===t}function dh(e){if(e instanceof Ve)return e.exitCode();return 1}function uh(e){if(e instanceof Ve)return{error:L(e),kind:e.kind,hint:e.hint,service:e.service};return{error:L(e)}}function ph(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`${ah(e)} \u2014 ${e.hint}`;return ah(e)}if(e instanceof Error)return e.message;return String(e)}function ah(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 ch=1,Y="configuration",qn="configuration_missing",Ho="service_not_found",lh="dependency",He="process_start",hn="authentication",Vn="authorization",Fr="token",ga="impersonation",va="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 Ca(e){return e>0?e:300}function Dd(){return{enabled:!1,paths:[],debounce_ms:300,ignore:[...Oi]}}function Id(){return{enabled:void 0,host:"",port:""}}function $i(){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 _a(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:Pa,port:0},token_endpoint:{enabled:!1,host:"",port:0},routes:[]},logs:{max_memory_events:M_,persistence:{enabled:!0,directory:"~/.devctl/logs",retention_days:O_,max_session_logs:0}},auth:{refresh_threshold_seconds:gh},shutdown:{grace_seconds:hh},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 Ni(e){if(e.type!=="")return e.type;return e.mode}function en(e){let t=Ni(e).toLowerCase();return t==="service"||t==="service_account"}function vh(e){let t=Ni(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 bn(e){let t=e.host===""?Pa:e.host;if(e.port===0)return t;return`${t}:${e.port}`}function yh(e){if(e.refresh_threshold_seconds<=0)return gh;return e.refresh_threshold_seconds}function Ea(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 hh;return e.grace_seconds}function bh(e){if(!e.logs.stdout&&!e.logs.stderr)return!0;return e.logs.stdout}function xh(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 wh(e){return e[0]}var Uo=1,wa="never",Sa="on_failure",ka="always",Oi,Fi=8080,hh=10,gh=300,M_=50000,O_=14,Pa="127.0.0.1";var ht=I(()=>{Oi=["**/node_modules/**","**/.git/**"]});var Nt=".devctl",lr="config.yaml";import{basename as $_}from"path";function wn(e,t=""){return{name:$_(e),project:t,profile:"",sa:"",audience:"",proxyPort:Fi}}function Ta(e,t=Fi){let r=Number(e);return Number.isFinite(r)&&r>0?r:t}function Sh(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
|
|
51
|
-
`)}var
|
|
50
|
+
`}var wt;var Aa=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 F_,writeFileSync as N_}from"fs";import{createInterface as H_}from"readline/promises";import{stdin as j_,stdout as B_}from"process";import{basename as La,dirname as Nd,join as tn,resolve as U_}from"path";function jd(e,t){let r=e===""?process.cwd():e;if(t==="")return{repo:r,cfgPath:tn(r,Nt,lr)};let n=U_(t);if(!(Hd(n)?F_(n).isDirectory():!/\.ya?ml$/i.test(n))){let i=Nd(n);return{repo:La(i)===Nt?Nd(i):i,cfgPath:n}}if(La(n)===Nt)return{repo:Nd(n),cfgPath:tn(n,lr)};return{repo:n,cfgPath:tn(n,Nt,lr)}}function kh(e,t=La(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 Da(e,{...wn(e,r),name:t,profile:n}),i}async function Ch(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=H_({input:j_,output:B_}),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}`,La(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(Fi));d.proxyPort=Ta(m),d.profile=await a(`${l()}. ${wt[7]?.prompt}`,""),s.close(),Da(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 Da(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}),N_(tn(r,"config.yaml"),Sh(t))}function Ht(e){process.stdout.write(`${e}
|
|
51
|
+
`)}var Ia=I(()=>{ht();Aa();te()});function bg(){return/\$\{(?:env\.)?([A-Za-z_][A-Za-z0-9_]*)\}/g}function eu(e){return[...e.matchAll(bg())].map((t)=>t[1]??"")}function xg(e,t){let r=[];return{value:e.replace(bg(),(o,i)=>{let s=t[i];if(s===void 0||s==="")return r.push(i),"";return s}),missing:r}}function ja(e){let t=e.trim();if(t===""||eu(t).length===0)return;return t}function wg(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 Ka(e){if(en(e))return dr({kind:ro,serviceAccount:e.service_account});if(vh(e)&&(e.type!==""||e.mode!==""))return dr({kind:Ga});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:Ga})}function Ag(e){return e.kind===Ga||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 ou(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 ou(e).filter((t)=>t.active).map((t)=>t.email)}function Lg(e,t,r){let n=[];for(let o of t){let i=e.services[o];if(!i)continue;let s=Ka(i.identity);if(en(i.identity)&&i.identity.service_account===""){n.push({name:o,message:"service account identity is not configured"});continue}if((Ag(s)||i.capabilities.some((c)=>c==="google_api"||c==="iap"||c==="service_identity"))&&!r)n.push({name:o,message:"ADC unavailable"})}return n}function Wi(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(Ag(Ka(t.identity)))return!0;return t.capabilities.some((r)=>r==="google_api"||r==="iap"||r==="service_identity")})}function iu(){return{name:"user",accepts:(e)=>!en(e),resolve:async(e,t)=>{if(e.type===""&&e.mode==="")return dr({kind:Go});return{...await t(),kind:Ga}}}}function su(){return{name:"service_account",accepts:(e)=>en(e),resolve:async(e,t)=>{if(e.service_account==="")throw E(ga,"service account identity is not configured");return{...await t(),kind:ro,serviceAccount:e.service_account}}}}async function Dg(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 su().resolve(e,t);return iu().resolve(e,t)}var Ga="user",ro="service_account",Go="none";var jr=I(()=>{ht();te()});function er(){return"devctl 0.3.1"}var jt="0.3.1",zi=1;function Xg(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 Xi(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 ev(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(lh,"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 bu(e,t){if(!e.services[t])throw E(Ho,`unknown service "${t}"`)}function rc(e,t,r){let n={},o=(a)=>{if(n[a])return;bu(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=ev(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;bu(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 tv(e,t){let r=[...ev(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 nc(e,t){let r=Object.fromEntries(co(e,t).map((n)=>[n,!0]));return tv(e,r)}function oc(e,t){let r={};for(let n of t)bu(e,n),r[n]=!0;return tv(e,r)}function rv(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
|
|
60
|
-
`||e==="\r"}var
|
|
59
|
+
`}return t}function ic(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:Qg(e,r),env:n};if(t==="")return{services:Object.keys(e.services),env:n};return{services:Qg(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 sc(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 Qg(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 Zg="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 Tv(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 Av(e){let t=Rv(e.scriptPath),r=Rv(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 Lv(e,t){let r=Ev(e),n=Ev(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 Rv(e){return e.replaceAll("\\","/")}function Ev(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 fc="restart the daemon with `devctl down` then start again";function Rn(e){if(!Number.isInteger(e))return Xu;return Math.min(yA,Math.max(bA,e))}function Ey(e,t){if(t.length!==1||t<"0"||t>"9")return e;if(e.length>=xA)return t;return`${e}${t}`}function Ty(e){return e.slice(0,-1)}function Ay(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%Ry:0;return Xu+r}function Ly(e,t){return t===fo(e)}var Xu=18700,Ry=600,Yj,vA=8,yA=65535,bA=1024,xA=5;var Qu=I(()=>{Js();Yj=Xu+Ry-1});class mr{nameMarkers;patterns;constructor(e,t){this.nameMarkers=[...Hy,...e],this.patterns=t.flatMap((r)=>{try{return[new RegExp(r)]}catch{return[]}})}update(e,t){this.nameMarkers=[...Hy,...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 jy(e,n+22);let o=t.indexOf("bearer ");if(o<0)return e;return jy(e,o+7)}function jy(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 Hy;var ni=I(()=>{Hy=["PASSWORD","SECRET","TOKEN","PRIVATE_KEY","CLIENT_SECRET","API_KEY","CREDENTIAL","ACCESS_KEY","AUTH_KEY"]});var Tn;var ip=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
|
---
|
|
@@ -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
|
|
597
|
+
`}});var By;var Uy=I(()=>{By=[{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+
|
|
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+
|
|
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 Ky(){return[...By,...BA()]}function qy(e){let t=e.trim();if(t==="")throw Error("path is required");let r=Ky(),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+Gy).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+Gy).join(`
|
|
2210
|
+
`).trim();if(i.length<=Wy)return i;return`${i.slice(0,Wy).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 Vy(e,t){let r=e.trim(),n=UA(r);if(n.length===0)throw Error("query is required");let o=KA(t),i=Ky().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,Wy=400,Gy=5,HA=2,jA=4;var zy=I(()=>{ip();Uy()});import{existsSync as qA,readFileSync as VA}from"fs";function An(e,t){return!(t??[]).includes(e)}function Yy(e){return _c.filter((t)=>An(t.name,e))}function Rc(e){return _c.some((t)=>t.name===e)}function us(e){return new mr(e.secrets.extra_markers,e.secrets.extra_patterns)}function Jy(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=us(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 Xy(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 Qy(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=us(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 Cc(e){return e.trim()===""?void 0:e}function XA(e){let t={name:e.name,match:e.match,upstream:e.upstream.url,auth:e.auth.type},r=Cc(e.auth.identity.type),n=Cc(e.auth.audience),o=Cc(e.auth.identity.service_account||e.auth.service_account),i=Cc(e.auth.client_id),s=ja(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)=>XA(r))}}}function QA(e){let t=us(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 eb(e,t,r){switch(t){case"list_services":return Jy(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 Xy(e.status());case"get_logs":return Qy(e,r);case"list_profiles":return JA(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: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=us(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=us(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 Vy(typeof r.query==="string"?r.query:"",typeof r.limit==="number"?r.limit:void 0);case"get_doc":return qy(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 tb(e){return sp.includes(e)}async function rb(e,t){switch(t){case"devctl://status":return Xy(e.status());case"devctl://services":return Jy(e.status());case"devctl://logs":return Qy(e,{});case"devctl://config":return Zy(e.config());case"devctl://doctor":return e.doctor();default:throw Error(`unknown resource ${t}`)}}var zA=200,sp,ap,_c;var ps=I(()=>{ni();zy();ip();sp=["devctl://status","devctl://services","devctl://logs","devctl://config","devctl://doctor"],ap=["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 lb(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)=>oc(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 db=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 ub(e){let t=Tc(e);if(t==="system"){if(mL())return{...lp["solarized-light"],name:"system"};return lp.system}return lp[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 vs(e)>=gs?Ac:t}function vs(e){let t=e.startsWith("#")?e.slice(1):e,r=Number.parseInt(t,kL);if(Number.isNaN(r))return 0;let n=r>>CL&dp,o=r>>PL&dp,i=r&dp;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 vs(e.background)>=gs}function pp(e){if(!e||!/^#[0-9a-f]{6}$/i.test(e))return!1;return vs(e)<gs}function Dc(e,t){let r=RL[e];return Lc(t)?r.light:r.dark}function pb(e,t){return vs(Dc(e,t))>=gs?EL:TL}function Ze(e,t){if(e===""||e==="all")return t.muted;let r=vs(t.background)>=gs?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 fb(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,up,lp,fL,hL,gL,vL=2166136261,yL=16777619,bL=299,xL=587,wL=114,SL=1000,gs=160,kL=16,dp=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"],up={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)"},lp={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(()=>{ea()});function gb(e){return e!==void 0}function mb(e){let t=e/1000;if(Number.isInteger(t))return`${t}s`;return`${e}ms`}function hp(e,t,r){let n=e.indexOf(t),i=((n<0?0:n)+r+e.length)%e.length;return e[i]??e[0]}function vb(e,t){let r=[...St],n=r.includes(e)?e:mp;return hp(r,n,t)}function yb(e,t){let r=[...LL],n=r.reduce((o,i)=>Math.abs(i-e)<Math.abs(o-e)?i:o,r[0]);return hp(r,n,t)}function ys(e){let t=[...fp];return t.reduce((r,n)=>Math.abs(n-e)<Math.abs(r-e)?n:r,t[0])}function bs(e,t){return hp([...fp],ys(e),t)}function xs(e){let t=ys(e),r=hb[t]??hb[Lo];return{px:t,...r}}function ho(e){return e.label==="compact"}function ws(e){return e.steps<=2}function bb(e){return fp.map((r,n)=>n<e?"\u2588":"\u2591").join(" ")}function Ic(e){return xs(e).label}function xb(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:mb(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 ${mp}, display ${Ic(Lo)}, mouse on, leader ${mb(sd)}. Enter asks first. ${t}`}]}function Mc(){return{theme:mp,mouse:!0,leader_timeout:sd,font_size:Lo}}function wb(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 gp(e,t){if(e.length===0)return 0;return Math.min(Math.max(t,0),e.length-1)}function Ln(e,t){return e[gp(e,t)]}function vp(e,t){return e??t}var LL,fp,mp="devctl",hb;var Er=I(()=>{tt();Rr();LL=[1000,2000,3000],fp=[12,14,16,18,20,22];hb={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(yp)}var yp;var Mt=I(()=>{Er();Rr();yp=DL(xs(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 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 Ss(e){if(!Number.isFinite(e)||e<bp)return"< 1s";let t=Math.floor(e/Sb),r=Math.floor(e%Sb/wp),n=Math.floor(e%wp/xp),o=Math.floor(e%xp/bp);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 ks(e){if(!Number.isFinite(e)||e<0)return"\u2014";let t=e/ML;if(t>=kb)return`${(t/kb).toFixed(1)}G`;if(t>=1)return`${Math.round(t)}M`;return`${Math.round(e)}K`}function Cb(e){if(!Number.isFinite(e)||e<0)return"\u2014";return`${e.toFixed(1)}%`}var bp=1000,xp,wp,Sb,ML=1024,kb=1024;var Ot=I(()=>{xp=60*bp,wp=60*xp,Sb=24*wp});function kp(e){return e>=FL}function Cp(e){return e>=NL}function Pp(e){return e>=HL}function Nc(e,t=0){return Math.max(1,e-_b-t*2)}function Rb(e){let t=Pb+Tr+$t;if(kp(e))t+=Ur;if(Cp(e))t+=$c;if(Pp(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(Sp,Math.max(Fc,n+OL)),i=Pb+Tr+$t+o+_b+$L,s=Math.max(Oc,Math.floor(e*jL));return Math.min(s,Math.max(Oc,i))}function Eb(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 _p(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 Rs(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 Tb(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 Ab(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 Es(e){return UL.has(e)}function vo(e){if(!e)return!1;return Es(e.state)}var Pb=8,Tr=12,Ur=10,$c=8,sn=8,Cs=9,Ps=7,_s=8,Fc=12,Sp=40,$t=2,OL=2,_b=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===fa?"\u2014":e}function Rp(e,t,r){let n=r?Db:WL+Db+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 $b(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 Fb(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 Nb(e,t){return e==="all"||e==="focus"&&t}function Qc(e){if(e==="clip")return"focus";if(e==="focus")return"all";return"clip"}function Ep(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 Tp(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=Ii(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 Hb(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 jb(e){let t=e.showTimestamps?Vc:0,r=e.showMeta?qc:0;return Math.max(Ib,e.width-t-e.serviceWidth-zc-r-Mb-Ts*Ob)}function Bb(e,t){let r=t.reduce((s,a)=>Math.max(s,a.length),0),n=Vc+zc+Mb+Ib+Ts*Ob,o=Math.max(Lb,e-n),i=Math.max(Lb,r);return Math.min(Sp,o,i)}function si(e){return eD.has(e)}function Wb(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 Gb(e,t){let r=t.length>0?t[t.length-1].seq:-1,n=e.filter((o)=>o.seq>r);return[...t,...n]}function Kb(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 Ap(e,t,r){return e&&t<=0&&r}function qb(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=Ii(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 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 Vb(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 zb(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 Yb(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 Lp(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,Lb=10,Ib=16,Mb=2,Ts=1,Ar=200,WL=2,Db=1,GL="\u25B8",Ob=2,KL=0.4,qL,zL,eD,Ub;var gr=I(()=>{ha();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"]),Ub=["devctl","mcp","auth"]});function ai(e){return e.length+oD}function Jb(e,t){if(t<sD)return Re(e,3);if(t<iD)return Re(e,4);return e}function Xb(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=Dp(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=Dp(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 Dp(e,o,Math.max(e[o]??0,r-c+d))}function Dp(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 Qb(e){if(e==="detail")return bo.findIndex((t)=>t.id==="services");return bo.findIndex((t)=>t.id===e)}function Zb(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 ex(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 nx(e,t=!0){let r=e<nl?tx+1:tx,n=t?pD:0;return r+n+dD+n+uD+n}function li(e){return Math.max(cD,e-lD*2)}function ox(e){let t=e.width<aD,r=e.proxyOn?`\u25CF ${Re(e.proxyAddress,t?10:18)}`:"";return[{label:t?`${e.running}/${e.total}`:Yb(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 rx(e){return e.key==="/"&&e.label==="command"}function fD(e){return e.key.length+e.label.length+3}function ix(e,t){let r=e.filter(rx),n=e.filter((s)=>!rx(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(nx(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 sx(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 ax(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 cx(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 lx(e){let t=e.payload?.error;return typeof t==="string"&&t!==""?t:"configuration reload failed"}var ci=100,nl=90,aD=60,cD=8,lD=6,tx=1,dD=1,uD=1,pD=1,oW,mD,hD;var vr=I(()=>{Ot();gr();In();oW=nx(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 mx(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 ux;if(t.length<fx)return 0;return r.some((n)=>n.includes(t)||n.replace(/^-+/,"").startsWith(t))?px: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<fx)return 0;if(e.includes(r))return ux;return t.some((n)=>n.includes(r))?px:0}function OD(e,t,r){if(r.length<PD)return 0;let n=dx(e,r);if(n>0)return wD+Math.min(n,TD);return t.some((o)=>dx(o,r)>0)?SD:0}function dx(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 Ip(e){return e.trim().replace(/^\//,"").split(/\s+/).slice(1).filter((r)=>r!=="")}function hx(e){return il().find((r)=>r.leader===e)?.name??""}function gx(e){let t=e.includes("--cascade")||e.includes("-c");return{services:e.filter((n)=>n!=="--cascade"&&n!=="-c"),cascade:t}}function vx(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,ux=50,px=45,wD=30,SD=28,kD=20,CD=10,fx=2,PD=3,_D=4,RD=4,ED=4,TD=9;function wx(e){return e==="detail"?"services":e}function Sx(e){let t=Dn.indexOf(wx(e));if(t<0)return"dashboard";return Dn[(t+1)%Dn.length]??"dashboard"}function kx(e){let t=Dn.indexOf(wx(e));if(t<0)return"dashboard";return Dn[(t-1+Dn.length)%Dn.length]??"dashboard"}function Op(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 $p(e){return e.name}function Cx(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 Px(e){let t=e.reduce((n,o)=>Math.max(n,$p(o).length),0),r=Math.min(Mp,Math.max(t,1));return FD+r+ND}function _x(e){return e.hint?`${e.name} ${e.hint} `:`${e.name} `}function Rx(e,t){if(e.hint)return e.hint.split(/\s+/);return Wr(t)?.name===e.name?Ip(t):[]}function yx(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 bx(e,t){return{...e,hint:t.token,desc:t.desc,usage:void 0,suggest:void 0}}function jD(e,t,r={}){let n=Ip(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(yx(d.token,l))s.push(bx(a,d));if(!c||!o)continue;for(let d of HD(a,r))if(yx(d.token,n))s.push(bx(a,d))}return s}function Ex(e,t={}){let r=mx(e),n=xo(e)===""?Op(r).flatMap((o)=>o.items):r;return jD(n,e,t)}function Fp(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 Tx(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,...Np()];if(t==="config-edit")return[{key:It("s"),label:"save"},{key:"esc",label:"discard"}];return BD(e,r)}function Np(){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: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 Ax(e,t,r){if(r<=t||e<t)return 0;return Math.min(e-t+1,r-t)}function xx(e,t,r){let n=e.slice(t,r+1),o=new Set(n.map((i)=>i.group));return n.length+o.size}function Lx(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&&xx(e,o,i+1)<=r,a=o>0&&xx(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,FD=3,ND=2,Mp=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 Dx(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(Es(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 Ix(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(Ix(e,t),UD,WD)}function Up(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 As[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(As.length-1,Math.round(i*(As.length-1)));return As[s]??As[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 Hp(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 Mx(e,t=Date.now()){if(!e?.startTime)return"\u2014";let r=new Date(e.startTime).getTime();if(!Number.isFinite(r))return"\u2014";return Ss(t-r)}function Ox(e,t=qD){return Object.entries(e).sort((r,n)=>n[1]-r[1]||r[0].localeCompare(n[0])).slice(0,t)}function $x(e,t=1){return Math.max(24,e-nI-t*2-oI-iI-sI)}function Fx(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+=Ps+$t;if(n)a+=_s+$t;if(o)a+=Cs+$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 Nx(e){let t=Number.isFinite(e)&&e>0?e:0;return{ratio:Math.min(1,t),label:JD(t)}}function Hx(e,t=lI){return`[${ii(e.ratio,t)}] ${e.label.padStart(4)}`}function Wp(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=ui*(o>0?3:2),s=Math.min(aI,Math.max(jp,r)),a=Math.min(cI,Math.max(Bp,n)),c=t-s-a-o-i;if(c<al&&a>Bp)a=Math.max(Bp,a-(al-c)),c=t-s-a-o-i;if(c<al&&s>jp)s=Math.max(jp,s-(al-c)),c=t-s-a-o-i;return{what:s,reading:a,meaning:Math.max(0,c),meter:o}}function jx(e){return uI.has((e?.health.type??"").toLowerCase())}function Bx(e){if(e==="darwin")return"macOS";if(e==="win32")return"Windows";if(e==="linux")return"Linux";return e||"this computer"}function Ux(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 ${Hp(e)}`:n==="warning"?`${r} cores nearly full \u2014 last minute ${Hp(e)}`:`${r} cores. Last minute ${Hp(e)} (under ${r} is fine)`;return{what:"CPU work",reading:o,meaning:i,tone:n,meter:Nx(Ix(e,r))}}function Wx(e,t){let r=YD(e,t),n=`${ks(e)} of ${ks(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:Nx(i)}}function Gx(e){let t=e?.state??"STOPPED";if(t==="FAILED")return"crashed";if(t==="STOPPING")return"stopping";if(t==="STARTING"||t==="RESTARTING")return"starting";if(Es(t))return"up";return"off"}function Kx(e){if(!e||!Es(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 qx(e){if(e==="keychain")return"password store";if(e==="file")return"local file";return e||"unknown store"}function Vx(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,As="\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,jp=8,aI=22,Bp=6,cI=28,al=12,ui=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 zx(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)=>ai(u.label)),c=Xb(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(zx,{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(zx,{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:Gp,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=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=ws(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:Gp,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:Gp,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 Gp="rounded";var ke=I(()=>{Mt();vr();In();Er();tt()});import{jsxDEV as $e,Fragment as vI}from"@opentui/react/jsx-dev-runtime";function Jx(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=ox({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=ws(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 Xx(e){let{palette:t,screen:r,width:n,onSelect:o}=e,i=n>=Yx+gI,s=i?Math.max(1,n-Yx):n,a=bo.map((l)=>({id:l.id,label:Jb(l.label,s)})),c=Qb(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 Qx(e){let{palette:t,screen:r,overlay:n,status:o,paused:i,errorOnly:s,width:a,copyKey:c}=e,l=ws(ye()),d=Math.max(18,Math.floor(a*0.42)),u=ix(Tx(r,n,c),d),f=sx(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 Zx(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 Yx,gI=8;var Yt=I(()=>{Mt();vr();wo();Ot();In();dl();ke();Er();Yx=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 ew(){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?`${ew()} (${r.message})`:ew())}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 Kp=I(()=>{te()});function qp(e){return e==="slash"||e==="config-edit"||e==="setup-wizard"}function tw(e,t){return mn(e,t.keybinds.leader??Ao("x"))}function rw(e,t){return mn(e,t.keybinds.command_list??Ao("p"))}function an(e,t){return mn(e,t.keybinds.command??"/")||e.name==="/"}function nw(e,t){return mn(e,t.keybinds.help??"?")||e.sequence==="?"}function ow(e,t){return mn(e,t.keybinds.search??"f")}function iw(e){return(e.name??"").toLowerCase()==="q"&&!e.ctrl&&!e.meta&&!e.super&&!e.alt&&!e.option}function sw(e){let t=e.name??"";return t==="R"||e.shift===!0&&t.toLowerCase()==="r"}function aw(e){return Or(e)&&(e.name??"").toLowerCase()==="l"}function cw(e,t){return mn(e,t.keybinds.copy??Zr())}function lw(e,t,r=kI){return t>0&&e-t<=r}function Mn(e,t,r,n){return mn(e,t.keybinds[r]??n)}function Ls(e){let t=(e.name??"").toLowerCase();return t==="pagedown"||Or(e)&&t==="d"}function Ds(e){let t=(e.name??"").toLowerCase();return t==="pageup"||Or(e)&&t==="u"}var kI=2000;var hl=I(()=>{Rr()});function fi(e){return`http://127.0.0.1:${e}/mcp`}function Vp(e){if(e==="")return{};return{Authorization:`Bearer ${e}`}}function zp(e,t){return JSON.stringify({mcpServers:{devctl:{type:"http",url:e,headers:Vp(t)}}},null,2)}function Yp(e,t){return JSON.stringify({mcpServers:{devctl:{url:e,headers:Vp(t)}}},null,2)}function Jp(e,t){return JSON.stringify({mcp:{devctl:{type:"remote",url:e,headers:Vp(t),enabled:!0}}},null,2)}function Xp(e,t){let r=t===""?"":`
|
|
2142
2214
|
http_headers = { Authorization = "Bearer ${t}" }`;return`[mcp_servers.devctl]
|
|
2143
2215
|
url = "${e}"${r}
|
|
2144
|
-
`}function
|
|
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 Is(e,t){return[{kind:"claude",title:"Claude",path:".mcp.json",language:"json",text:zp(e,t),hint:"claude mcp add-json"},{kind:"cursor",title:"Cursor",path:".cursor/mcp.json",language:"json",text:Yp(e,t)},{kind:"kilo",title:"Kilo Code",path:"kilo.jsonc",language:"json",text:Jp(e,t)},{kind:"codex",title:"Codex",path:"~/.codex/config.toml",language:"toml",text:Xp(e,t),hint:CI(e)}]}function dw(e,t){let r=[`URL ${e}`,""];for(let n of Is(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 tf(){return ap.flatMap((e)=>_c.filter((t)=>t.category===e))}function vl(){return gl+tf().length}function fw(){return vl()+pw}function Ms(e){if(e<gl||e>=vl())return;return tf()[e-gl]}function rf(e){let t=e-vl();return t>=0&&t<pw?t:void 0}function mw(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=Is(p,m),v=r?.mcp?.disabled_tools??[],b=tf(),g=b.filter((S)=>!An(S.name,v)).length,_=h[rf(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(Qp,{palette:t,text:"Server"},void 0,!1,void 0,this),q(uw,{palette:t,active:i===Zp,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(Zp),onActivate:a},void 0,!1,void 0,this),q(uw,{palette:t,active:i===ef,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(ef)},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(Qp,{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),ap.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(Qp,{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===Zp)return[{key:"space",label:t?"stop server":"start server"},{key:"j/k",label:"move"}];if(e===ef)return[{key:"\u2190\u2192",label:"change port"},{key:"0-9",label:"type port"},{key:"enter",label:"apply port"},{key:"j/k",label:"move"}];if(Ms(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 Qp(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 uw(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:nf,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=pb(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:nf,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:nf+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 Zp=0,ef=1,gl=2,pw=4,PI,nf=14,_I=14;var Os=I(()=>{Mt();ke();tt();ps();PI={inspect:"Inspect",logs:"Logs",diagnostics:"Diagnostics",control:"Control",setup:"Setup"}});import{jsxDEV as Fe,Fragment as gw}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:Ri(e),label:"copy selection"},{key:"/export",label:"write ~/.devctl/exports"},{key:"/exports",label:"open export folder"}]}function $n(e){return e+II}function vw(e){let t=$n(sf.length),r=$n(af.length),n=$n(e.logCount),o=$n(cf.length),i=$n(lf.length),s=df+e.pad*2;if(e.stacked)return s+t+r+n+o+i+of+($I-1)*e.gap;let a=Math.max(t,r),c=Math.max(o,i);return s+a+n+c+of+(FI-1)*e.gap}function HI(e){return vw(e)-df-e.pad*2}function jI(e){return e-df*2<DI}function yw(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=vw(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(gw,{children:[Fe(On,{palette:t,title:"navigation",bindings:sf},void 0,!1,void 0,this),Fe(On,{palette:t,title:"services",bindings:af},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:cf},void 0,!1,void 0,this),Fe(On,{palette:t,title:"display",bindings:lf},void 0,!1,void 0,this)]},void 0,!0,void 0,this):Fe(gw,{children:[Fe(hw,{palette:t,left:{title:"navigation",bindings:sf},right:{title:"services",bindings:af}},void 0,!1,void 0,this),Fe(On,{palette:t,title:"logs",bindings:d},void 0,!1,void 0,this),Fe(hw,{palette:t,left:{title:"commands",bindings:cf},right:{title:"display",bindings:lf}},void 0,!1,void 0,this)]},void 0,!0,void 0,this),Fe("box",{height:of,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 hw(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,df=2,of=1,uf=8,MI=2,OI=2,$I=6,FI=4,sf,af,cf,lf;var pf=I(()=>{Mt();ke();Er();Rr();sf=[{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"}],af=[{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)"}];cf=[{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"}],lf=[{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 bw(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 qp(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(Ls(t))return rt(j,li(o)),!0;if(Ds(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"||Ls(t))return rt(m.current,uf),!0;if(C==="pageup"||Ds(t))return rt(m.current,-uf),!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)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 mi(e),!0}if(R.current)clearTimeout(R.current);S("none");let j=hx(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(qp(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=di(D,F);if(j)O(_x(j));return!0}if(r==="slash"&&C==="return")return Z(),!0;return!0}return!0}var xw=I(()=>{vr();wo();hl();Rr();ke();pf();tt()});var yl=I(()=>{Qu()});function ww(e,t){if(t.length===0)return[];let r=new Set(t);return co(e,t).filter((n)=>!r.has(n))}function Sw(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 kw(e){let t=bl(e);if(t==="")return"Started selected services";return`Started ${t}`}function Cw(e){let t=bl(e);if(t==="")return"Stopped running services";return`Stopped ${t}`}function Pw(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 $s(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 _w(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=$s(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 Rw(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 Ew(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 Tw(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 Aw(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)=>Ey(n,r)),!0;return!1}function Lw(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(Lp(g,_,-1));return}if(K&&(r==="right"||r==="]"||n==="logs"&&r==="l")){C(Lp(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")&&sw(t)){let A=Bc(p,d[l]??m);if(s&&ww(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(Ls(t)){let A=li(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(Ds(t)){let A=li(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)=>Ty(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=Ms(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")&&aw(t))W()}var Dw=I(()=>{yl();vr();gr();xl();Lt();hl();ke();Os();Er()});import{useKeyboard as WI}from"@opentui/react";import{useCallback as ff}from"react";function Iw({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=ff(()=>{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=ff((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=ff(()=>{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=Ms(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(cw(ce,e)){S();return}if(Or(ce)&&(K==="="||K==="+"||K==="plus")){Xt(bs(mt,1));return}if(Or(ce)&&(K==="-"||K==="_"||K==="minus")){Xt(bs(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(bw({...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(tw(ce,e)){y("leader"),or.current=setTimeout(()=>y("none"),xr);return}if(rw(ce,e)||an(ce,e)){W(""),Z(0),C("commands"),y("slash");return}if(nw(ce,e)){y("help");return}if(ow(ce,e)||u==="logs"&&K==="f"&&!ce.ctrl&&!ce.meta){j("logs"),H(!0);return}if(K==="escape"||iw(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(lw(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?kx(u):Sx(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(Aw(A,ce))return;let ve=ex(K);if(ve){j(ve);return}Lw(A,ce)})}var Mw=I(()=>{te();wo();In();Lt();hl();Os();Er();Rr();xw();Dw()});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=hi(n.services)?n.services:{},i=[];if(n.networks!==void 0)i.push({service:"(root)",field:"networks",reason:Ow.networks??"not modeled"});let s={},a=[];for(let[l,d]of Object.entries(o)){if(!hi(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
|
|
2219
|
+
`}function qI(e,t,r){for(let f of Object.keys(t)){let p=Ow[f];if(p)r.push({service:e,field:f,reason:p})}let n={},o=$w(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 $w(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(hi(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(hi(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(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 XI(e){if(!hi(e)||e.disable===!0)return;let t=$w(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 Ow;var mf=I(()=>{Ow={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 Fw(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 Nw(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 Hw(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 jw(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:`${yh(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 Bw(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 Uw(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 Ww(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:Rs(r),identity:Gc(r),health:Wc(r),depends:r.dependencies.map(_a).join(", ")||"\u2014",restart:Kc(r)}})}function Gw(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 Kw(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 qw(e){return Object.keys(e.templates).sort().map((t)=>({name:t,summary:oM(e.templates[t])}))}function Vw(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 zw(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
|
|
2224
|
+
`)}function Yw(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 Jw(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,Xw=80,gi=10;var hf=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 Qw({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=_p(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}; ${fc}`)}).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(
|
|
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
|
|
2163
|
-
`),t}function
|
|
2164
|
-
`,l=
|
|
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:zw(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=gx(Ce),ue=_p(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=vx(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 Zw=I(()=>{te();mf();hf();gr();Lt();tt()});import{useCallback as eS,useRef as cM,useState as tS}from"react";function rS({cfg:e,readTextFile:t,setOverlay:r,setStatus:n,validateConfigText:o,writeTextFile:i,controller:s,setConfirmKind:a,refresh:c}){let l=cM(null),[d,u]=tS(""),[f,p]=tS(""),m=eS(()=>{if(!e)return;try{u(t(e.configPath)),p(""),r("config-edit")}catch(v){n(L(v))}},[e]),h=eS(()=>{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 nS=I(()=>{te()});import{useEffect as lM}from"react";function oS({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)=>Wb(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===Ei){a(lx(h));return}if(h.type===la)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 iS=I(()=>{te();vr();gr()});import{useCallback as dM,useEffect as sS,useRef as aS,useState as vi}from"react";function cS({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),[b,g]=vi(""),[_,R]=vi(0),[S,y]=vi({active:"Preparing diagnostics",checks:[]}),x=aS(void 0),w=aS(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 sS(()=>{c(r?.google.project_id??"").then(f).catch((D)=>{a(L(D))})},[r?.google.project_id,n?.identity.project]),sS(()=>{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 lS=I(()=>{te()});import{useCallback as gf,useState as Cl}from"react";function dS({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"),_=gf(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(kw(F))}catch(w){v(!1),await o(),i(L(w))}},[r,e,o,n]),R=gf(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(Cw(w))}catch(w){v(!1),await o(),i(L(w))}},[r,e,o,n]),S=gf(async(y,x,w=!1)=>{if(!e||!r){i("no configuration loaded");return}try{let k=Sw(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 uS=I(()=>{te();xl();Lt()});import{useCallback as Lr,useEffect as Kr,useMemo as Fn,useRef as uM,useState as be}from"react";function fS(e,t,r,n,o){let i=Tp(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 mS({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=pS,[H,ae]=be(t.log_timestamps!==!1),[Q,nt]=be(t.log_metadata!==!1),[ft,ie]=be(()=>[...Ub]),[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(()=>Ep(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(()=>Ep(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)=>fS(le,ue,ce,vt,yt),[ue]),wr=Lr((le)=>fS(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=Tp(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?pS: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]),Us=Lr(async()=>{if(!e||c)return;try{let le=await e.logsPage(Dr);a((bt)=>Gb(bt,le.events)),xr(le.prevCursor),qt(le.hasPrev),await dn()}catch(le){i(L(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(()=>Us())},[o,Us]),Kr(()=>{sr.current=s},[s]),Kr(()=>{let le=Ap(ge,ve.start,nr),bt=F&&Ap(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,Ws=Kb(Xr,Pt.events);a(Ws);let _o=Ws.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:ki,toggleSystemLogs:Ci}}var pS,pM=2000;var hS=I(()=>{te();gr();pS=[]});import{useCallback as ko,useState as gS}from"react";function vS({tui:e,controller:t,cfg:r,persistPrefs:n,snap:o,refresh:i,setStatus:s}){let[a,c]=gS(""),[l,d]=gS(()=>e.mcp_port??fo(t?.cfg.repoRoot??process.cwd())),u=ko((g)=>{let _=Rn(g);d(_);let R=r?.repoRoot??process.cwd();if(Ly(R,_)){n({mcp_port:null},`MCP port ${_} (default)`);return}n({mcp_port:_},`MCP port ${_}`)},[r,n]),f=ko(()=>{if(a==="")return l;let g=Ay(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 _=rf(g);if(_===void 0)return!1;let R=o?.mcp?.address??fi(o?.mcp?.port??l),S=Is(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 yS=I(()=>{te();yl();ps();Kp();Os()});import{RGBA as fM}from"@opentui/core";import{useCallback as cn,useEffect as mM,useMemo as bS,useRef as Pl,useState as _l}from"react";function xS({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(()=>ys(e.font_size)),k=Pl(x),D=gb(r()),O=bS(()=>{let H=ub(m);if(Tc(m)!=="terminal"||!pp(n))return H;let ae=n??H.background;return{...H,background:ae,panel:ae,element:ae,inverse:ae}},[n,m]),F=Tc(m)==="terminal"&&pp(n)?fM.defaultBackground(n??void 0):O.background,Z=bS(()=>xb({themeName:m,fontSize:x,mouse:b,leaderMs:R,locked:D,configPath:D?e.path||vp(r(),o()):vp(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=ys(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(vb(m,H));return}if(Q.id==="leader"){B(yb(R,H));return}if(Q.id==="font"){z(bs(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 wS=I(()=>{Er();tt()});import{jsxDEV as Fs}from"@opentui/react/jsx-dev-runtime";function SS(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 Fs(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:[Fs("text",{fg:t.muted,wrapMode:"word",children:p},void 0,!1,void 0,this),u.id==="auth"||u.id==="write"||u.id==="repo"?Fs("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):Fs("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),Fs(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 vf(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 kS(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=Ta(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 yf=I(()=>{Aa();ke()});import{useCallback as bf,useState as Rl}from"react";function CS({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=bf(()=>{let S=wn(l);u(0),p(S),h(vf("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]),_=bf(async(S)=>{try{Da(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=bf(async()=>{let S=wt[d];if(!S)return;let y=kS(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?vf(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 PS=I(()=>{Aa();Ia();te();yf()});import{useCallback as gM,useEffect as _S,useState as xf}from"react";function RS({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;_S(()=>{o(void 0),c("")},[t]),_S(()=>{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 ES=I(()=>{te()});import{jsxDEV as Nn}from"@opentui/react/jsx-dev-runtime";function TS(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 AS=I(()=>{ke();Rr()});import{jsxDEV as wf}from"@opentui/react/jsx-dev-runtime";function LS(e){let{palette:t,title:r,body:n,kind:o,termW:i,termH:s}=e,a=cx(o),c=a.map((l)=>`${l.key} ${l.label}`).join(" \xB7 ");return wf(pt,{palette:t,title:r,bottomTitle:c,termW:i,termH:s,preferW:52,preferH:8,borderColor:t.warning,gap:1,children:[wf("text",{fg:t.text,wrapMode:"word",children:n},void 0,!1,void 0,this),wf(it,{palette:t,hints:a},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}var DS=I(()=>{vr();ke()});import{jsxDEV as IS}from"@opentui/react/jsx-dev-runtime";function MS(e){let{palette:t,termW:r,termH:n}=e,o=Np();return IS(pt,{palette:t,title:"leader",termW:r,termH:n,preferW:68,preferH:5,anchor:"bottom",children:IS(it,{palette:t,hints:o},void 0,!1,void 0,this)},void 0,!1,void 0,this)}var OS=I(()=>{wo();ke()});import{jsxDEV as Jt,Fragment as vM}from"@opentui/react/jsx-dev-runtime";function $S(e){let{palette:t,event:r,termW:n,termH:o,scrollRef:i}=e;if(!r)return null;let s=r.raw?$b(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 FS=I(()=>{gr();ke();tt()});import{jsxDEV as U}from"@opentui/react/jsx-dev-runtime";function Sf(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 NS(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 HS(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=_w(r,n,s),h=r.waves[m.currentWaveIndex]??[],v=$s(h,n,s),b=Sf(v,t),g=yM(o,i,v,t),_=Ew(s,o,i,r.profile),R=Tw(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(NS,{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=$s(C,n,s),z=Sf(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(NS,{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=$s(C,n,s),z=Sf(B,t),de=`${Rw(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=Pw(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 jS=I(()=>{Ot();xl();Lt();ke();tt()});import{jsxDEV as yr}from"@opentui/react/jsx-dev-runtime";function BS(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 US=I(()=>{ke()});import{jsxDEV as El}from"@opentui/react/jsx-dev-runtime";function WS(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 GS=I(()=>{ke()});import{jsxDEV as Wt}from"@opentui/react/jsx-dev-runtime";function KS(e){let{palette:t,items:r,query:n,selected:o,onQuery:i,onSubmit:s,title:a="commands"}=e,c=xo(n)!=="",l=Ax(o,kf,r.length),d=c?r.slice(l,l+kf):Lx(r,o,kf),u=c?[{group:"",items:d}]:Op(d),f=Math.max(d.length+(c?0:u.length),1),p=di(r,o),m=Px(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":" "} /${$p(b).slice(0,Mp)}`},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:Cx(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 kf=10,bM=3;var qS=I(()=>{wo()});import{jsxDEV as YS}from"@opentui/react/jsx-dev-runtime";function JS(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?`${up[u]} \xB7 current`:up[u],value:u})),d=ol(o,i,VS,zS,"center",!ho(ye()));return YS(pt,{palette:t,title:"/themes",bottomTitle:"enter save \xB7 esc revert",termW:o,termH:i,preferW:VS,preferH:zS,children:YS("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 VS=52,zS=22;var XS=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 QS(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?ou(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=I(()=>{jr();Yt();ke()});import{jsxDEV as M}from"@opentui/react/jsx-dev-runtime";function nk(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>=Xw,c=a?Math.floor((s-tk)/2):s,l=Ww(r),d=Gw(r),u=Kw(r),f=qw(r),p=Vw(r);return M(kt,{palette:t,title:`config ${r.project.name||"unnamed"}`,children:[M(ut,{palette:t,items:Fw(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:rk,children:[M(Cf,{wide:a,left:M(qr,{palette:t,title:"project",children:M(yi,{palette:t,facts:Nw(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:Hw(r),width:c},void 0,!1,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this),M(Cf,{wide:a,left:M(qr,{palette:t,title:"runtime",children:M(yi,{palette:t,facts:jw(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:Bw(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:Uw(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(ek,{palette:t,rows:p,empty:"no tasks",width:s,selected:o},void 0,!1,void 0,this)},void 0,!1,void 0,this),M(Cf,{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(ek,{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:Yw(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 Cf(e){let{wide:t,left:r,right:n}=e;if(!t)return M("box",{flexDirection:"column",overflow:"hidden",gap:rk,flexShrink:0,children:[r,n]},void 0,!0,void 0,this);return M("box",{flexDirection:"row",overflow:"hidden",gap:tk,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:_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=Jw(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(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: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 ek(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 tk=1,rk=1;var ok=I(()=>{Yt();hf();Mt();Ot();ke();tt()});import{jsxDEV as Ft}from"@opentui/react/jsx-dev-runtime";function ik(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 sk=I(()=>{Yt();ke()});import{jsxDEV as Dl}from"@opentui/react/jsx-dev-runtime";function bi(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 ak=()=>{};import{jsxDEV as Ns}from"@opentui/react/jsx-dev-runtime";function xi(e){let{palette:t,logs:r,names:n,service:o,errorOnly:i,width:s,onService:a,onToggleErrors:c,facets:l}=e,d=l?zb(n,l):Vb(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(ai(f[p]?.label??"all"),s-ai(m));return Ns(Ut,{palette:t,backgroundColor:t.element,children:Ns("box",{height:1,flexDirection:"row",overflow:"hidden",backgroundColor:t.element,children:[Ns("box",{flexGrow:1,overflow:"hidden",children:Ns(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),Ns(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 ck=I(()=>{gr();In();ke();tt()});import{jsxDEV as RM}from"@opentui/react/jsx-dev-runtime";function Hs(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 lk=I(()=>{ke()});import{useEffect as Pf,useRef as dk}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,b=dk(null),g=dk(""),_=n===void 0?r:r.slice(-n),R=[...new Set(r.map((k)=>k.service))],S=Bb(o,R),y=jb({width:o,serviceWidth:S,showTimestamps:d,showMeta:u}),x=_[_.length-1],w=x===void 0?"empty":String(x.seq);return Pf(()=>{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]),Pf(()=>{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]),Pf(()=>{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:Nb(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=Fb(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:Ts,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:Ts,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:[Hb(n,i,s).map((a,c)=>Ye("span",{fg:fb(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 uk=I(()=>{Ot();gr();tt()});var _f=I(()=>{ak();ck();lk();uk()});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=Pp(s),m=Cp(s),h=kp(s),v=Rb(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 Rf=I(()=>{Mt();Ot();Lt();ke();tt()});import{jsxDEV as fe}from"@opentui/react/jsx-dev-runtime";function fk(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<ci,Ee=jc(n),Ne=cl(n,i),ne=i.filter((ge)=>n?.services[ge]?.state==="FAILED").length,H=qb(o),ae=Ee?Ab(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)=>!si(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(xi,{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(Hs,{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(wi,{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(bi,{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 pk(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)=>pk(d.rt)-pk(u.rt));if(s.length===0)return null;let a=s.slice(0,MM),c=s.length-a.length,l=Math.max(8,o-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.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:Ef,flexShrink:0,overflow:"hidden",children:fe("text",{fg:Ze(d,t),wrapMode:"none",children:oe(d,Ef)},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-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.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:Tf,flexShrink:0,overflow:"hidden",children:fe("text",{fg:t.text,wrapMode:"none",children:oe(a.name,Tf)},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,Ef=14,NM=4,Tf=14;var mk=I(()=>{At();Yt();Mt();vr();Ot();gr();Lt();dl();ke();tt();_f();Rf()});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 hk(e){let{palette:t,report:r,loading:n,progress:o,error:i,selected:s,onPick:a,onReload:c}=e,l=pi(s,Lf);if(n)return re(WM,{palette:t,progress:o},void 0,!1,void 0,this);if(i)return re(Zx,{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,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)%Af.frames.length),Af.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:`${Af.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:`${Lf}-${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:`${Lf}-${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 Lf="doctor-row",Af;var gk=I(()=>{Yt();Ot();ke();tt();Af=jM.bouncingBall});import{jsxDEV as We,Fragment as KM}from"@opentui/react/jsx-dev-runtime";function yk(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=Rp(u,ae.pad,k),nt=a?r:r.filter((ge)=>!si(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(vk,{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(vk,{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(bi,{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(xi,{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(Hs,{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(wi,{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(bi,{palette:t,width:u,newer:_,onJump:x},void 0,!1,void 0,this):null]},void 0,!0,void 0,this)}function vk(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(xi,{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 bk=I(()=>{_f();Yt();Mt();gr();ke();Rr()});import{jsxDEV as Ge}from"@opentui/react/jsx-dev-runtime";function wk(e){let{palette:t,cfg:r,snap:n,profile:o,selected:i,onPick:s}=e,a=ye(),c=pi(i,xk),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:`${xk}-${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 xk="profile-row";var Sk=I(()=>{Yt();Mt();Lt();ke();tt()});import{jsxDEV as V,Fragment as _k}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||Rk,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:Df,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:Df,flexShrink:0,overflow:"hidden",children:V("text",{fg:t.muted,children:oe("TIME",Df)},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 Ek(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(kk,Math.min(tO,Math.floor(o*0.36))),m=f?o-Ck:o-p-Ck,h=Math.max(20,m-2),v=h>=Pk,b=h>=rO,g=h>=Pk?nO:oO,_=l.reduce((S,y)=>Math.min(g,Math.max(S,(y.route||Rk).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:kk,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(_k,{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:ja(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(_k,{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 Df=9,Ol=8,$l=4,Fl=8,Nl=4,YM=8,JM=2,XM=64,Rk="(none)",kk=26,tO=56,Ck=2,Pk=46,rO=54,nO=20,oO=12;var Tk=I(()=>{Yt();vr();Ot();ke()});import{jsxDEV as pe}from"@opentui/react/jsx-dev-runtime";function Dk(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(If,{...e,compact:!1},void 0,!1,void 0,this)},void 0,!1,void 0,this)}function If(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)||Rs(d,u),h=Tb(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(_a).join(", ")||"\u2014"},{label:"ports",value:Rs(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:Lk(f)},{text:p,tone:Lk(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(Mf,{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(Mf,{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(Ak,{palette:t,items:r,width:i},void 0,!1,void 0,this),pe(Ak,{palette:t,items:n,width:i},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function Ak(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(Mf,{palette:e.palette,item:r,valueWidth:t},r.label,!1,void 0,this))},void 0,!1,void 0,this)}function Mf(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 Lk(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 Of=I(()=>{ht();Yt();Mt();Ot();Lt();ke();tt()});import{jsxDEV as Hn}from"@opentui/react/jsx-dev-runtime";function Ik(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,b=n[i]??"",g=Hc(a,n,v),_=4,R=v?Math.max($f,a-_):Math.max($f,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:$f,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(If,{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 $f=36,lO=12;var Mk=I(()=>{Yt();vr();Lt();tt();Of();Rf()});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 Nk(e){let{palette:t,items:r,selected:n,locked:o,width:i,onPick:s,onActivate:a}=e,c=ye(),l=gp(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=pi(n,Fk),l=Math.max(8,i-Ok-$k-10),d=0,u=wb(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:`${Fk}-${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:$k,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:Ok,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?`${bb(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 Ok=14,$k=2,dO=5,Fk="settings-row";var Hk=I(()=>{Mt();Ot();ke();Er()});import{jsxDEV as Ae}from"@opentui/react/jsx-dev-runtime";function jk(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 Bk=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 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 Ff(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 Bs(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:Ff(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 Uk(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 js(e){let{palette:t,facts:r,width:n}=e,o=Wp(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(Bs,{palette:t,header:!0,gap:ui,cells:i},void 0,!1,void 0,this),ee(Uk,{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?Hx(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(Bs,{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(Bs,{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 Wk(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)=>jx(r.services[B])),d=Dx(c,n),u=Vx(d,l),f=l&&d.live>0?d.healthy/d.live:d.total>0?d.live/d.total:0,p=ii(f,wO),m=d.failed>0?"error":l&&d.live>0&&d.healthy<d.live?"warning":"success",h=$x(o,s.pad),v=n?.session_id?Jl(n.session_id):void 0,b=v?Ss(Date.now()-v.getTime()):"just started",g=n?.logs.total??0,_=n?.logs.errors??0,R=Ox(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?Ux(w.loadAvg1,w.cpuCount):void 0,O=w?Wx(k,w.memTotalKB):void 0,F=[];if(w&&D&&O){F.push(D,O,{what:"Computer",reading:Bx(w.platform),meaning:`on for ${Ss(w.hostUptimeSec*SO)}`,tone:"text"});let B=n?.stats_series;if(B&&B.cpu.length>1)F.push({what:"Load trend",reading:Up(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:Up(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?qx(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(Si,{palette:t,title:`Your services ${d.live} of ${d.total} started`,tone:Z,children:[ee(js,{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",Wp(u,h).what)},void 0,!1,void 0,this),ee("span",{children:" ".repeat(ui)},void 0,!1,void 0,this),ee("span",{fg:Ff(t,{text:"",width:1,tone:m}),children:`[${p}]`},void 0,!1,void 0,this),ee("span",{fg:Ff(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:D?.tone==="error"||O?.tone==="error"?"error":D?.tone==="warning"||O?.tone==="warning"?"warning":"success",children:ee(js,{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(js,{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(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(Si,{palette:t,title:"Log lines",tone:_>0?"warning":"muted",children:ee(js,{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(js,{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=Fx(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:Ps});if(i.mem)s.push({text:"ram",width:_s});if(i.up)s.push({text:"up",width:Cs});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(Bs,{palette:t,header:!0,gap:$t,cells:s},void 0,!1,void 0,this),ee(Uk,{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=Gx(n),a=Kx(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?Cb(n.cpuPercent):"\u2014",width:Ps,tone:_O(n?.cpuPercent)});if(o.mem)l.push({text:n?.memoryKB!==void 0?ks(n.memoryKB):"\u2014",width:_s,tone:"text"});if(o.up)l.push({text:Mx(n),width:Cs,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(Bs,{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 Gk=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 Kk,useRef as Vr,useState as Je}from"react";import{jsxDEV as he,Fragment as LO}from"@opentui/react/jsx-dev-runtime";function qk({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(Eb(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=xS({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:Us}=P,ki=cS({controller:g,workspace:c,cfg:J,snap:Q,screen:k,configReloadError:Ce,setSnap:nt,setStatus:ie}),{google:Ci,doctor:le,doctorLoading:bt,doctorError:Pt,doctorProgress:Xr,setDoctorTick:Ws}=ki,_o=t.keybinds.copy??Zr(),Sr=Kk(()=>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=mS({controller:g,tui:t,names:Sr,screen:k,refresh:un,setStatus:ie}),{logs:Wf,setLogs:vC,paused:Gf,errorOnly:jl,setErrorOnly:Bl,showSystemLogs:Kf,logSearch:qf,setLogSearch:Vf,logSearchFocused:yC,setLogSearchFocused:bC,logFollow:zf,logSince:xC,logService:Yf,setLogService:Ul,logServiceB:wC,setLogServiceB:SC,splitLogs:Wl,splitFocus:Gl,setSplitFocus:kC,logSliceB:Jf,logWindowB:Xf,logSelectedB:Qf,logPinnedB:CC,pinLogViewB:PC,logServices:_C,logShowTimestamps:RC,logShowMeta:EC,logSource:TC,logRegex:Zf,logWrap:em,logPinned:tm,logSelected:rm,logsFullscreen:Kl,dashboardLogCursor:AC,setDashboardLogCursor:LC,logFacets:nm,logSources:om,filteredLogs:im,filteredLogsB:DC,logWindow:Gs,logSlice:Ks,pinLogView:ql,applyLogCursorA:IC,applyLogCursorB:MC,jumpToLatestLogs:sm}=Hl,Ro=Kk(()=>{if(C==="tasks")return Fp(Object.keys(J?.tasks??{}),Z,"tasks","run this task");if(C==="services")return Fp(Object.keys(J?.services??{}),Z,"services","exec in this service");return Ex(Z,{services:Object.keys(J?.services??{}),tasks:Object.keys(J?.tasks??{})})},[J,Z,C]);AO(()=>{Ee(0)},[Z]);let Vl=Zb(k,{doctor:le?.checks.length??0,settings:Un.length,profiles:Object.keys(J?.profiles??{}).length,services:Sr.length,logs:Wl&&Gl===1?Jf.length:Ks.length,mcp:fw(),config:Object.keys(J?.tasks??{}).length}),am=k==="logs"?Wl&&Gl===1?Qf:rm:Ne,Ir=Vl<=0?Math.max(0,am):Math.max(0,Math.min(am,Vl-1)),OC=k==="detail"?ge:k==="services"?Sr[Ir]??"":"",{inspectorEnv:cm,inspectorEnvStatus:lm,inspectorEnvError:dm,resolveEnvironment:$C}=RS({controller:g,cfg:J,envService:OC}),Eo=jn(()=>{if(F("none"),W(""),j("commands"),bC(!1),A.current)clearTimeout(A.current)},[]),zl=jn((X)=>{ae((xt)=>xt.includes(X)?xt.filter((XC)=>XC!==X):[...xt,X])},[]),um=vS({tui:t,controller:g,cfg:J,persistPrefs:Dr,snap:Q,refresh:un,setStatus:ie}),{mcpPortDraft:FC,mcpPort:NC,toggleMcpTool:HC,toggleMcp:jC,copyMcpSnippet:BC}=um;oS({controller:g,cfg:J,paused:Gf,logSince:xC,refresh:un,setLogs:vC,setCfg:Dt,setConfigReloadError:Po,setStatus:ie});let qs=jn((X)=>{vt(X),D("detail")},[]),pm=jn((X)=>{let xt=X.value!==""?X.value:X.required?"(required, not set)":"(empty)";br({title:X.key,body:xt}),F("scroll-text")},[]),Yl=dS({controller:g,workspace:c,cfg:J,snap:Q,refresh:un,setStatus:ie,setOverlay:F}),{plan:Vs,planInitiallyRunning:UC,planBusy:WC,lifecycle:fm}=Yl,Qr=CS({workspace:c,renderer:y,setOverlay:F,setStatus:ie,setScreen:D,onAttached:o,setController:_,setCfg:Dt,setBootError:S}),{configEditRef:GC,configEditText:KC,configEditError:qC,setConfigEditError:VC,openConfigBuffer:mm,saveConfigBuffer:zC}=rS({cfg:J,readTextFile:m,setOverlay:F,setStatus:ie,validateConfigText:f,writeTextFile:h,controller:g,setConfirmKind:K,refresh:un}),hm=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:zs}=Qw({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:$C,openDetail:qs,copySelection:hm,lastExportPath:Vt,openConfigBuffer:mm,onDown:n??((X)=>r(X)),workspace:c,logView:Hl,diagnostics:ki,lifecycleActions:Yl}),gm=jn(()=>{let X=di(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)zs(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}zs(X,Rx(X,Z))},[Eo,Ro,Z,zs,de,C]),vm=jn((X)=>{dn(X),Eo()},[Eo,dn]);Iw({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:qs,copySelection:hm,height:w,paletteIndex:B,applyTheme:vm,runCommand:zs,saveConfigBuffer:zC,filtered:Ro,slashIndex:de,submitSlash:gm,advanceWizard:()=>void Qr.advanceWizard(),bootErrorMissing:s,bootError:R,createStarterConfig:v,startWizard:Qr.startWizard,toggleChecked:zl,checked:H,detailName:ge,refresh:un,openConfigBuffer:mm,setOverlay:F,setConfirmKind:K,setConfirmDetail:Me,setPortTarget:gt,setLogDetail:Qe,logDetail:rr,setLogSearch:Vf,setProfile:Ct,setStatus:ie,setPaletteIndex:z,setSlashIndex:Ee,setQuery:W,setSlashPicker:j,setScreen:D,setSelected:ne,setChecked:ae,setConfigEditError:VC,freePort:p},logView:Hl,lifecycleActions:Yl,mcp:um,preferences:P,diagnostics:ki,refs:{interruptArmedAt:ve,leaderTimer:A,logDetailsScrollRef:qt,scrollTextScrollRef:ir,routeDetailsScrollRef:or,planScrollRef:sr,helpScrollRef:xr,configScrollRef:Kt,detailScrollRef:nr}});let ym=ax(ce,Xe,Le),YC=new Set(Vs?.waves.flat()??[]),JC=Vs===void 0||fm==="stop"?"":Object.entries(Q?.services??{}).find(([X,xt])=>YC.has(X)&&xt.state==="FAILED")?.[0]??"";return he(yp.Provider,{value:xs(Jr),children:he("box",{flexDirection:"column",width:x,height:w,backgroundColor:Bn,overflow:"hidden",children:[Kl?null:he(LO,{children:[he(Jx,{palette:se,cfg:J,snap:Q,google:Ci,profile:Xe,reveal:at,width:x},void 0,!1,void 0,this),he(Xx,{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(fk,{palette:se,cfg:J,snap:Q,logs:Wf,names:Sr,selected:Ir,checked:H,width:x,followTick:zf,logService:Yf,errorOnly:jl,showSystemLogs:Kf,onOpen:qs,onSelectIndex:ne,selectedLog:AC,onToggle:zl,logSources:om,onFilterService:Ul,onToggleErrors:()=>Bl((X)=>!X),onShowErrors:()=>{Ul(""),Bl(!0)},wrapMode:em,view:Ks,follow:!tm,onLeaveLatest:ql,onPickLog:(X)=>{let xt=Ks[X];if(!xt)return;LC(X),Qe(xt),ql()},viewStart:Gs.start,viewTotal:im.length,newer:Gs.newer,onJumpLatest:sm,facets:nm,leftover:ct,search:qf,regex:Zf},void 0,!1,void 0,this):null,k==="services"?he(Ik,{palette:se,cfg:J,names:Sr,snap:Q,selected:Ir,checked:H,width:x,reveal:at,onOpen:qs,onSelectIndex:ne,onToggle:zl,resolvedEnv:cm,envStatus:lm,envError:dm,onSelectEnv:pm},void 0,!1,void 0,this):null,k==="detail"?he(Dk,{palette:se,cfg:J,snap:Q,name:ge,reveal:at,width:x,envScrollRef:nr,resolvedEnv:cm,envStatus:lm,envError:dm,onSelectEnv:pm},void 0,!1,void 0,this):null,k==="logs"?he(yk,{palette:se,logs:Wf,names:Sr,logSources:om,service:Yf,errorOnly:jl,showSystemLogs:Kf,search:qf,searchFocused:yC,followTick:zf,width:x,onSearch:Vf,onService:Ul,onToggleErrors:()=>Bl((X)=>!X),source:TC,regex:Zf,services:_C,showTimestamps:RC,showMeta:EC,view:Ks,wrapMode:em,selected:rm,follow:!tm,newer:Gs.newer,viewStart:Gs.start,viewTotal:im.length,onSelect:IC,fullscreen:Kl,onLeaveLatest:ql,onJumpLatest:sm,facets:nm,split:Wl,splitFocus:Gl,serviceB:wC,viewB:Jf,selectedB:Qf,followB:!CC,newerB:Xf.newer,viewStartB:Xf.start,viewTotalB:DC.length,onServiceB:SC,onSelectB:MC,onLeaveLatestB:PC,onFocusPane:kC},void 0,!1,void 0,this):null,k==="auth"?he(QS,{palette:se,cfg:J,google:Ci,identity:Q?.identity},void 0,!1,void 0,this):null,k==="credentials"?he(ik,{palette:se,credentials:Q?.credentials},void 0,!1,void 0,this):null,k==="proxy"?he(Ek,{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(mw,{palette:se,snap:Q,port:NC,portDraft:FC,selected:Ir,onPick:ne,onToggle:()=>{jC()},onCopy:BC,onToggleTool:(X)=>{HC(X)}},void 0,!1,void 0,this):null,k==="doctor"?he(hk,{palette:se,report:le,loading:bt,progress:Xr,error:Pt,selected:Ir,onPick:ne,onReload:()=>{Ws((X)=>X+1),ie("Re-running doctor")}},void 0,!1,void 0,this):null,k==="stats"?he(Wk,{palette:se,cfg:J,snap:Q,width:x,onRefresh:un},void 0,!1,void 0,this):null,k==="config"?he(nk,{palette:se,cfg:J,width:x,selectedTask:Ir,scrollRef:Kt},void 0,!1,void 0,this):null,k==="profiles"?he(wk,{palette:se,cfg:J,snap:Q,profile:Xe,selected:Ir,onPick:ne},void 0,!1,void 0,this):null,k==="setup"?he(jk,{issues:J?b(J):["configuration not loaded"],palette:se,cfg:J,google:Ci,bootError:R,bootErrorMissing:s,step:Ir},void 0,!1,void 0,this):null,k==="settings"?he(Nk,{palette:se,items:Un,selected:Ir,locked:wr,width:x,onPick:ne,onActivate:Us},void 0,!1,void 0,this):null]},void 0,!0,void 0,this),O==="slash"?he(KS,{palette:se,items:Ro,query:Z,selected:de,title:C==="tasks"?"tasks":C==="services"?"services":"commands",onQuery:W,onSubmit:gm},void 0,!1,void 0,this):null,O==="themes"?he(JS,{palette:se,themeName:ue,selected:B%St.length,termW:x,termH:w,onIndex:z,onPreview:Yr,onPick:vm},void 0,!1,void 0,this):null,O==="help"?he(yw,{palette:se,termW:x,termH:w,copyKey:_o,scrollRef:xr},void 0,!1,void 0,this):null,O==="leader"?he(MS,{palette:se,termW:x,termH:w},void 0,!1,void 0,this):null,O==="confirm"?he(LS,{palette:se,title:ym.title,body:ym.body,kind:ce,termW:x,termH:w},void 0,!1,void 0,this):null,O==="setup-wizard"?he(SS,{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($S,{palette:se,event:rr,termW:x,termH:w,scrollRef:qt},void 0,!1,void 0,this):null,O==="route-details"?he(BS,{palette:se,route:Xt,termW:x,termH:w,scrollRef:or},void 0,!1,void 0,this):null,O==="scroll-text"&&mt?he(WS,{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(TS,{palette:se,path:J.configPath,initialValue:KC,error:qC,termW:x,termH:w,textareaRef:GC},void 0,!1,void 0,this):null,O==="plan"&&Vs?he(HS,{palette:se,plan:Vs,snap:Q,busy:WC,failed:JC,kind:fm,termW:x,termH:w,scrollRef:sr,initiallyRunning:UC,onDismiss:Eo},void 0,!1,void 0,this):null,Kl?null:he(Qx,{palette:se,screen:k,overlay:O,status:ft,paused:Gf,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 Vk=I(()=>{te();Yt();Kp();Mt();vr();wo();In();Lt();Mw();Zw();nS();iS();lS();uS();hS();yS();wS();PS();ES();AS();DS();pf();OS();FS();jS();US();GS();yf();qS();XS();ZS();ok();sk();mk();gk();bk();Os();Sk();Tk();Of();Mk();Hk();Bk();Gk();Er();tt();Rr()});var zk={};tP(zk,{renderApp:()=>Hf,runTui:()=>Nf,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 Hf(e,t,r)}async function Nf(e,t){let r,n,o=!1;try{r=await e.openTui("",t)}catch(s){n=L(s),o=ya(s,qn)}let i=e.loadTuiConfig(r?.cfg.repoRoot??process.cwd(),r?.cfg.ui.keymap);await Hf(e,r,i,n,o)}async function Hf(e,t,r=e.loadTuiConfig(t?.cfg.repoRoot??process.cwd(),t?.cfg.ui.keymap),n,o=!1){Ys();let i=wm(),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(qk,{workspace:lb(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 jf=I(()=>{db();te();Vk()});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 oa,readFileSync as ta}from"fs";import{dirname as DP,isAbsolute as Lm,join as na,resolve as IP}from"path";Js();Js();import{spawnSync as lP}from"child_process";import{randomBytes as Zl}from"crypto";import{copyFileSync as dP,existsSync as pn,mkdirSync as uP,readdirSync as pP,readFileSync as ed,renameSync as td,statSync as fP,unlinkSync as Xl,writeFileSync as mP}from"fs";import{homedir as Sm}from"os";import{basename as hP,dirname as Ql,isAbsolute as gP,join as _t,resolve as vP}from"path";var yP=448,bP=384;function Pi(e,t){let r=e.startsWith("~/")?_t(Sm(),e.slice(2)):e;return gP(r)?r:vP(t,r)}function cr(){let e=process.env.DEVCTL_HOME;if(e&&e!=="")return e;return _t(Sm(),".devctl")}function Mr(e){uP(e,{recursive:!0,mode:yP})}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))dP(o,_t(t,n))}}}return Mr(t),t}function Xs(){let e=_t(cr(),"logs");return Mr(e),e}function Qs(){let e=_t(cr(),"exports");return Mr(e),e}function kr(){let e=_t(cr(),"credentials");return Mr(e),e}function Zs(e,t=process.platform){if(t==="win32")return`\\\\.\\pipe\\devctl-${ar(e)}`;return _t(To(e),"devctl.sock")}function xP(e){return _t(To(e),"devctl.lock")}function km(e){return _t(To(e),"state.json")}function _i(e){return _t(To(e),"bootstrap.log")}var wP=5,Cm="bootstrap-";function Pm(e){let t=_i(e);if(!pn(t))return;let r=To(e),n=_t(r,`${Cm}${rd()}.log`);try{td(t,n)}catch{return}SP(r)}function SP(e){let t;try{t=pP(e).filter((n)=>n.startsWith(Cm)&&n.endsWith(".log"))}catch{return}let r=t.map((n)=>{let o=_t(e,n);try{return{path:o,mtime:fP(o).mtimeMs}}catch{return}}).filter((n)=>n!==void 0).sort((n,o)=>o.mtime-n.mtime);for(let n=wP;n<r.length;n++)try{Xl(r[n].path)}catch{}}function Cr(e,t){Mr(Ql(e));let r=_t(Ql(e),`.${hP(e)}.tmp-${process.pid}-${Zl(4).toString("hex")}`);mP(r,t,{mode:bP}),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(km(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 _m(e,t){Cr(km(e),`${JSON.stringify(t,null,2)}
|
|
2234
|
+
`)}function Rm(e,t){let r=xP(e);if(pn(r)){let n=kP(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 kP(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 CP(e);try{return process.kill(e,0),!0}catch{return!1}}function od(e){try{let t=lP("cmd.exe",["/d","/c",`tasklist /FO CSV /NH /FI "PID eq ${e}"`],{encoding:"buffer",windowsHide:!0,timeout:5000});return PP(t.stdout)}catch{return""}}function CP(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 PP(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 _P(e){return _t(To(e),"mcp-token")}function Em(e){let t=_P(e);if(pn(t)){let n=ed(t,"utf8").trim();if(n!=="")return n}let r=id();return Cr(t,r),r}ea();ea();var MP="OPENCODE_TUI_CONFIG";function ia(){return na(cr(),"tui.json")}function sa(e=""){for(let t of[Tm,MP]){let r=process.env[t];if(!r||r==="")continue;let n=Lm(r)?r:IP(e||process.cwd(),r);if(oa(n))return n}return}function OP(e,t){if(!t||Object.keys(t).length===0)return e;return{...e,keybinds:{...e.keybinds,...t}}}function Do(e,t){let r=OP(Am(),t),n=sa(e);if(n)return cd(r,ra(ta(n,"utf8")),n);let o=$P(e),i=o?cd(r,ra(ta(o,"utf8")),o):r,s=ia();if(!oa(s)||o===s)return i;return cd(i,ra(ta(s,"utf8")),s)}function Dm(e){let t=ia(),r={};if(oa(t)){let o=ra(ta(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 $P(e){let t=sa(e);if(t)return t;let r=[e||process.cwd(),na(e||process.cwd(),".devctl"),cr()],n=["tui.json","tui.jsonc"];for(let o of r)for(let i of n){let s=na(o,i);if(oa(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(NP(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]=FP(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 FP(e,t){if(e.startsWith("file://")||Lm(e))return e;return na(DP(t),e)}function NP(e){return e==="block"||e==="underline"||e==="line"||e==="default"}function Wn(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}function ra(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 f_,existsSync as Mi,mkdirSync as m_,readdirSync as Pd,readFileSync as h_,rmSync as g_,statSync as Cd,writeFileSync as v_}from"fs";import{dirname as th,join as No}from"path";ha();ha();var y_=50000,_d="session-";function eh(e){return Buffer.from(JSON.stringify(e),"utf8").toString("base64url")}function b_(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:y_,this.bus=t,this.detector=r,this.sessionID=i;let c=o===""||o.startsWith("~/")?Xs():o;if(this.persist=n&&i!=="",this.persistDir=this.persist?No(c,`${_d}${i}`):"",this.persist)m_(this.persistDir,{recursive:!0,mode:448}),S_(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=w_(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=f_(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=Qm(t.limit),n=t.cursor?b_(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:eh({session:this.sessionID,seq:d??i?.seq??0}),nextCursor:eh({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 rh(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 x_(e=new Date){let t=e.toISOString().replace(/[:.]/g,"-");return No(Qs(),`devctl-logs-${t}.log`)}function nh(e=""){if(e==="")return x_();return Pi(e,process.cwd())}function Ed(e,t){Mr(th(e));let r=t.map((n)=>`${n.timestamp} ${n.service} ${n.level} ${n.raw??n.message}`);v_(e,`${r.join(`
|
|
2165
2237
|
`)}
|
|
2166
|
-
`,{mode:384})}function
|
|
2167
|
-
`)){if(a.trim()==="")continue;let c=a.split(" "),l=c.slice(3).join(" "),d=
|
|
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
|
|
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
|
|
2171
|
-
`)}async delete(e){let t=
|
|
2172
|
-
`)}function
|
|
2173
|
-
`)}function
|
|
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 oh(e){let t=Mi(e)&&Cd(e).isDirectory()?e:th(e);if(Mr(t),process.platform==="darwin"){let r=Mi(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 ih(e=Xs()){if(!Mi(e))return[];return Pd(e).filter((t)=>t.startsWith(_d)).sort().reverse()}function sh(e,t=Xs()){let r=No(t,e);if(!Mi(r))return[];let n=[];for(let i of Pd(r)){if(!i.endsWith(".log"))continue;let s=h_(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 w_(e){let t=e.replace(/[^A-Za-z0-9._-]+/g,"_");return t===""?"service":t}function S_(e,t,r){if(!Mi(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)g_(i.path,{recursive:!0,force:!0})})}te();var{spawn:Ld}=globalThis.Bun;import{createServer as fh}from"net";var k_=65535,Ad=1;async function mh(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 C_();if(d<Ad||d>k_)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 P_(i,c.name,d);n[d]=i,a[c.name]=d}o[i]=a}return o}function C_(){return new Promise((e,t)=>{let r=fh();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 ba(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=fh();r.listen(e,"127.0.0.1",()=>{r.close(()=>t(!0))}),r.on("error",()=>t(!1))})}async function P_(e,t,r){let n=await zn(r);return __(e,t,r,n)}function __(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 R_(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 L_(e);let t=await E_(e);if(t)return t;return T_(e)}async function E_(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,R_(r,e)}catch{return}}async function T_(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 A_(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 L_(e){let t=Ld({cmd:["netstat","-ano"],stdout:"pipe",stderr:"ignore"}),r=t.stdout?await new Response(t.stdout).text():"";return await t.exited,A_(r,e)}var D_=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 I_(D_);try{process.kill(e.pid,0),process.kill(e.pid,"SIGKILL")}catch{return}}function I_(e){return new Promise((t)=>{setTimeout(t,e)})}Ia();te();import{readdirSync as KR,readFileSync as qR}from"fs";import{basename as Rg,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 Ph(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 W_(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 G_(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 K_(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 q_(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 Hi(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:Ph(e.environment),health:G_(e.health),identity:W_(e.identity),logs:{stdout:ot(t.stdout),stderr:ot(t.stderr)},restart:K_(e.restart),startup:q_(e.startup),capabilities:Rt(e.capabilities),proxy:Vd(e.proxy),expose:qd(e.expose),container:Kd(e.container),watch:V_(e.watch),hooks:z_(e.hooks)}}function V_(e){if(!N(e))return Dd();return{enabled:ot(e.enabled),paths:Rt(e.paths),debounce_ms:Ca(_e(e.debounce_ms)),ignore:e.ignore===void 0?[...Oi]:Rt(e.ignore)}}function z_(e){let t=N(e)?e:{};return{pre_start:Pr(t.pre_start),post_start:Pr(t.post_start)}}function _h(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:Ph(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 Ma(e){if(!N(e))return{services:[],environment:{}};return{services:Rt(e.services),environment:Sn(e.environment)}}function Y_(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 J_(e){if(!N(e))return $i();return{type:G(e.type),identity:Y_(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:$i()};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:J_(e.auth)}}te();import{existsSync as X_,statSync as Eh}from"fs";import{basename as Rh,dirname as Oa,isAbsolute as Q_,join as $a,resolve as Th}from"path";function Qn(e,t){if(t!=="")return Z_(t);let r=e===""?process.cwd():e;r=Q_(r)?r:Th(r);for(;;){let n=$a(r,Nt,lr);if(ji(n))return{repoRoot:r,configPath:n};let o=$a(r,"devctl.yaml");if(ji(o))return{repoRoot:r,configPath:o};let i=Oa(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 Z_(e){let t=Th(e),r;try{r=Eh(t)}catch(o){throw Pe(qn,"config path not found",o)}if(r.isDirectory()){if(Rh(t)===Nt)return{repoRoot:Oa(t),configPath:$a(t,lr)};return{repoRoot:t,configPath:$a(t,lr)}}let n=Oa(t);if(Rh(n)===Nt)n=Oa(n);return{repoRoot:n,configPath:t}}function ji(e){return X_(e)&&!Eh(e).isDirectory()}ht();function Ha(){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 eR=["environment","restart","startup","health","logs","identity","container","watch","hooks"];function Fa(e,t,r){let n=Ud(r);if(N(r))for(let i of eR){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 Ah(e,t,r=Ha(),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):Ma(i)}if(N(t.templates))for(let[o,i]of Object.entries(t.templates)){let s=e.templates[o];e.templates[o]=s?Na(s,i):Hi(i),Fa(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?Na(s,i):Hi(i),Fa(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):_h(i);if(N(t.proxy))tR(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 tR(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:Lh(e.environment,t.environment)}}function Na(e,t){if(!N(t))return e;let r=Ud(t),n={...e,environment:Lh(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?Ca(_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 Lh(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 Dh(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 Ih(e){let t=e.repoRoot||process.cwd(),r=e.proxy.credentials.trim()===""?"":Pi(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:Pi(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:$i()},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 Mh(e,t=Ha()){if(Object.keys(e.templates).length===0)return;let r={};for(let[n,o]of Object.entries(e.services))r[n]=Oh(e,o,t.services[n]??new Set,t,{});e.services=r}function Oh(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=Oh(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 $h(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 Fh=["version","project","google","profiles","templates","services","tasks","proxy","logs","auth","shutdown","ui","secrets","doctor","plugins","environment"],Nh=["extends","description","command","shell","working_dir","dependencies","ports","environment","health","identity","logs","restart","startup","capabilities","proxy","expose","container","watch","hooks"],Hh=["type","url","address","command","interval_seconds","timeout_seconds","start_period_seconds","unhealthy_threshold","healthy_reset_threshold"],jh=["service","condition"],Bh=["type","mode","service_account","config"],Uh=["enabled","policy","max_retries","backoff_seconds"],Wh=["wait_for_healthy","timeout_seconds"],Gh=["enabled","host","port"],Kh=["enabled","gateway","credentials","listen","token_endpoint","routes"],qh=["host","port"],zd=["name","match","upstream","auth"],Yd=["host","path"],Jd=["url","service","port"],Xd=["type","identity","audience","service_account","client_id","client_secret","credentials"],Vh=["max_memory_events","persistence"],zh=["enabled","directory","retention_days","max_session_logs"],Yh=["refresh_threshold_seconds"],Jh=["stop_services_on_exit","grace_seconds"],Xh=["theme","keymap"],Qh=["name"],Zh=["project_id","region"],eg=["services","environment"],Qd=["required","defaults"],tg=["stdout","stderr"],rg=["extra_markers","extra_patterns"],ng=["tools"],og=["name","command"],ig=["enabled","host","port"],sg=["path"],ag=["sources","secrets"],cg=["image","runtime","ports","env","volumes"],lg=["enabled","paths","debounce_ms","ignore"],dg=["pre_start","post_start"],ug=["command","shell","working_dir","dependencies","environment"],pg=["google","google_api","iap","network","service_identity","local_http"],fg=["|","||","&&",";",">",">>","<","&"];var mR=2;function kn(e,t){if(Array.isArray(e))return e.flatMap((o,i)=>kn(o,mg(t,String(i))));if(!wR(e))return[];let r=gR(t),n=[];for(let o of Object.keys(e)){let i=mg(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 Fh;case"project":return Qh;case"google":return Zh;case"proxy":return Kh;case"proxy.listen":return qh;case"proxy.token_endpoint":return ig;case"logs":return Vh;case"logs.persistence":return zh;case"auth":return Yh;case"shutdown":return Jh;case"ui":return Xh;case"secrets":return rg;case"doctor":return ng;case"environment":return ag;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 jh;return yR(e)}if(e.startsWith("profiles.")){if(e.split(".").length===2)return eg}if(e.startsWith("tasks.")){let t=e.split(".");if(t.length===2)return ug;if(t[2]==="environment")return Qd}if(e.includes("routes"))return xR(e);if(e.startsWith("doctor.tools")&&e.split(".").length>=3)return og;if(e==="plugins")return[];if(e.startsWith("plugins.")&&e.split(".").length===2)return sg;return[]}function yR(e){let t=e.split(".");if(t.length===2)return Nh;if(t.length>=3)switch(t[2]){case"health":return Hh;case"identity":return Bh;case"restart":return Uh;case"startup":return Wh;case"logs":return tg;case"environment":return Qd;case"proxy":return bR(t);case"expose":return Gh;case"container":return cg;case"watch":return lg;case"hooks":return dg;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 Yd;if(n==="upstream")return Jd;if(n==="auth")return Xd;return zd}function xR(e){if(e==="proxy.routes")return[];if(e.endsWith(".match"))return Yd;if(e.endsWith(".upstream"))return Jd;if(e.endsWith(".auth"))return Xd;if(e.includes("proxy.routes")&&e.split(".").length===mR+1)return zd;return[]}function mg(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(hg(t))return!0;return SR(t)}function hg(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 hg(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 gg(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=wh(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=gg(i,s);if(c===void 0)throw Error(`unresolvable reference \${${e}}`);return`http://127.0.0.1:${c}`}if(n[2]==="port"){let a=gg(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 Zd(e,t,r){let n={};for(let[o,i]of Object.entries(e))n[o]=kR(i,t,r);return n}function vg(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 yg(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 Ua=65535,Wa=1,Cg=["http","tcp","process","command"];function tu(e){let t=[];for(let[r,n]of Object.entries(e.services)){let o=n.health.type;if(o!==""&&!Cg.includes(o.toLowerCase()))t.push({service:r,type:o})}return t}var TR=0,Sg=1,kg=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(...Ba(o,n.command,n.shell));for(let i of n.dependencies)if(!e.services[i])t.push(`${o}.dependencies: unknown service "${i}"`);t.push(...Pg(`${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(...Ba(i,o.command,o.shell)),t.push(...Ba(`${i}.hooks.pre_start`,o.hooks.pre_start,o.shell)),t.push(...Ba(`${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<Wa||c.value>Ua)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=Ra(o.restart);if(a!==wa&&a!==Sa&&a!==ka)t.push(`${i}.restart.policy must be never, on_failure, or always`);if(t.push(...Pg(`${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<Wa||l>Ua)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(!Cg.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=Ni(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 ru(e){let t=[];for(let[r,n]of Object.entries(e.services)){let o=Ni(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===kg)return;if(s===Sg){r.push(`dependency cycle: ${[...n,i].join(" \u2192 ")}`);return}t[i]=Sg,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]=kg};for(let i of Object.keys(e.services))o(i);return r}function Pg(e,t,r){let n=[],o=(i,s)=>{for(let a of vg(s))if(!yg(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<Wa||e.proxy.listen.port>Ua))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!==""&&eu(n).length>0&&!wg(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||Pa;if(!Cn(n))t.push("proxy.token_endpoint.host must be a loopback address");if(r.port!==0&&(r.port<Wa||r.port>Ua))t.push("proxy.token_endpoint.port is invalid");return t}function Ba(e,t,r){if(t.shell||r)return[];for(let n of t.args)if(fg.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(!pg.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 Bi(e,t,r){try{return Ui(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 Ui(r,n)}function to(e,t){try{return nn(e,t)}catch(r){if(!ya(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 Ui(e,t,r){let n=Yn(),o=Ha();Eg(t,n,o,r?.candidateText,"main"),n.repoRoot=e,n.configPath=t;let i=VR(t);if(Rg(i)===Nt)QR(i,n,o);n=XR(n,e,t,o),n=$h(n);try{Mh(n,o)}catch(a){throw Pe(Y,"template merge failed",a)}Dh(n,o.provenance),Ih(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||!ji(s))continue;Eg(s,e,n,void 0,s===o?"home_local":"repo_local")}return e}function Eg(e,t,r,n,o="main"){let i=n!==void 0?Tg(n,e):nu(e),s=kn(i,"");if(s.length>0)throw E(Y,Wo(s));Ah(t,i,r,{source:e,layer:o})}function QR(e,t,r){_g(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?Na(c,i):Hi(i),Fa(r.services,o,i),rn(r.provenance,i,s,"modular_service",`services.${o}`)}),_g(eo(e,"profiles"),(o,i,s)=>{let a=kn(i,`profiles.${o}`);if(a.length>0)throw E(Y,Wo(a));t.profiles[o]=Ma(i),rn(r.provenance,i,s,"modular_profile",`profiles.${o}`)});let n=eo(e,"proxy","routes.yaml");if(ji(n))ZR(n,t,r)}function ZR(e,t,r){let n=nu(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)){if(Array.isArray(n.proxy.routes))t.proxy.routes.push(...n.proxy.routes.map((o)=>Xn(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)=>Xn(o)))}function _g(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=nu(i);t(Rg(n,o),s,i)}}function nu(e){let t;try{t=qR(e,"utf8")}catch(r){throw Pe(Y,"unable to read config",r)}return Tg(t,e)}function Tg(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 qa,readFileSync as rE}from"fs";import{homedir as Mg}from"os";import{join as au}from"path";var{spawn:cu}=globalThis.Bun;var nE="https://www.googleapis.com/auth/cloud-platform",Og=1500,oE=2000;function qo(){let e=globalThis;if(e.window&&typeof e.window.fetch!=="function")e.window.fetch=fetch}function lu(){let e=process.env.GOOGLE_APPLICATION_CREDENTIALS||au(Mg(),".config","gcloud","application_default_credentials.json");if(!qa(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 du("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 Ig("core/project");if(n!=="")t.projectID=n,t.projectSource="gcloud configuration"}}if(Gi())try{await jg(iE(t),oE)}catch(r){t.adcAvailable=!1,t.error=Vo(r)}if(t.userEmail===""&&t.gcloudInstalled)t.userEmail=await Ig("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 $g(e){let t=await io(e);return dr({kind:"user",email:t.userEmail,project:t.projectID,projectSource:t.projectSource,adcAvailable:t.adcAvailable})}async function Fg(){if(await cu({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 Ng(){if(await cu({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(va,"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(ga,"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(va,"IAP audience is incorrect","set auth.audience on the proxy route to the IAP OAuth client ID");if(t.includes("iap"))return Pe(va,"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 du(e){try{return(await Hg(process.platform==="win32"?["where",e]:["which",e],Og)).code===0}catch{return!1}}function Gi(){let e=process.env.GOOGLE_APPLICATION_CREDENTIALS;if(e&&qa(e))return!0;if(qa(au(Mg(),".config","gcloud","application_default_credentials.json")))return!0;let t=process.env.APPDATA;return Boolean(t&&qa(au(t,"gcloud","application_default_credentials.json")))}async function Ig(e){try{let t=await Hg(["gcloud","config","get-value",e],Og);if(t.code!==0)return"";let r=t.stdout.trim();if(r===""||r==="(unset)")return"";return r}catch{return""}}async function Hg(e,t){let r=cu({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 jg(Promise.all([n,r.exited]),t);return{code:i??1,stdout:o}}catch{return r.kill(),{code:1,stdout:""}}}function jg(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 qi,Impersonated as EE,UserRefreshClient as TE}from"google-auth-library";var{spawn:mu}=globalThis.Bun;import{existsSync as za,readdirSync as dE,readFileSync as Bg,unlinkSync as uE}from"fs";import{join as Ki}from"path";var hu="keychain-index.json",so="devctl.credentials",pu=".cred.json";function fu(e){return Ki(kr(),`${Ja(e)}${pu}`)}function Ya(e){if(e==="file"||process.env.DEVCTL_HOME&&e!=="keychain")return new Va;if(e==="keychain"||process.platform==="darwin"||process.platform==="win32"||process.platform==="linux")return new Ug(new Va);return new Va}class Va{backend="file";async get(e){let t=hE(e);if(!t||t.accessToken==="")return;return t}async set(e,t){Cr(fu(e),`${JSON.stringify({...mE(t),key:e})}
|
|
2243
|
+
`)}async delete(e){let t=fu(e);if(za(t))uE(t)}async list(){let e=kr();if(!za(e))return[];let t=[];for(let r of dE(e)){if(!r.endsWith(pu))continue;let n=r.slice(0,-pu.length),o=Wg(Ki(kr(),r));if(!o)continue;t.push(Gg(o.key??n,o.record))}return t.sort((r,n)=>r.identity.localeCompare(n.identity))}}class Ug{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(gu()),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,Gg(n,o))}return[...r.values()].sort((n,o)=>n.identity.localeCompare(o.identity))}}function pE(e){let t=gu();if(!t.includes(e))Cr(Ki(kr(),hu),`${JSON.stringify([...t,e])}
|
|
2244
|
+
`)}function fE(e){let t=gu(),r=t.filter((n)=>n!==e);if(r.length!==t.length)Cr(Ki(kr(),hu),`${JSON.stringify(r)}
|
|
2245
|
+
`)}function gu(){let e=Ki(kr(),hu);if(!za(e))return[];try{let t=JSON.parse(Bg(e,"utf8"));return Array.isArray(t)?t.filter((r)=>typeof r==="string"):[]}catch{return[]}}function mE(e){return{...e,accessToken:""}}function hE(e){return Wg(fu(e))?.record}function Wg(e){if(!za(e))return;try{let t=JSON.parse(Bg(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 Gg(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 Ja(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-${Ja(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-${Ja(e)}.bin')`])}async function bE(e){if(process.platform==="darwin"){let t=await uu(["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 uu(["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-${Ja(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 uu(["powershell","-NoProfile","-Command",t]);return r.code===0&&r.stdout!==""?r.stdout:void 0}return}var vu=3000;async function zo(e){try{return await mu({cmd:e,stdout:"ignore",stderr:"ignore",timeout:vu}).exited}catch{return 1}}async function xE(e,t){try{return await mu({cmd:e,stdin:new TextEncoder().encode(t),stdout:"ignore",stderr:"ignore",timeout:vu}).exited}catch{return 1}}async function uu(e){try{let t=mu({cmd:e,stdout:"pipe",stderr:"ignore",timeout:vu}),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 Kg(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 Xa={now:()=>new Date,isoNow:()=>new Date().toISOString(),unixMs:()=>Date.now()};var yu=300000,AE=3000000,LE=3,DE=200,Qa="https://www.googleapis.com/auth/cloud-platform",Vg="https://www.googleapis.com/auth/userinfo.email";function qg(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}=xg(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 Za(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=yu,r=Date.now()){if(e.accessToken==="")return!1;return e.expiresAt.getTime()-r>=t}function $E(e,t=yu,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=Xa){this.thresholdMs=e>0?e:yu,this.providers=[...t],this.bus=r,this.store=n??Ya(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=qg(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=qg(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 Kg(()=>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 qi({scopes:r.length>0?r:[Qa]}).getClient(),s=new EE({sourceClient:i,targetPrincipal:n,targetScopes:r.length>0?r:[Qa],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:Vi(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 qi({scopes:[Qa]}).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:Vi(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 qi({scopes:r.length>0?r:[Vg]}).getIdTokenClient(t)).idTokenProvider.fetchIdToken(t);return{accessToken:s,tokenType:"Bearer",expiresAt:Vi(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 qi({scopes:r.length>0?r:[Vg]}).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:Vi(s),audience:t,identity:e,scopes:r}}async function zE(e,t,r){try{qo();let o=await new qi({scopes:r.length>0?r:[Qa]}).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 Vi(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 Vi(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 Yi=4000,QE=8000;function Yo(e){let t=e?.tokens,r=()=>(t??=new ao(60000,_n(),void 0,Ya("file")),t);return{detectGoogle:io,hasCommand:du,portAvailable:jo,hasLocalAdc:Gi,adcQuotaProject:lu,mintToken:async(n,o,i)=>{await r().get(n,o,[],i)},probeServiceUsage:async(n,o)=>{let i=await Ji(r().get("user","",[]),Yi),s=`https://serviceusage.googleapis.com/v1/projects/${n}/services/${o}`,a=await fetch(s,{headers:{Authorization:`Bearer ${i.accessToken}`},signal:AbortSignal.timeout(Yi)});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 ec=Yo();function tc(e=Yo()){return{run:(t,r,n)=>ZE(t,e,r,n)}}async function ZE(e,t=ec,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??ec.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:Wi(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:Wi(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:Wi(e)?"error":"warn",message:"no Google project configured",hint:"set google.project_id in .devctl/config.yaml"});if(Wi(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 Ji((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 Yg(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 Yg(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??Gi;if(n===""||!o())return;let i=r.probeServiceUsage??ec.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)=>Ji(i(n,c.service),Yi)))).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??Gi)())return;let c=r.mintToken??ec.mintToken,l=o.map(async(u)=>{try{await Ji(c(`sa:${u}`,""),Yi),t({name:`Impersonate ${u}`,severity:"ok",message:"token minted"})}catch(f){let p=(r.adcQuotaProject??lu)(),m=n||"the ADC principal",h=p===""?"unknown":p,v=zg(`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=Za(u.auth);await Ji(c(f,u.auth.audience,p),Yi),t({name:`IAP ${u.name}`,severity:"ok",message:"id token minted"})}catch(p){t(zg(`IAP ${u.name}`,p,"check IAP OAuth client ID and ADC"))}});await Promise.all([...l,...d])}function zg(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 Ji(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 Yg(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 Jg(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
|
-
`}
|
|
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(
|
|
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
|
|
2179
|
-
`)}async function
|
|
2180
|
-
`).find((i)=>i.startsWith("n"));if(r&&r.length>1)return r.slice(1).trim();let n=await
|
|
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
|
|
2182
|
-
`);i=a.pop()??"";for(let c of a)r(t,c.replace(/\r$/,""))}if(i!=="")r(t,i)}var
|
|
2248
|
+
`}At();class nv{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 ov{stop;constructor(e){this.stop=e}execute(e){return this.stop(e.map((t)=>xu(t)))}}class iv{restart;constructor(e){this.restart=e}execute(e,t){return this.restart(e.map((r)=>xu(r)),t)}}class sv{start;constructor(e){this.start=e}execute(e,t){return this.start.execute({profile:e,client_env:t})}}class av{host;constructor(e){this.host=e}execute(){return this.host.startProxy()}}class cv{host;constructor(e){this.host=e}execute(){return this.host.stopProxy()}}class lv{host;constructor(e){this.host=e}execute(){return this.host.reload()}}class dv{host;constructor(e){this.host=e}execute(){return this.host.refreshIdentity({probeServiceAccounts:!0})}}class ac{runner;constructor(e){this.runner=e}execute(e,t,r){return this.runner.run(e,t,r)}}class cc{execute(e,t,r){return rc(e,t,r)}}class lc{execute(e,t){return nc(e,t)}}class uv{host;constructor(e){this.host=e}execute(){return this.host.snapshot()}}class dc{execute(e,t){return sc(e,t)}}function xu(e){return Xg(e)}function pv(e,t,r){let n=r??e,o=new nv((i)=>n.start(i));return{startService:o,stopService:new ov((i)=>n.stop(i)),restartService:new iv((i,s)=>n.restart(i,s)),startProfile:new sv(o),startProxy:new av(e),stopProxy:new cv(e),reloadConfig:new lv(e),refreshIdentity:new dv(e),runDoctor:new ac(t),getServiceStatus:new uv(e),getStartupPlan:new cc,getShutdownPlan:new lc,resolveStart:new dc}}import{createConnection as gT}from"net";var{spawn:vT}=globalThis.Bun;import{readdirSync as oT}from"fs";import{join as fv,resolve as wu,sep as Su}from"path";function iT(e){let t=fv(cr(),"state"),r;try{r=oT(t)}catch{return}let n=mv(wu(e)),o;for(let i of r){let s=nd(fv(t,i,"state.json"));if(!s)continue;let a=mv(wu(s.repo_root));if(!sT(a,n))continue;if(!o||a.length>o.length)o=a}return o}function uc(e,t,r=""){if(t!=="")return{repoRoot:wu(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 mv(e){let t=e.length>1&&e.endsWith(Su)?e.slice(0,-1):e;return process.platform==="win32"?t.toLowerCase():t}function sT(e,t){return t===e||t.startsWith(e.endsWith(Su)?e:`${e}${Su}`)}import{existsSync as gv,readFileSync as vv}from"fs";import{isAbsolute as aT,join as ku}from"path";import{parse as cT}from"dotenv";te();var Zi=["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=hv(e.repoRoot,e.profile);if(e.workDir!=="")Object.assign(t,hv(e.workDir,e.profile));return t}}}function pT(e){let t=e?.environment.sources??[];if(t.length===0)return[...Zi];let r=new Set([...dT,...t]),n=new Set(Zi),o=t.filter((s)=>!n.has(s)),i=[];for(let s of Zi){if(s==="defaults")i.push(...o);if(r.has(s))i.push(s)}return i}async function es(e,t){let r={},n=t.serviceCfg.working_dir;if(n!==""&&!aT(n))n=ku(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:Qi(t.profileEnv,t.cfg,i),dotenv:Qi(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:Qi(t.serviceCfg.environment.defaults,t.cfg,i),vars:Qi(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,Qi(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 Qi(e,t,r){if(!t||Object.keys(e).length===0)return e;return Zd(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=ku(kr(),"env",o);if(!gv(i))continue;try{r[o]=vv(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 hv(e,t){let r={},n=[".env",".env.development",".env.local"];if(t!=="")n.push(`.env.${t}`);for(let o of n){let i=ku(e,o);if(!gv(i))continue;try{let s=cT(vv(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 ts(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,yv=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 ${zi}`}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 Cu(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 bv{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(ph({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 xv(e,t){let r=Zs(e),n=Date.now()+t;return new Promise((o,i)=>{let s=()=>{let a=gT(r);a.once("connect",()=>{let c=new bv(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===zi,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 xv(e,wT)}catch{return}}function ET(e,t,r,n){return r?[e,...n]:[e,t,...n]}async function wv(e,t){let r=await uo(e);if(r)return r;Pm(e);let n=_i(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`)))}),xv(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 pc{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()},yv)}async execService(e,t,r=!1){return await this.call("exec",{service:e,command:t,print_env:r,client_env:Xo()},yv)}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 Cu(this.client,e),this.client.call(e,t,r)}}function rs(e){let t=e&&PT(e.compat);if(t)process.stderr.write(`warning: ${t}
|
|
2251
|
+
`)}async function Sv(e,t,r=""){let n=uc(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 rs(o),{repoRoot:n.repoRoot,client:o}}async function kv(e,t,r,n){let o=n?.allowMissingConfig===!0?to(e,t):nn(e,t),i=new pc(o);if(!r)return i.client=await uo(o.repoRoot),rs(i.client),i;return i.client=await wv(o.repoRoot,o.configPath),rs(i.client),i}async function Cv(e){let t=new pc(Yn());return t.client=e,rs(t.client),t.cfg=await t.configSnapshot(),t}async function Pv(e,t){let r=uc(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 Cv(n)}async function _v(e,t){let r=uc(e,"",t),n=r?await uo(r.repoRoot):void 0;if(n)return Cv(n);let o=nn(e,t),i=new pc(o),s=fn(o.repoRoot);if(i.client=await wv(o.repoRoot,o.configPath),rs(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?"":Iv(process.argv[1]??"");return Av({scriptPath:t,execPath:Iv(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 Dv(t);let o=((await r.json()).tag_name??"").replace(/^v/,"");return{current:jt,latest:o,newer:o!==""&&Lv(o,jt)>0,hint:t.hint,kind:t.kind,command:t.command}}catch{return Dv(t)}}async function FT(e,t=Mv){return t(e)}async function Mv(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 Ov(e=fetch){return{check:()=>$T(e),apply:(t,r)=>r===!0?Mv(t,!0):FT(t)}}function Dv(e){return{current:jt,latest:"",newer:!1,hint:e.hint,kind:e.kind,command:e.command}}function Iv(e){if(e==="")return"";try{return IT(e)}catch{return e}}function $v(e){let t=e?.tokens??new ao(60000,_n(),void 0),r=e?.doctorHost??Yo({tokens:t}),n=Ov(),o={loadTuiConfig:Do,saveTuiPreferences:Dm,resolveTuiOverridePath:sa,userTuiConfigPath:ia,listSessions:ih,loadSessionEvents:sh,loadPath:Ui,validateConfigText:Bi,discover:Qn,configDiff:Pn,openTui:_v,findDaemon:Sv,tryDial:uo,assertMethodAllowed:Cu,readPersistedState:fn,bootstrapLogPath:_i,exportsDir:Qs,resolveExportPath:nh,writeLogExport:Ed,openInFileManager:oh,freePort:xa,createStarterConfig:kh,runSetup:(i,s,a)=>Ch(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:Fg,logoutGoogle:Ng,refreshUserToken:async(i="user")=>t.refresh(i,"",[]),runDoctor:new ac(e?.doctorRunner??tc(r)),getStartupPlan:new cc,getShutdownPlan:new lc,resolveStart:new dc,formatDoctor:Jg,openController:kv,openAttach:Pv,checkUpdate:()=>n.check(),applyUpdate:(i,s)=>n.apply(i,s),formatUpdateStatus:Tv};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 Fv(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:Xv}=globalThis.Bun;var{spawn:JT}=globalThis.Bun;async function Nv(e,t){if(e<=0)return;try{process.kill(-e,t);return}catch{try{process.kill(e,t)}catch{return}}}async function Hv(e){if(!Qt(e))return;let t=await ns(["ps","-p",String(e),"-o","command="]),r=Date.now(),n=XT(await ns(["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 ns(["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 ns(["pwdx",String(e)]),o=n.indexOf(":");if(o>=0)return n.slice(o+1).trim();return""}async function ns(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 jv(e){let t=new Map;if(e.length===0)return t;let r=await ns(["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 Bv(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:Uv}=globalThis.Bun;var ZT=1024,e0=3000;async function Wv(e,t){if(e<=0)return;try{await Uv({cmd:["taskkill","/PID",String(e),"/T","/F"],stdout:"ignore",stderr:"ignore"}).exited}catch{return}}async function Gv(e){let t=t0(od(e),e);if(t)return t;if(!Qt(e))return;let r=await Pu(["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 Pu(["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:hc(n),startTime:r.StartTime}}catch{let r=mc(t,"Path");if(r==="")return;return{command:r,cwd:hc(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=hc(o);return{command:n,cwd:i,startTime:r.CreationDate||void 0}}catch{return{command:mc(t,"CommandLine")||mc(t,"ExecutablePath"),cwd:hc(mc(t,"ExecutablePath"))}}}async function Kv(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 Pu(["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 hc(e){if(e==="")return"";let t=e.replace(/\//g,"\\"),r=t.lastIndexOf("\\");return r>0?t.slice(0,r):""}function mc(e,t){let r=`${t}=`,n=e.split(/\r?\n/).find((o)=>o.startsWith(r));return n?n.slice(r.length).trim():""}async function Pu(e){let t,r=setTimeout(()=>{try{t?.kill()}catch{}},e0);try{t=Uv({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:_u}=globalThis.Bun;async function Vv(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 Jv(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 zv(e){return Object.fromEntries(Object.entries(e).filter(([t])=>!s0.has(t)))}async function Yv(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 Jv(e,t,i,r,n)}function Jv(e,t,r,n,o){let i=!0,s=_u({cmd:[e,"logs","--follow",r],stdout:"pipe",stderr:"pipe",stdin:"ignore"});qv(s.stdout,"stdout",n),qv(s.stderr,"stderr",n);let a=_u({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=_u({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 qv(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 Ru{isRunning(e){let t=this.running.get(e);return t!==void 0&&os(t)}running=new Map;async start(e){let t=this.running.get(e.name);if(t&&os(t))return t;if(this.running.delete(e.name),e.args.length===0)throw E(He,"empty command");let r=e.shell?ey(e.args):e.args,n;try{n=Xv({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 gc(n.stdout,"stdout",e.onLine),gc(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=Xv({cmd:e.shell?ey(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=[
|
|
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=[gc(t.stdout,"stdout",o),gc(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&&os(t))return t;let r=await Vv({...e,env:zv(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&&os(t))return t;let r=await Yv(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&&os(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 Qv(r.pid,"SIGTERM"),!await ry(r.done,n)){if(await Qv(r.pid,"SIGKILL"),!await ry(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 os(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 Qv(e,t){if(process.platform==="win32"){await Wv(e,t);return}await Nv(e,t)}async function ny(e){if(process.platform==="win32")return Gv(e);return Hv(e)}async function oy(e){if(process.platform==="win32")return Kv(e);return jv(e)}var u0=2000;function is(e,t){if(e.workDir!==""&&t.cwd!==""&&ty(e.workDir)!==ty(t.cwd))return!1;if(!Bv(e.args,t.command))return!1;let r=Zv(e.startTime),n=Zv(t.startTime);if(r!==void 0&&n!==void 0)return Math.abs(r-n)<=u0;return!0}function Zv(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 ey(e){if(process.platform==="win32")return["cmd.exe","/c",e.join(" ")];return["/bin/sh","-c",e.join(" ")]}function ty(e){return e.replace(/\\/g,"/").replace(/\/+$/,"")}async function p0(e){while(Qt(e))await f0(d0);return 0}async function gc(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 ry(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 iy={exists:(e)=>m0(e),readText:(e)=>h0(e,"utf8"),writeText:(e,t)=>g0(e,t)};ht();var Eu=3,v0=3,y0=10,ss={shouldRestart(e){if(!e.enabled||e.policy===wa)return!1;if(e.retryCount>=e.maxRetries)return!1;if(e.policy===ka)return!0;return e.policy===Sa&&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:Eu}}},Tu={shouldRestartUnhealthy(e,t=v0){return e>=t},shouldResetRestartBudget(e,t=y0){return e>=t}},vc={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 Au{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(aa,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?ss.shouldRestart(ss.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&&sy(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||sy(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(Om,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(!Tu.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:Eu;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(Tu.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 sy(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 Lu=[_r,dt,fr,je,Et],C0={[Zg]:[Et,on,je,dt,fr,_r],[Et]:[on,je,dt],[on]:[je,dt,Et,_r],[je]:Lu,[pr]:Lu,[Br]:Lu,[_r]:[Et,dt],[dt]:[on,Et,je],[fr]:[on,je,dt,Et]};function as(e,t){if(e===t)return!0;return(C0[e]??[]).includes(t)}function ay(e,t){if(!as(e,t))throw E(Y,`illegal service lifecycle transition ${e} \u2192 ${t}`);return t}At();var P0=100,cy=30000;class Du{processes;clock;session;health;constructor(e,t){this.processes=e;this.clock=t;this.health=new Au(()=>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 rc(e,t,r)}planStop(e,t,r=!1){return r?oc(e,t):nc(e,t)}applyLifecycle(e,t){return ay(e,t)}isLegalLifecycle(e,t){return as(e,t)}async start(e){let t=this.host();if(e.detach===!0)t.detached=!0;let r=sc(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=Lg(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(ca,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(aa,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:bh(i),captureStderr:xh(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(Im,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]),vc.waitForHealthy(i)){let p=vc.timeoutMs(i,cy);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=vc.timeoutMs(n,cy);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 cs(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 yc(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=ts(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 es(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:cs(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 ly(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 dy(e,t){let r=e.cfg.services[t];if(e.orchestrator.serviceIsActive(t)){if(r&&!e.ports.has(t)){let a=await ba(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}),bc(e,t,xc(e,fn(e.cfg.repoRoot)?.processes.find((p)=>p.name===t))),e.setState(t,je,Tt,0,"");let f=await yc(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 ba(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!==""&&is({args:[...r.command.args],workDir:e.serviceWorkDir(r),startTime:new Date(a.startTime)},c)){e.ports.set(t,n);let d=ly(e,t,s,[...r.command.args],e.serviceWorkDir(r),new Date(a.startTime))??e.orchestrator.health.bumpGeneration(t);bc(e,t,xc(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 yc(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 uy(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}),bc(e,n,xc(e,i)),e.setState(n,je,Tt,0,"");let d=await yc(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!==""&&is({args:n.command,workDir:n.cwd,startTime:n.startTime?new Date(n.startTime):void 0},o))){if(Object.values(n.ports).length>0&&await ba({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=ly(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);bc(e,n.name,xc(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 yc(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(Nm,"",{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 bc(e,t,r){e.serviceProfile.set(t,r),e.serviceProfileEnv.set(t,lo(e.cfg,r))}function xc(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 py(e,t){return t}function Iu(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 vy,request as E0}from"http";import{request as T0}from"https";import{randomBytes as fy}from"crypto";import{Readable as A0}from"stream";te();jr();var ls="x-devctl-request-id",L0="x-devctl-internal-token",my=100,Mu=new Set(["connection","keep-alive","proxy-authenticate","proxy-authorization","te","trailers","transfer-encoding","upgrade"]),D0=new Set(["gzip","deflate","br"]);class Ou{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>my)this.recentRequests.splice(0,this.recentRequests.length-my)}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=vy((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($m,"",{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(Fm,"",{})),t()})})}async serveUpgrade(e,t,r){let n=Date.now(),o=e.headers[ls]?.toString()||fy(8).toString("hex"),i=e.method??"GET",s=e.url??"/",a=this.detector?this.detector.redactText(s):s,c=gy(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=
|
|
2190
|
-
`,
|
|
2191
|
-
`}`).join("");if(t.write(`${k}${
|
|
2192
|
-
`),r.length>0)y.write(r);if(
|
|
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(Mu.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[ls]=o,this.middleware.length===0)await wc(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 _=hy(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")}),
|
|
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",()=>
|
|
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[ls]?.toString()||fy(8).toString("hex");t.setHeader(ls,n);let o=e.method??"GET",i=e.url??"/",s=this.detector?this.detector.redactText(i):i,a=gy(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(Mu.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[ls]=n,this.middleware.length===0)await wc(a,f,this.tokens);for(let y of this.middleware)await y.apply({route:a,headers:f,tokens:this.tokens});let v=hy(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(Mu.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 wc(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,[],Za(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 hy(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 gy(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 $u{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=vy((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 yy=1;class by{tokenProviders=[];identityProviders=[];pluginIdentityProviders=[];environmentSources=[];healthChecks=[];logParsers=[];proxyMiddleware=[];loadErrors=[];pluginPaths=[];registerBuiltins(){this.tokenProviders.push(..._n()),this.identityProviders.push(iu(),su()),this.logParsers.push(Um()),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 Sc(e,t=process.cwd()){let r=new by;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!==yy)throw Error(`plugin SDK version ${String(s.sdkVersion??"missing")} is incompatible; expected ${yy}`);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 wc(e.route,e.headers,e.tokens)}}}function po(e,t){if(!t)return;e.push(...t)}var V0=200;function Fu(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 Nu(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=Nu(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 Sc(n,t.repoRoot);e.registry=a;for(let c of a.loadErrors)e.log("devctl","ERROR",`plugin ${c.path} skipped: ${c.message}`);Fu(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 Hu(e,t){let r=tu(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 ju(e,t){let r=ru(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 Bu(e,t){let r=new Set(Zi),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,Xi(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 Uu(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 xy(e){let t;try{t=nn(e.cfg.repoRoot,e.cfg.configPath)}catch(l){throw e.bus.publish(Se(Ei,"",{error:L(l)})),e.log("devctl","ERROR",`configuration reload failed: ${L(l)}`),l}try{Hu(e.registry,t),ju(e.registry,t),Bu(e.registry,t)}catch(l){throw e.bus.publish(Se(Ei,"",{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)Uu(e)}let r=Iu(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=py(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(la,"",{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",ic(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 Sy,relative as eA}from"path";ht();function wy(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:Oi).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 Wu{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=Sy(n,i);try{let a=Z0(s,{recursive:!0},(c,l)=>{let d=l?Sy(s,l.toString()):s,u=eA(n,d).replaceAll("\\","/");if(!wy(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 Gu,join as Ku}from"path";class qu{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!==""&&!Gu(e.working_dir))return Ku(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=ts(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 es(s.repoRoot,{service:e,profile:r,serviceCfg:t,profileEnv:n,assignedPorts:a,runtime:d,cfg:s,fetchSecret:cs(async()=>(await this.deps.tokens.get("user","",[])).accessToken),pluginSources:this.deps.environmentSources(),clientEnv:o,includeProcess:i}),f=t.working_dir&&!Gu(t.working_dir)?Ku(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 es(n.repoRoot,{service:`task:${e}`,profile:this.profile,serviceCfg:t,profileEnv:this.profileEnv,assignedPorts:{},runtime:ts(`task:${e}`,"127.0.0.1",{},"",n.project.name),cfg:n,clientEnv:r,fetchSecret:cs(async()=>(await this.deps.tokens.get("user","",[])).accessToken),pluginSources:this.deps.environmentSources()}),i=t.working_dir&&!Gu(t.working_dir)?Ku(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 Vu=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=kc(e,"free")+kc(e,"inactive")+kc(e,"speculative")+kc(e,"purgeable");return Math.round(n*r/Vu)}function kc(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 ky(){let e=Math.max(0,Math.round(nA()/Vu)),t=Math.max(0,Math.round(rA()/Vu)),r=Math.max(0,Math.min(e,lA()??t));return{totalKB:e,unusedKB:t,leftoverKB:r}}function Cy(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 zu(){let e=uA(),t=ky();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 Yu(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 Py(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,...Yu(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:zu(),stats_series:e.statsSeries}}function _y(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
|
|
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>=
|
|
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 Ju{cache;accounts=new Map;entries=[];deps;constructor(e){this.deps=e,this.cache=Cy(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=Ka(t.identity);if(r.kind!=="none")try{if(r=await Dg(t.identity,()=>$g(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}=Yu(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();Qu();var wA=600,SA=65535;async function Dy(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 Zu{listener;disabled=[];token;deps;constructor(e){this.deps=e,this.token=Em(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 Dy(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 ep{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 Ou(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 $u(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,Iy=5000,My=60;class tp{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:Iy,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 oy(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<Iy)return;this.lastAt=e;let t=zu(),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>My)this.cpu.shift();if(this.mem.length>My)this.mem.shift()}}function rp(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 ds(e){if(!rp(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 np{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=uh(n);t({id:r.id,error:o.error,kind:o.kind,hint:o.hint,service:o.service})}}}te();jr();At();class op{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 ep({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 qu({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 Zu({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 tp({clock:this.clock,runtimes:()=>this.runtimes}),this.identity=new Ju({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 np({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,Xi(r));this.orchestrator.bind(this.lifecycleSession()),this.commands=t.createCommands(this),this.serviceWatchers=new Wu({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=Zs(this.cfg.repoRoot);this.lock=this.acquireLockFn(this.cfg.repoRoot,e),this.rpc.removeStaleSocket(e),this.registry=await Sc(this.cfg.plugins.map((t)=>t.path),this.cfg.repoRoot),this.pluginMtimes=Nu(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}`);Fu(this.reloadHost()),Hu(this.registry,this.cfg),ju(this.registry,this.cfg),Bu(this.registry,this.cfg),await this.recoverSession(),this.serviceWatchers.sync(this.cfg.services),Uu(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=rp(t)?t:{};switch(e){case"ping":return{session:this.sessionID,version:jt,protocol:zi};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:ds(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:ds(r.client_env)}),null;case"run_task":return this.runTask(typeof r.name==="string"?r.name:"",ds(r.client_env)??{});case"exec":return this.execService(typeof r.service==="string"?r.service:"",En(r.command),ds(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:Ea(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 mh(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 dy(this.recoverHost(),e)}async recoverSession(){return uy(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)=>Bi(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 xy(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 Py(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 _y(this.snapshot())}setState(e,t,r,n,o){let i=this.runtimes.get(e)??Xi(e);if(!as(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(Mm,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!==""&&is({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",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(ca,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})}_m(this.cfg.repoRoot,{session_id:this.sessionID,repo_root:this.cfg.repoRoot,profile:this.profile,processes:e})}}var $y=500,_A=1e4,RA=2000,EA=new URL("./log-worker.ts",import.meta.url);class Fy{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=$y){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 Oy(e,t,r){return rh(new Rd(e.max,t,r,e.persist,e.directory,e.sessionID,e.retentionDays,e.maxSessionLogs))}async function Ny(e,t,r,n={}){if(n.standalone??Bun.isStandaloneExecutable===!0)return{logs:Oy(e,t,r),usingWorker:!1};try{let i=new Fy(e,t,{script:n.script});return await i.waitUntilReady(n.initTimeoutMs??$y),{logs:i,usingWorker:!0}}catch{return{logs:Oy(e,t,r),usingWorker:!1}}}ni();import{createServer as tL}from"http";te();ps();var rL="2025-03-26",ab="2.0",nb=-32700,ob=-32600,nL=-32601,ib=-32602,oL=-32603;function iL(e){return e!==""&&Cn(e)}class cp{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,hs()),t.end();return}if(!this.authorized(e)){this.emit("WARN",`rejected unauthorized request from ${sb(e)}`),oi(t,401,{error:"unauthorized"});return}if(aL(e)!=="/mcp"){oi(t,404,{error:"not found"});return}if(e.method==="GET"||e.method==="DELETE"){t.writeHead(405,{Allow:"POST, OPTIONS",...hs()}),t.end();return}if(e.method!=="POST"){t.writeHead(405,{Allow:"POST, OPTIONS",...hs()}),t.end();return}let n;try{n=await cL(e)}catch(a){oi(t,400,fs(null,nb,a instanceof Error?a.message:"invalid body"));return}let o;try{o=JSON.parse(n)}catch{oi(t,400,fs(null,nb,"parse error"));return}let i=sb(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,hs()),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(!Ec(e))return fs(null,ob,"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:fs(n,ob,"missing method");try{let i=await this.handleMethod(r.method,Ec(r.params)?r.params:{},t);if(o)return;return{jsonrpc:ab,id:n,result:i}}catch(i){if(o)return;let s=i instanceof ms?i.code:oL,a=i instanceof Error?i.message:"internal error";return this.emit("WARN",`rpc error method=${r.method} addr=${t}: ${a}`),fs(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:Yy(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:sp.map((n)=>({uri:n,name:n.replace("devctl://",""),mimeType:"application/json"}))};case"resources/read":return this.readResource(t);default:throw new ms(nL,`method not found: ${e}`)}}async callTool(e){let t=typeof e.name==="string"?e.name:"";if(t==="")throw new ms(ib,"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 eb(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(!tb(t))throw new ms(ib,`unknown resource ${t}`);let r=await rb(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 sb(e){return e.socket.remoteAddress??"unknown"}class ms extends Error{code;constructor(e,t){super(t);this.code=e}}function hs(){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),...hs()}),e.end(n)}function fs(e,t,r){return{jsonrpc:ab,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)}ps();var uL=(e)=>new cp({host:"127.0.0.1",...e});async function pL(e,t={}){let r=t.clock??Xa,n=t.fs??iy,o=t.processes??new Ru,i=t.bus??new ud(2048),s=t.tokens??new ao(e.auth.refresh_threshold_seconds*1000,_n(),i,void 0,r),a=new Du(o,r),c=rd(),l=new mr(e.secrets.extra_markers,e.secrets.extra_patterns),d=Bun.isStandaloneExecutable===!0,{logs:u,usingWorker:f}=await Ny({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 op(e,{healthCheckers:t.healthCheckers??Fv([]),detectGoogle:t.detectGoogle??io,tokens:s,inspectProcess:ny,processAlive:Qt,acquireLock:Rm,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)=>pv(m,tc(Yo({tokens:s})),a)}),orchestrator:a,clock:r,fs:n}}async function cb(e,t){let r=to(e,t),{supervisor:n}=await pL(r),o=!1,i=()=>{if(o)return;o=!0,n.shutdown(Ea(r.shutdown)).finally(()=>process.exit(0))};process.on("SIGINT",i),process.on("SIGTERM",i),await n.run()}ht();te();jf();import{Command as GO}from"commander";var Yk=["zsh","bash","fish"];function Jk(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 ${
|
|
2229
|
-
`);return}if(
|
|
2230
|
-
`),
|
|
2231
|
-
`),i.projectID!=="")
|
|
2232
|
-
`);
|
|
2233
|
-
`),
|
|
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("",
|
|
2235
|
-
`);return}if(
|
|
2236
|
-
`),s)for(let[a,c]of Object.entries(s.service_account_status).sort(([l],[d])=>l.localeCompare(d)))
|
|
2237
|
-
`)}finally{await o.close()}})}te();
|
|
2238
|
-
`);else
|
|
2239
|
-
`);if(n.supervisor_restart_required&&n.supervisor_restart_required.length>0)
|
|
2240
|
-
`)}finally{await r.close()}})}function
|
|
2300
|
+
`;throw Error(`unsupported shell "${e}"; use ${Yk.join(", ")}`)}function Bf(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([...Yk].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 Xk(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();mf();import{mkdirSync as $O}from"fs";import{basename as FO,dirname as NO}from"path";import{stringify as HO}from"yaml";Ia();function Qk(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: ${ic(n.supervisor_restart_required)}
|
|
2312
|
+
`)}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("",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
|
-
`));
|
|
2243
|
-
`)}catch(o){if(n.json){
|
|
2244
|
-
`);return}throw o}}),r.command("diff").description("show where effective configuration values came from").option("--json").action((n)=>{let o=t.load("",
|
|
2245
|
-
`);return}for(let s of i){
|
|
2246
|
-
`),
|
|
2247
|
-
`);for(let a of s.shadowed)
|
|
2248
|
-
`)}}),r.command("show").option("--json").action((n)=>{let o=t.load("",
|
|
2249
|
-
`);return}
|
|
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){
|
|
2253
|
-
`);return}if(t.fileExists(a))throw Error(`configuration already exists at ${a}; not overwriting`)
|
|
2254
|
-
`)})}ni();
|
|
2255
|
-
`);else for(let c of Object.keys(a).sort())
|
|
2256
|
-
`)}else if(o.json)
|
|
2257
|
-
`);else{if(s.stdout)
|
|
2258
|
-
`);else{if(i.stdout)
|
|
2259
|
-
`);return}if(
|
|
2260
|
-
`)}finally{await o.close()}})}function
|
|
2261
|
-
`)}finally{await o.close()}})}function
|
|
2262
|
-
`)}finally{await o.close()}})}async function
|
|
2263
|
-
`);return}if(
|
|
2264
|
-
`),s&&s.processes.length>0){
|
|
2265
|
-
`);for(let a of s.processes)
|
|
2266
|
-
`)}return}e.assertMethodAllowed(o,"status");let i=await o.call("status",null);if(r.json){
|
|
2267
|
-
`);return}
|
|
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 tC(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 rC(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 nC(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(rv(i)),n.detach)T(`detached; services continue running
|
|
2332
|
+
`)}finally{await o.close()}})}function oC(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 iC(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 eC(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))
|
|
2271
|
-
`);
|
|
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
|
-
`),
|
|
2274
|
-
`),
|
|
2275
|
-
`),
|
|
2276
|
-
`)}finally{o?.close()}}var
|
|
2277
|
-
`),await
|
|
2278
|
-
`);try{await
|
|
2279
|
-
`);return}let i=await
|
|
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 sC(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 eC(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 eC(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 aC(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
|
|
2282
|
-
`}async function
|
|
2283
|
-
`:
|
|
2284
|
-
`);return}if(o.json){
|
|
2285
|
-
`);return}for(let l of c)
|
|
2286
|
-
`);return}for(let c of a.events)
|
|
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("",
|
|
2288
|
-
`)}finally{await i.close()}})}function
|
|
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
|
|
2290
|
-
`);return}let i=await o.status();if(n.json){
|
|
2291
|
-
`);return}
|
|
2292
|
-
`);for(let s of i.proxy.routes??[]){let a=s.match?` match=${s.match}`:"",c=s.client_id?` client_id=${s.client_id}`:"";
|
|
2293
|
-
`)}}finally{await o.close()}}),r.command("start").action(async()=>{let n=await t.openController("",
|
|
2294
|
-
`);return}if(
|
|
2295
|
-
|
|
2296
|
-
`),i?.setup_mode===!0)
|
|
2297
|
-
`),
|
|
2298
|
-
`),
|
|
2299
|
-
|
|
2300
|
-
`);
|
|
2301
|
-
`);return}if(
|
|
2302
|
-
`),
|
|
2303
|
-
`),
|
|
2304
|
-
`),!n.newer){if(n.latest!=="")
|
|
2305
|
-
`);return}if(
|
|
2306
|
-
`),r.check||!n.command)return;if(
|
|
2307
|
-
`),(await t.applyUpdate(n.command,!0)).code!==0){process.exitCode=
|
|
2308
|
-
`)})}function
|
|
2309
|
-
`)}),r.action(async()=>{let n=r.opts();await
|
|
2310
|
-
`);else
|
|
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 cC(e,t){e.command("attach").action(async()=>{let r=await t.openAttach("",xe(e)),{runTuiWithController:n}=await Promise.resolve().then(() => (jf(),zk));await n(t,r)})}import{setTimeout as lC}from"timers/promises";function Uf(e){return`${e.timestamp.slice(11,19)} ${e.service.padEnd(10)} ${String(e.level).padEnd(6)} ${e.message}
|
|
2354
|
+
`}async function dC(e,t,r,n=1000){let o=await e(void 0);o.events.forEach(t);let i=o.nextCursor;while(!r.aborted){try{await lC(n,void 0,{signal:r})}catch{return}o=await e(i),o.events.forEach(t),i=o.nextCursor}}function uC(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 dC((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
|
+
`:Uf(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(Uf(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(Uf(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 pC(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 lC(500,void 0,{signal:a.signal})}catch{break}s()}}finally{process.off("SIGINT",c),process.off("SIGTERM",c)}})}yl();function fC(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 mC(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??fi(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(zp(c,l)),cursor:JSON.parse(Yp(c,l)),kilo:JSON.parse(Jp(c,l)),codex:Xp(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(dw(c,l))}finally{await o.close()}})}te();function hC(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=ch;return}T(`updated to ${n.latest}; ${fc}
|
|
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 Nf(e,n.config??"")}),nC(r,e),oC(r,e),iC(r,e),rC(r,e),tC(r,e),sC(r,e),aC(r,e),uC(r,e),pC(r,e),qO(r,e),VO(r,e),Xk(r,e),fC(r,e),mC(r,e),Zk(r,e),Qk(r,e),cC(r,e),zO(r,e),hC(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(Jk(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(Bf(o,i).join(`
|
|
2311
2383
|
`)+`
|
|
2312
|
-
`)}catch{
|
|
2384
|
+
`)}catch{T(Bf(o,Yn()).join(`
|
|
2313
2385
|
`)+`
|
|
2314
|
-
`)}})}function
|
|
2315
|
-
`),process.exit(
|
|
2386
|
+
`)}})}function YO(e,t){e.command("_supervisor").option("--repo <path>","repository root").action(async(r)=>{await t(r.repo??"",xe(e))})}async function gC(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(dh(r))}}Ys();await gC($v(),cb);
|