@amr-m-abdelgawad/devctl 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/devctl.js +91 -91
- 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 ZC=Object.defineProperty;var eP=(e)=>e;function tP(e,t){this[e]=eP.bind(null,t)}var rP=(e,t)=>{for(var r in t)ZC(e,r,{get:t[r],enumerable:!0,configurable:!0,set:tP.bind(t,r)})};var I=(e,t)=>()=>(e&&(t=e(e=0)),t);var nP=import.meta.require;function wm(e,t=""){if(t==="MetadataLookupWarning")return!0;return e.includes("MetadataLookupWarning")||e.startsWith("received unexpected error =")}function Js(){if(xm)return;xm=!0;let e=process.emitWarning.bind(process);process.emitWarning=(t,...r)=>{let n=iP(r[0]),o=oP(t);if(wm(o,n))return;e(t,...r)}}function Sm(){let e=process.stderr.write.bind(process.stderr);return process.stderr.write=(t,r,n)=>{let o=sP(t);if(wm(o)){let i=typeof r==="function"?r:typeof n==="function"?n:void 0;if(typeof i==="function")i();return!0}if(typeof r==="function")return e(t,r);return e(t,r,n)},()=>{process.stderr.write=e}}function oP(e){if(typeof e==="string")return e;if(e instanceof Error)return e.message;return String(e)}function iP(e){if(typeof e==="string")return e;if(e&&typeof e==="object"&&"type"in e&&typeof e.type==="string")return e.type;return""}function sP(e){if(typeof e==="string")return e;if(e instanceof Uint8Array)return new TextDecoder().decode(e);return""}var xm=!1;import{createHash as aP}from"crypto";import{resolve as cP}from"path";function ar(e){let t=cP(e),r=process.platform==="win32"?t.toLowerCase():t;return aP("sha256").update(r).digest("hex").slice(0,lP)}var lP=16;var Xs=()=>{};function Jl(e){let t=e.indexOf("Z-"),r=t>=0?e.slice(0,t+1):e,n=r.indexOf("T");if(n<0||!r.endsWith("Z"))return;let o=r.slice(0,n),i=r.slice(n+1,-1).replace(/-/g,":"),s=new Date(`${o}T${i}Z`);return Number.isNaN(s.getTime())?void 0:s}function ad(e=process.platform){return e==="darwin"}function EP(e=process.platform){return ad(e)?"cmd":"ctrl"}function Ao(e,t=process.platform){return`${EP(t)}+${e}`}function TP(e=process.platform){return ad(e)?"command":"ctrl"}function It(e,t=process.platform){return`${TP(t)}+${e}`}function Ei(e){return e.replace(/\bcmd\b/gi,"command")}function Or(e,t=process.platform){let r=ad(t),n=Boolean(e.meta||e.super),o=Boolean(e.ctrl);return r?n&&!o:o&&!n}function Zr(e=process.platform){return Ao("c",e)}function AP(e=process.platform){return{leader:Ao("x",e),command_list:Ao("p",e),command:"/",help:"?",search:"f",quit:"q",services:"s",logs:"l",auth:"a",proxy:"p",doctor:"d",config:"c",setup:"u",refresh:"r",restart:"R",find:"f",fullscreen:"z",confirm:"enter",cancel:"esc",select:"space",interrupt:"escape",copy:Zr(e)}}function Lm(){return{theme:"devctl",leader_timeout:2000,font_size:14,keybinds:{...LP},scroll_speed:3,scroll_acceleration:{enabled:!1},diff_style:"auto",cursor:{style:"block",blinking:!0},mouse:!0,attention:{enabled:!1,notifications:!0,sound:!0,volume:0.4,sound_pack:"default",sounds:{}},log_timestamps:!0,log_metadata:!0,mcp_enabled:!1}}function DP(e){if(e===""||e==="none")return[];return e.split(",").flatMap((t)=>{let r=t.trim();if(r===""||r==="none"||/\s/.test(r))return[];let n=r.toLowerCase().split("+"),o=n[n.length-1]??"";return[{name:o==="return"?"return":o,ctrl:n.includes("ctrl")||n.includes("control"),shift:n.includes("shift"),meta:n.includes("meta")||n.includes("cmd")||n.includes("super"),alt:n.includes("alt")||n.includes("option")}]})}function hn(e,t){return DP(t).some((n)=>(e.name??"").toLowerCase()===n.name&&Boolean(e.ctrl)===n.ctrl&&Boolean(e.shift)===n.shift&&Boolean(e.meta||e.super)===n.meta&&Boolean(e.option??e.alt)===n.alt)}var sd=2000,Lo=14,Am="DEVCTL_TUI_CONFIG",LP;var ta=I(()=>{LP=AP()});function Se(e,t,r){return{type:e,timestamp:new Date().toISOString(),service:t,payload:r}}class ud{subs=new Map;nextID=0;buffer=[];maxBuf;constructor(e){this.maxBuf=e>0?e:1024}subscribe(e,t=[]){this.nextID+=1;let r=this.nextID;return this.subs.set(r,{types:new Set(t),handler:e}),()=>{this.subs.delete(r)}}publish(e){let t=e.timestamp===""?{...e,timestamp:new Date().toISOString()}:e;if(this.buffer.length>=this.maxBuf)this.buffer=this.buffer.slice(1);this.buffer.push(t);for(let r of this.subs.values())if(r.types.size===0||r.types.has(t.type))r.handler(t)}recent(){return[...this.buffer]}}var Mm="ServiceStarted",ca="ServiceStopped",la="ServiceFailed",Om="ServiceStateChanged",$m="ServiceHealthChanged",Io="LogReceived",Mo="TokenRefreshed",Gn="TokenRefreshFailed",ld="AuthenticationChanged",Fm="ProxyStarted",Nm="ProxyStopped",dd="ProxyRequest",da="ConfigurationChanged",Ti="ConfigurationReloadFailed",Hm="SessionRecovered";var jm=5000,Bm=500,pd=16384,Um=65536,ua="TRACE",pa="DEBUG",fa="INFO",Ai="WARN",Kn="ERROR",Oo="FATAL",ma="UNKNOWN",fd;var Li=I(()=>{fd={["TRACE"]:0,["DEBUG"]:1,["INFO"]:2,["WARN"]:3,["ERROR"]:4,["FATAL"]:5,["UNKNOWN"]:2}});function Wm(){return{name:"default",parse:(e)=>{let t=vd(e);if(t)return t;return{level:yd(e),request_id:bd(e)||void 0}}}}function Di(e,t){for(let r of t){let n=e[r];if(typeof n==="string"&&n.trim()!=="")return n}return}function Km(e){for(let t of jP){let r=e[t];if(typeof r==="number"){let n=YP[r];if(n)return n;continue}if(typeof r==="string"&&r.trim()!=="")return r.toUpperCase()}return}function Ii(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}function Zt(e){if(typeof e==="string"&&e.trim()!=="")return e;if(typeof e==="number"&&Number.isFinite(e))return String(e);return}function $o(e){if(typeof e==="number"&&Number.isFinite(e))return e;if(typeof e==="string"&&e.trim()!==""){let t=Number(e);if(Number.isFinite(t))return t}return}function $r(e,t){for(let r of t)if(e[r]!==void 0&&e[r]!==null)return e[r];return}function hd(e){return e.filter((t)=>t!==void 0&&t!=="").join(" ")}function JP(e){if(e>=UP)return Kn;if(e>=BP)return Ai;return}function XP(e){if(Ii(e))return e;return{}}function qm(e){let t=Zt($r(e,["http.method","method","request_method"])),r=Zt($r(e,["http.route","url.path","path","route","request_uri"])),n=Zt($r(e,["http.status_code","status","status_code"])),o=hd([t,r,n]);return o===""?void 0:o}function Vm(e){if(!Ii(e))return Zt(e);if(typeof e.stringValue==="string")return e.stringValue.trim()===""?void 0:e.stringValue;return Zt(e.intValue??e.doubleValue??e.boolValue)}function QP(e){if(typeof e==="string"&&e.trim()!=="")return e;return Vm(e)}function zm(e){if(!Array.isArray(e)||e.length===0)return!1;return e.every((t)=>Ii(t)&&typeof t.key==="string"&&t.value!==void 0)}function ZP(e){let t={};if(!zm(e))return t;for(let r of e){let n=r.key;if(typeof n==="string"&&n!==""){let o=Vm(r.value);if(o!==void 0)t[n]=o}}return t}function e_(e){let t=Km(e);if(t)return t;let r=$o(e.severityNumber);if(r===void 0)return;if(r>=zP)return Oo;if(r>=VP)return Kn;if(r>=qP)return Ai;if(r>=KP)return fa;if(r>=GP)return pa;if(r>=WP)return ua;return}function t_(e){if(e.timeUnixNano!==void 0||e.observedTimeUnixNano!==void 0)return!0;if(Ii(e.body)&&(e.body.stringValue!==void 0||e.body.intValue!==void 0||e.body.doubleValue!==void 0))return!0;return zm(e.attributes)}function r_(e,t){if(!t_(e))return;let r=ZP(e.attributes),n=QP(e.body)??qm(r)??"log",o=Di(e,md)??Zt(r.trace_id??r.traceId);return{message:n,level:e_(e),request_id:o,raw:t}}function n_(e,t){let r=Zt(e.metric_name);if(r===void 0)return;let n=Zt(e.metric_type),o=$o(e.value);if(n===void 0&&o===void 0)return;let i=Zt(e.unit),s=o===void 0?void 0:i===void 0?String(o):`${o} ${i}`;return{message:hd([r,n,s,qm(XP(e.attributes))]),raw:t}}function o_(e){let t=e.trim().split(/\s+/);return{method:t[0],uri:t[1]}}function i_(e,t){if(Di(e,Gm)!==void 0)return;let r=Zt(e.request),n=r===void 0?{}:o_(r),o=Zt($r(e,["request_method","method","http_method","verb"]))??n.method,i=Zt($r(e,["request_uri","uri","url","path","request_path"]))??n.uri,s=$o($r(e,["status","status_code","statusCode","http_status"])),a=Zt($r(e,["remote_ip","remote_addr","client_ip"]));if(o===void 0||i===void 0)return;if(s===void 0&&a===void 0&&r===void 0)return;let c=$o($r(e,["body_bytes_sent","bytes_sent","bytes"])),l=$o($r(e,["request_time","duration"])),d=$o($r(e,["request_time_ms","latency_ms","duration_ms"])),u=c===void 0?void 0:`${c}B`,f=d!==void 0?`${d}ms`:l===void 0?void 0:`${l}s`;return{message:hd([a,o,i,s===void 0?void 0:String(s),u,f]),level:s===void 0?void 0:JP(s),request_id:Di(e,md),raw:t}}function s_(e,t){let r=Di(e,Gm),n=Km(e),o=Di(e,md);if(r===void 0&&n===void 0&&o===void 0)return;return{message:r??t,level:n,request_id:o,raw:t}}function gd(e){if(e.length<=pd)return e;return e.slice(0,pd)}function vd(e){let t=e.trim();if(!t.startsWith("{")||!t.endsWith("}"))return;if(t.length>Um)return;let r;try{r=JSON.parse(t)}catch{return}if(!Ii(r))return;return r_(r,t)??n_(r,t)??i_(r,t)??s_(r,t)}function yd(e){return c_.find((r)=>r.re.test(e))?.level??ma}function bd(e){return a_.exec(e)?.[1]??""}var Gm,jP,md,BP=400,UP=500,WP=1,GP=5,KP=9,qP=13,VP=17,zP=21,YP,a_,c_;var Ym=I(()=>{Li();Gm=["message","msg","text","log","event"],jP=["level","severity","severityText","levelname","loglevel","lvl"],md=["request_id","requestId","trace_id","traceId","correlation_id","correlationId"],YP={10:ua,20:pa,30:fa,40:Ai,50:Kn,60:Oo};a_=/(?:x-devctl-request-id|request[_-]?id)[=: ]+([A-Za-z0-9-]+)/i,c_=[{re:/\b(fatal|critical)\b/i,level:Oo},{re:/\b(error|err)\b/i,level:Kn},{re:/\b(warn|warning)\b/i,level:Ai},{re:/\b(debug|dbg)\b/i,level:pa},{re:/\b(trace)\b/i,level:ua},{re:/\b(info|information)\b/i,level:fa}]});function Mi(e){let t=l_(e);if(t===void 0)return;return new RegExp(t)}function l_(e){if(e.length===0||e.length>200)return;let t={raw:e,i:0,out:[],quantifiers:0,depth:0,lastAtom:"none",groupQuant:[]};if(!Jm(t)||t.i!==e.length||t.depth!==0)return;return t.out.join("")}function Jm(e){while(e.i<e.raw.length){if(e.raw[e.i]===")")return!0;if(!d_(e))return!1}return!0}function d_(e){let t=e.raw[e.i]??"";if(t==="|")return ha(e,"|","none");if(t==="^")return ha(e,"^","none");if(t==="$")return ha(e,"$","none");if(t===".")return ha(e,".","atom");if(t==="(")return u_(e);if(t==="[")return f_(e);if(t==="*"||t==="+"||t==="?"||t==="{")return p_(e);if(t==="\\")return Xm(e);return e.out.push(RegExp.escape(t)),e.i+=1,e.lastAtom="atom",!0}function ha(e,t,r){return e.out.push(t),e.i+=1,e.lastAtom=r,!0}function u_(e){if(e.i+=1,e.depth+=1,e.depth>2)return!1;if(e.groupQuant.push(!1),e.out.push("(?:"),!Jm(e)||e.raw[e.i]!==")")return!1;e.out.push(")"),e.i+=1,e.depth-=1;let t=e.groupQuant.pop()===!0;return e.lastAtom=t?"quantified-group":"atom",!0}function p_(e){let t=e.raw[e.i]??"";if(t==="{"||e.lastAtom==="none"||e.lastAtom==="quant"||e.lastAtom==="quantified-group")return!1;if(e.quantifiers>=8)return!1;if(t==="*")e.out.push("*");else if(t==="+")e.out.push("+");else if(t==="?")e.out.push("?");else return!1;e.i+=1,e.quantifiers+=1;let r=e.groupQuant.length-1;if(r>=0)e.groupQuant[r]=!0;if(e.raw[e.i]==="?")e.out.push("?"),e.i+=1;return e.lastAtom="quant",!0}function f_(e){if(e.i+=1,e.out.push("["),e.raw[e.i]==="^")e.out.push("^"),e.i+=1;if(e.raw[e.i]==="]")e.out.push(RegExp.escape("]")),e.i+=1;while(e.i<e.raw.length&&e.raw[e.i]!=="]"){let t=e.raw[e.i]??"";if(t==="\\"){if(!Xm(e))return!1}else if(t==="-")e.out.push("-"),e.i+=1;else e.out.push(RegExp.escape(t)),e.i+=1}if(e.raw[e.i]!=="]")return!1;return e.out.push("]"),e.i+=1,e.lastAtom="atom",!0}function Xm(e){let t=e.raw[e.i+1];if(t===void 0)return!1;if(t==="d")e.out.push("\\d");else if(t==="D")e.out.push("\\D");else if(t==="w")e.out.push("\\w");else if(t==="W")e.out.push("\\W");else if(t==="s")e.out.push("\\s");else if(t==="S")e.out.push("\\S");else if(t==="n")e.out.push("\\n");else if(t==="t")e.out.push("\\t");else if(t==="r")e.out.push("\\r");else if(t==="b")e.out.push("\\b");else if(t==="B")e.out.push("\\B");else e.out.push(RegExp.escape(t));return e.i+=2,e.lastAtom="atom",!0}function Fo(e,t){if(e.services&&e.services.length>0&&!e.services.includes(t.service))return!1;if(e.level&&(fd[t.level]??2)<(fd[e.level]??2))return!1;if(e.source&&t.source!==e.source)return!1;if(e.since&&t.timestamp<e.since)return!1;if(e.until&&t.timestamp>e.until)return!1;return!0}function xd(e){if(!e.search)return()=>!0;if(e.regex){let r=Mi(e.search);if(r)return(n)=>r.test(n.message)||n.raw!==void 0&&r.test(n.raw)}let t=e.search.toLowerCase();return(r)=>r.message.toLowerCase().includes(t)||r.raw!==void 0&&r.raw.toLowerCase().includes(t)}function wd(e){let t=xd(e);return(r)=>Fo(e,r)&&t(r)}var Qm=I(()=>{Li()});function Zm(e){if(!Number.isInteger(e)||(e??0)<=0)return Bm;return Math.min(e,jm)}var eh=I(()=>{Li()});var ga=I(()=>{Li();Ym();Qm();eh()});function E(e,t){return new Ve(e,t)}function Pe(e,t,r){let n=r instanceof Error?r:Error(String(r));return new Ve(e,t,{cause:n})}function Te(e,t,r,n=""){return new Ve(e,t,{hint:r,service:n})}function ba(e,t){return e instanceof Ve&&e.kind===t}function uh(e){if(e instanceof Ve)return e.exitCode();return 1}function ph(e){if(e instanceof Ve)return{error:L(e),kind:e.kind,hint:e.hint,service:e.service};return{error:L(e)}}function fh(e){if(typeof e==="string")return E("general",e);if(e.kind)return new Ve(e.kind,e.error.replace(`${e.kind}: `,""),{hint:e.hint,service:e.service});return E("general",e.error)}function L(e){if(e instanceof Ve){if(e.hint!=="")return`${ch(e)} \u2014 ${e.hint}`;return ch(e)}if(e instanceof Error)return e.message;return String(e)}function ch(e){let t=`${e.kind}: `;if(e.message.startsWith(t)){let r=e.message.slice(t.length),n=e.causeError?`: ${e.causeError.message}`:"";if(n!==""&&r.endsWith(n))return r.slice(0,r.length-n.length);return r}return e.message}var lh=1,Y="configuration",qn="configuration_missing",Ho="service_not_found",dh="dependency",He="process_start",gn="authentication",Vn="authorization",Fr="token",va="impersonation",ya="iap",Nr="proxy",Td="health_check",lt="general",Ve;var te=I(()=>{Ve=class Ve extends Error{kind;hint;service;causeError;constructor(e,t,r){super(r?.cause?`${e}: ${t}: ${r.cause.message}`:`${e}: ${t}`);this.name="DevctlError",this.kind=e,this.hint=r?.hint??"",this.service=r?.service??"",this.causeError=r?.cause}exitCode(){switch(this.kind){case"configuration":case"configuration_missing":case"service_not_found":case"dependency":return 2;case"authentication":case"token":case"iap":return 3;case"authorization":case"impersonation":return 4;case"process_start":return 5;case"health_check":return 6;case"proxy":return 7;default:return 1}}}});function Pa(e){return e>0?e:300}function Dd(){return{enabled:!1,paths:[],debounce_ms:300,ignore:[...$i]}}function Id(){return{enabled:void 0,host:"",port:""}}function Fi(){return{type:"",identity:{type:"",service_account:""},audience:"",service_account:"",client_id:"",client_secret:"",credentials:"",headers:{}}}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 vn(e){return typeof e==="string"?"service_started":e.condition||"service_started"}function Ra(e){return vn(e)==="service_healthy"?`${zt(e)} (healthy)`:zt(e)}function $d(){return{type:"",mode:"",service_account:"",config:{}}}function yn(){return{extends:"",description:"",command:Bo(),shell:!1,working_dir:"",dependencies:[],ports:[],environment:Md(),health:Od(),identity:$d(),logs:{stdout:!1,stderr:!1},restart:{policy:"",max_retries:0,backoff_seconds:0},startup:{wait_for_healthy:!1,timeout_seconds:0},capabilities:[],proxy:[],expose:Id(),container:void 0,watch:Dd(),hooks:{pre_start:Bo(),post_start:Bo()}}}function Yn(){return{version:1,project:{name:""},google:{project_id:"",region:""},profiles:{},templates:{},services:{},tasks:{},proxy:{enabled:!1,gateway:!1,credentials:"",listen:{host:_a,port:0},token_endpoint:{enabled:!1,host:"",port:0},routes:[]},logs:{max_memory_events:O_,persistence:{enabled:!0,directory:"~/.devctl/logs",retention_days:$_,max_session_logs:0}},auth:{refresh_threshold_seconds:vh},shutdown:{grace_seconds:gh},ui:{theme:"system",keymap:{}},secrets:{extra_markers:[],extra_patterns:[]},doctor:{tools:[]},plugins:[],environment:{sources:[],secrets:{}},provenance:{},repoRoot:"",configPath:""}}function bn(e){return e.args.length===0||e.args.length===1&&(e.args[0]??"").trim()===""}function Hi(e){if(e.type!=="")return e.type;return e.mode}function en(e){let t=Hi(e).toLowerCase();return t==="service"||t==="service_account"}function yh(e){let t=Hi(e).toLowerCase();return t==="user"||t===""}function Ea(e){if(e.policy!==""){if(e.policy==="always"||e.policy==="on_failure"||e.policy==="never")return e.policy;return e.policy}if(e.enabled===!0)return"on_failure";return"never"}function xn(e){let t=e.host===""?_a:e.host;if(e.port===0)return t;return`${t}:${e.port}`}function bh(e){if(e.refresh_threshold_seconds<=0)return vh;return e.refresh_threshold_seconds}function Ta(e){if(e.stop_services_on_exit===void 0)return!0;return e.stop_services_on_exit}function wn(e){if(e.grace_seconds<=0)return gh;return e.grace_seconds}function xh(e){if(!e.logs.stdout&&!e.logs.stderr)return!0;return e.logs.stdout}function wh(e){if(!e.logs.stdout&&!e.logs.stderr)return!0;return e.logs.stderr}function Jn(e,t){return e.find((r)=>r.name===t)}function Sh(e){return e[0]}var Uo=1,Sa="never",ka="on_failure",Ca="always",$i,Ni=8080,gh=10,vh=300,O_=50000,$_=14,_a="127.0.0.1";var ht=I(()=>{$i=["**/node_modules/**","**/.git/**"]});var Nt=".devctl",lr="config.yaml";import{basename as F_}from"path";function Sn(e,t=""){return{name:F_(e),project:t,profile:"",sa:"",audience:"",proxyPort:Ni}}function Aa(e,t=Ni){let r=Number(e);return Number.isFinite(r)&&r>0?r:t}function kh(e){let t=e;return`# yaml-language-server: $schema=https://raw.githubusercontent.com/amr-m-abdelgawad/devctl/main/schema/devctl.config.schema.json
|
|
3
|
+
var iP=Object.defineProperty;var sP=(e)=>e;function aP(e,t){this[e]=sP.bind(null,t)}var cP=(e,t)=>{for(var r in t)iP(e,r,{get:t[r],enumerable:!0,configurable:!0,set:aP.bind(t,r)})};var I=(e,t)=>()=>(e&&(t=e(e=0)),t);var lP=import.meta.require;function _f(e,t=""){if(t==="MetadataLookupWarning")return!0;return e.includes("MetadataLookupWarning")||e.startsWith("received unexpected error =")}function ra(){if(Rf)return;Rf=!0;let e=process.emitWarning.bind(process);process.emitWarning=(t,...r)=>{let n=uP(r[0]),o=dP(t);if(_f(o,n))return;e(t,...r)}}function Ef(){let e=process.stderr.write.bind(process.stderr);return process.stderr.write=(t,r,n)=>{let o=pP(t);if(_f(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 dP(e){if(typeof e==="string")return e;if(e instanceof Error)return e.message;return String(e)}function uP(e){if(typeof e==="string")return e;if(e&&typeof e==="object"&&"type"in e&&typeof e.type==="string")return e.type;return""}function pP(e){if(typeof e==="string")return e;if(e instanceof Uint8Array)return new TextDecoder().decode(e);return""}var Rf=!1;import{createHash as mP}from"crypto";import{resolve as fP}from"path";function cr(e){let t=fP(e),r=process.platform==="win32"?t.toLowerCase():t;return mP("sha256").update(r).digest("hex").slice(0,hP)}var hP=16;var na=()=>{};function td(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 pd(e=process.platform){return e==="darwin"}function MP(e=process.platform){return pd(e)?"cmd":"ctrl"}function Do(e,t=process.platform){return`${MP(t)}+${e}`}function OP(e=process.platform){return pd(e)?"command":"ctrl"}function It(e,t=process.platform){return`${OP(t)}+${e}`}function Li(e){return e.replace(/\bcmd\b/gi,"command")}function Hr(e,t=process.platform){let r=pd(t),n=Boolean(e.meta||e.super),o=Boolean(e.ctrl);return r?n&&!o:o&&!n}function en(e=process.platform){return Do("c",e)}function $P(e=process.platform){return{leader:Do("x",e),command_list:Do("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:en(e)}}function Ff(){return{theme:"devctl",leader_timeout:2000,font_size:14,keybinds:{...FP},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 NP(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 vn(e,t){return NP(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 ud=2000,Io=14,$f="DEVCTL_TUI_CONFIG",FP;var aa=I(()=>{FP=$P()});function ye(e,t,r){return{type:e,timestamp:new Date().toISOString(),service:t,payload:r}}class hd{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 jf="ServiceStarted",fa="ServiceStopped",ha="ServiceFailed",Bf="ServiceStateChanged",Uf="ServiceHealthChanged",Oo="LogReceived",$o="TokenRefreshed",qn="TokenRefreshFailed",fd="AuthenticationChanged",Wf="ProxyStarted",Gf="ProxyStopped",Di="ProxyRequest",ga="ConfigurationChanged",Ii="ConfigurationReloadFailed",Kf="SessionRecovered";var qf=5000,Vf=500,gd=16384,zf=65536,va="TRACE",ya="DEBUG",ba="INFO",Mi="WARN",Vn="ERROR",Fo="FATAL",xa="UNKNOWN",vd;var Oi=I(()=>{vd={["TRACE"]:0,["DEBUG"]:1,["INFO"]:2,["WARN"]:3,["ERROR"]:4,["FATAL"]:5,["UNKNOWN"]:2}});function Yf(){return{name:"default",parse:(e)=>{let t=wd(e);if(t)return t;return{level:Sd(e),request_id:kd(e)||void 0}}}}function $i(e,t){for(let r of t){let n=e[r];if(typeof n==="string"&&n.trim()!=="")return n}return}function Xf(e){for(let t of qP){let r=e[t];if(typeof r==="number"){let n=tR[r];if(n)return n;continue}if(typeof r==="string"&&r.trim()!=="")return r.toUpperCase()}return}function Fi(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}function er(e){if(typeof e==="string"&&e.trim()!=="")return e;if(typeof e==="number"&&Number.isFinite(e))return String(e);return}function No(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 jr(e,t){for(let r of t)if(e[r]!==void 0&&e[r]!==null)return e[r];return}function bd(e){return e.filter((t)=>t!==void 0&&t!=="").join(" ")}function rR(e){if(e>=zP)return Vn;if(e>=VP)return Mi;return}function nR(e){if(Fi(e))return e;return{}}function Qf(e){let t=er(jr(e,["http.method","method","request_method"])),r=er(jr(e,["http.route","url.path","path","route","request_uri"])),n=er(jr(e,["http.status_code","status","status_code"])),o=bd([t,r,n]);return o===""?void 0:o}function Zf(e){if(!Fi(e))return er(e);if(typeof e.stringValue==="string")return e.stringValue.trim()===""?void 0:e.stringValue;return er(e.intValue??e.doubleValue??e.boolValue)}function oR(e){if(typeof e==="string"&&e.trim()!=="")return e;return Zf(e)}function eh(e){if(!Array.isArray(e)||e.length===0)return!1;return e.every((t)=>Fi(t)&&typeof t.key==="string"&&t.value!==void 0)}function iR(e){let t={};if(!eh(e))return t;for(let r of e){let n=r.key;if(typeof n==="string"&&n!==""){let o=Zf(r.value);if(o!==void 0)t[n]=o}}return t}function sR(e){let t=Xf(e);if(t)return t;let r=No(e.severityNumber);if(r===void 0)return;if(r>=eR)return Fo;if(r>=ZP)return Vn;if(r>=QP)return Mi;if(r>=XP)return ba;if(r>=JP)return ya;if(r>=YP)return va;return}function aR(e){if(e.timeUnixNano!==void 0||e.observedTimeUnixNano!==void 0)return!0;if(Fi(e.body)&&(e.body.stringValue!==void 0||e.body.intValue!==void 0||e.body.doubleValue!==void 0))return!0;return eh(e.attributes)}function cR(e,t){if(!aR(e))return;let r=iR(e.attributes),n=oR(e.body)??Qf(r)??"log",o=$i(e,yd)??er(r.trace_id??r.traceId);return{message:n,level:sR(e),request_id:o,raw:t}}function lR(e,t){let r=er(e.metric_name);if(r===void 0)return;let n=er(e.metric_type),o=No(e.value);if(n===void 0&&o===void 0)return;let i=er(e.unit),s=o===void 0?void 0:i===void 0?String(o):`${o} ${i}`;return{message:bd([r,n,s,Qf(nR(e.attributes))]),raw:t}}function dR(e){let t=e.trim().split(/\s+/);return{method:t[0],uri:t[1]}}function uR(e,t){if($i(e,Jf)!==void 0)return;let r=er(e.request),n=r===void 0?{}:dR(r),o=er(jr(e,["request_method","method","http_method","verb"]))??n.method,i=er(jr(e,["request_uri","uri","url","path","request_path"]))??n.uri,s=No(jr(e,["status","status_code","statusCode","http_status"])),a=er(jr(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=No(jr(e,["body_bytes_sent","bytes_sent","bytes"])),l=No(jr(e,["request_time","duration"])),d=No(jr(e,["request_time_ms","latency_ms","duration_ms"])),u=c===void 0?void 0:`${c}B`,m=d!==void 0?`${d}ms`:l===void 0?void 0:`${l}s`;return{message:bd([a,o,i,s===void 0?void 0:String(s),u,m]),level:s===void 0?void 0:rR(s),request_id:$i(e,yd),raw:t}}function pR(e,t){let r=$i(e,Jf),n=Xf(e),o=$i(e,yd);if(r===void 0&&n===void 0&&o===void 0)return;return{message:r??t,level:n,request_id:o,raw:t}}function xd(e){if(e.length<=gd)return e;return e.slice(0,gd)}function wd(e){let t=e.trim();if(!t.startsWith("{")||!t.endsWith("}"))return;if(t.length>zf)return;let r;try{r=JSON.parse(t)}catch{return}if(!Fi(r))return;return cR(r,t)??lR(r,t)??uR(r,t)??pR(r,t)}function Sd(e){return fR.find((r)=>r.re.test(e))?.level??xa}function kd(e){return mR.exec(e)?.[1]??""}var Jf,qP,yd,VP=400,zP=500,YP=1,JP=5,XP=9,QP=13,ZP=17,eR=21,tR,mR,fR;var th=I(()=>{Oi();Jf=["message","msg","text","log","event"],qP=["level","severity","severityText","levelname","loglevel","lvl"],yd=["request_id","requestId","trace_id","traceId","correlation_id","correlationId"],tR={10:va,20:ya,30:ba,40:Mi,50:Vn,60:Fo};mR=/(?:x-devctl-request-id|request[_-]?id)[=: ]+([A-Za-z0-9-]+)/i,fR=[{re:/\b(fatal|critical)\b/i,level:Fo},{re:/\b(error|err)\b/i,level:Vn},{re:/\b(warn|warning)\b/i,level:Mi},{re:/\b(debug|dbg)\b/i,level:ya},{re:/\b(trace)\b/i,level:va},{re:/\b(info|information)\b/i,level:ba}]});function Ni(e){let t=hR(e);if(t===void 0)return;return new RegExp(t)}function hR(e){if(e.length===0||e.length>200)return;let t={raw:e,i:0,out:[],quantifiers:0,depth:0,lastAtom:"none",groupQuant:[]};if(!rh(t)||t.i!==e.length||t.depth!==0)return;return t.out.join("")}function rh(e){while(e.i<e.raw.length){if(e.raw[e.i]===")")return!0;if(!gR(e))return!1}return!0}function gR(e){let t=e.raw[e.i]??"";if(t==="|")return wa(e,"|","none");if(t==="^")return wa(e,"^","none");if(t==="$")return wa(e,"$","none");if(t===".")return wa(e,".","atom");if(t==="(")return vR(e);if(t==="[")return bR(e);if(t==="*"||t==="+"||t==="?"||t==="{")return yR(e);if(t==="\\")return nh(e);return e.out.push(RegExp.escape(t)),e.i+=1,e.lastAtom="atom",!0}function wa(e,t,r){return e.out.push(t),e.i+=1,e.lastAtom=r,!0}function vR(e){if(e.i+=1,e.depth+=1,e.depth>2)return!1;if(e.groupQuant.push(!1),e.out.push("(?:"),!rh(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 yR(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 bR(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(!nh(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 nh(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 Ho(e,t){if(e.services&&e.services.length>0&&!e.services.includes(t.service))return!1;if(e.level&&(vd[t.level]??2)<(vd[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 Cd(e){if(!e.search)return()=>!0;if(e.regex){let r=Ni(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 Pd(e){let t=Cd(e);return(r)=>Ho(e,r)&&t(r)}var oh=I(()=>{Oi()});function ih(e){if(!Number.isInteger(e)||(e??0)<=0)return Vf;return Math.min(e,qf)}var sh=I(()=>{Oi()});var Sa=I(()=>{Oi();th();oh();sh()});function P(e,t){return new Ve(e,t)}function Se(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 Pa(e,t){return e instanceof Ve&&e.kind===t}function vh(e){if(e instanceof Ve)return e.exitCode();return 1}function yh(e){if(e instanceof Ve)return{error:L(e),kind:e.kind,hint:e.hint,service:e.service};return{error:L(e)}}function bh(e){if(typeof e==="string")return P("general",e);if(e.kind)return new Ve(e.kind,e.error.replace(`${e.kind}: `,""),{hint:e.hint,service:e.service});return P("general",e.error)}function L(e){if(e instanceof Ve){if(e.hint!=="")return`${fh(e)} \u2014 ${e.hint}`;return fh(e)}if(e instanceof Error)return e.message;return String(e)}function fh(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 hh=1,Y="configuration",zn="configuration_missing",Bo="service_not_found",gh="dependency",He="process_start",yn="authentication",Yn="authorization",Rr="token",ka="impersonation",Ca="iap",zt="proxy",Id="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 La(e){return e>0?e:300}function $d(){return{enabled:!1,paths:[],debounce_ms:300,ignore:[...ji]}}function Fd(){return{enabled:void 0,host:"",port:""}}function Bi(){return{type:"",identity:{type:"",service_account:""},audience:"",service_account:"",client_id:"",client_secret:"",credentials:"",headers:{}}}function Ko(e){return(e.transport??"").toLowerCase()==="grpc"}function Wo(){return{args:[],shell:!1}}function Nd(){return{vars:{},required:[],defaults:{}}}function Hd(){return{type:"",url:"",address:"",command:Wo(),interval_seconds:0,timeout_seconds:0,start_period_seconds:0,unhealthy_threshold:3,healthy_reset_threshold:10}}function Yt(e){return typeof e==="string"?e:e.service}function bn(e){return typeof e==="string"?"service_started":e.condition||"service_started"}function Ia(e){return bn(e)==="service_healthy"?`${Yt(e)} (healthy)`:Yt(e)}function jd(){return{type:"",mode:"",service_account:"",config:{}}}function xn(){return{extends:"",description:"",command:Wo(),shell:!1,working_dir:"",dependencies:[],ports:[],environment:Nd(),health:Hd(),identity:jd(),logs:{stdout:!1,stderr:!1},restart:{policy:"",max_retries:0,backoff_seconds:0},startup:{wait_for_healthy:!1,timeout_seconds:0},capabilities:[],proxy:[],expose:Fd(),container:void 0,watch:$d(),hooks:{pre_start:Wo(),post_start:Wo()}}}function Xn(){return{version:1,project:{name:""},google:{project_id:"",region:""},profiles:{},templates:{},services:{},tasks:{},proxy:{enabled:!1,gateway:!1,credentials:"",listen:{host:Da,port:0},token_endpoint:{enabled:!1,host:"",port:0},routes:[]},logs:{max_memory_events:BR,persistence:{enabled:!0,directory:"~/.devctl/logs",retention_days:UR,max_session_logs:0}},auth:{refresh_threshold_seconds:kh},shutdown:{grace_seconds:Sh},ui:{theme:"system",keymap:{}},secrets:{extra_markers:[],extra_patterns:[]},doctor:{tools:[]},plugins:[],environment:{sources:[],secrets:{}},provenance:{},repoRoot:"",configPath:""}}function wn(e){return e.args.length===0||e.args.length===1&&(e.args[0]??"").trim()===""}function Wi(e){if(e.type!=="")return e.type;return e.mode}function tn(e){let t=Wi(e).toLowerCase();return t==="service"||t==="service_account"}function Ch(e){let t=Wi(e).toLowerCase();return t==="user"||t===""}function Ma(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 Sn(e){let t=e.host===""?Da:e.host;if(e.port===0)return t;return`${t}:${e.port}`}function Ph(e){if(e.refresh_threshold_seconds<=0)return kh;return e.refresh_threshold_seconds}function Oa(e){if(e.stop_services_on_exit===void 0)return!0;return e.stop_services_on_exit}function kn(e){if(e.grace_seconds<=0)return Sh;return e.grace_seconds}function Rh(e){if(!e.logs.stdout&&!e.logs.stderr)return!0;return e.logs.stdout}function _h(e){if(!e.logs.stdout&&!e.logs.stderr)return!0;return e.logs.stderr}function Qn(e,t){return e.find((r)=>r.name===t)}function Eh(e){return e[0]}var Go=1,Ea="never",Ta="on_failure",Aa="always",ji,Ui=8080,Sh=10,kh=300,BR=50000,UR=14,Da="127.0.0.1";var dt=I(()=>{ji=["**/node_modules/**","**/.git/**"]});var Nt=".devctl",dr="config.yaml";import{basename as WR}from"path";function Cn(e,t=""){return{name:WR(e),project:t,profile:"",sa:"",audience:"",proxyPort:Ui}}function $a(e,t=Ui){let r=Number(e);return Number.isFinite(r)&&r>0?r:t}function Th(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 Fa=I(()=>{dt();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 Wd,mkdirSync as Bd,statSync as GR,writeFileSync as KR}from"fs";import{createInterface as qR}from"readline/promises";import{stdin as VR,stdout as zR}from"process";import{basename as Na,dirname as Ud,join as rn,resolve as YR}from"path";function Gd(e,t){let r=e===""?process.cwd():e;if(t==="")return{repo:r,cfgPath:rn(r,Nt,dr)};let n=YR(t);if(!(Wd(n)?GR(n).isDirectory():!/\.ya?ml$/i.test(n))){let i=Ud(n);return{repo:Na(i)===Nt?Ud(i):i,cfgPath:n}}if(Na(n)===Nt)return{repo:Ud(n),cfgPath:rn(n,dr)};return{repo:n,cfgPath:rn(n,Nt,dr)}}function Ah(e,t=Na(e),r="",n="",o=!1){let i=rn(e,Nt,dr);if(!o&&Wd(i))throw P(Y,`configuration already exists at ${i}; not overwriting`);return Ha(e,{...Cn(e,r),name:t,profile:n}),i}async function Lh(e,t,r="",n=!1){let{repo:o,cfgPath:i}=Gd(t,r);if(!n&&Wd(i)){Ht(`Found existing configuration at ${i}; nothing written.`),Ht("Pass --force to overwrite it, or edit it directly.");return}let s=qR({input:VR,output:zR}),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=Cn(o,""),u=await a(`${l()}. ${wt[1]?.prompt}`,Na(o));d.name=u;let m=await e.detectGoogle(""),p=await a(`${l()}. ${wt[2]?.prompt}`,m.projectID);if(d.project=p,Ht(`${l()}. ${wt[3]?.title}`),!m.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"),m.userEmail!=="")Ht(` user: ${m.userEmail}`);d.sa=await a(`${l()}. ${wt[4]?.prompt}`,""),d.audience=await a(`${l()}. ${wt[5]?.prompt}`,"");let f=await a(`${l()}. ${wt[6]?.prompt}`,String(Ui));d.proxyPort=$a(f),d.profile=await a(`${l()}. ${wt[7]?.prompt}`,""),s.close(),Ha(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 Ha(e,t){let r=rn(e,".devctl");Bd(rn(r,"services"),{recursive:!0}),Bd(rn(r,"profiles"),{recursive:!0}),Bd(rn(r,"proxy"),{recursive:!0}),KR(rn(r,"config.yaml"),Th(t))}function Ht(e){process.stdout.write(`${e}
|
|
51
|
+
`)}var ja=I(()=>{dt();Fa();te()});function Pg(){return/\$\{(?:env\.)?([A-Za-z_][A-Za-z0-9_]*)\}/g}function iu(e){return[...e.matchAll(Pg())].map((t)=>t[1]??"")}function Rg(e,t){let r=[];return{value:e.replace(Pg(),(o,i)=>{let s=t[i];if(s===void 0||s==="")return r.push(i),"";return s}),missing:r}}function za(e){let t=e.trim();if(t===""||iu(t).length===0)return;return t}function _g(e){return/^\$\{(?:env\.)?[A-Za-z_][A-Za-z0-9_]*\}$/.test(e.trim())}function Rn(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:aE(e,t),...n,shadowed:r.slice(0,-1)}]})}function aE(e,t){let r=e;for(let n of t.split(".")){if(typeof r!=="object"||r===null)return;r=r[n]}return r}function Xa(e){if(tn(e))return ur({kind:oo,serviceAccount:e.service_account});if(Ch(e)&&(e.type!==""||e.mode!==""))return ur({kind:Ja});let t=(e.type||e.mode).toLowerCase();if(t!=="")return ur({kind:t,providerConfig:e.config??{}});return ur({kind:Vo})}function an(e){if(e.type.toLowerCase()==="none")return ur({kind:Vo});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 ur({kind:oo,serviceAccount:r});if(e.type.toLowerCase()==="iap"&&t==="")return ur({kind:Vo});if(e.type===""&&t==="")return ur({kind:Vo});return ur({kind:Ja})}function Fg(e){return e.kind===Ja||e.kind===oo}function _n(e){if(e.tokenKey!=="")return e.tokenKey;if(e.kind===oo)return`sa:${e.serviceAccount}`;if(e.email!=="")return`user:${e.email}`;return"user"}function ur(e={}){return{kind:Vo,email:"",serviceAccount:"",project:"",projectSource:"",adcAvailable:!1,providerConfig:{},tokenKey:"",...e}}function lu(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(tn(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 Br(e){return lu(e).filter((t)=>t.active).map((t)=>t.email)}function Ng(e,t,r){let n=[];for(let o of t){let i=e.services[o];if(!i)continue;let s=Xa(i.identity);if(tn(i.identity)&&i.identity.service_account===""){n.push({name:o,message:"service account identity is not configured"});continue}if((Fg(s)||i.capabilities.some((c)=>c==="google_api"||c==="iap"||c==="service_identity"))&&!r)n.push({name:o,message:"ADC unavailable"})}return n}function Ji(e){if(Br(e).length>0)return!0;if(e.proxy.routes.some((t)=>t.auth.type.toLowerCase()==="iap"))return!0;return Object.values(e.services).some((t)=>{if(Fg(Xa(t.identity)))return!0;return t.capabilities.some((r)=>r==="google_api"||r==="iap"||r==="service_identity")})}function du(){return{name:"user",accepts:(e)=>!tn(e),resolve:async(e,t)=>{if(e.type===""&&e.mode==="")return ur({kind:Vo});return{...await t(),kind:Ja}}}}function uu(){return{name:"service_account",accepts:(e)=>tn(e),resolve:async(e,t)=>{if(e.service_account==="")throw P(ka,"service account identity is not configured");return{...await t(),kind:oo,serviceAccount:e.service_account}}}}async function Hg(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(tn(e))return uu().resolve(e,t);return du().resolve(e,t)}var Ja="user",oo="service_account",Vo="none";var Tr=I(()=>{dt();te()});function tr(){return"devctl 0.5.0"}var jt="0.5.0",ts=1;function ov(e){return e}function pr(e){return e}function Qo(e){if(e.state===je&&e.health===Bt)return mr;if(e.state===je&&e.health===Oe)return Ur;return e.state}function os(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 av(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=Yt(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 P(gh,"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 Cu(e,t){if(!e.services[t])throw P(Bo,`unknown service "${t}"`)}function cc(e,t,r){let n={},o=(a)=>{if(n[a])return;Cu(e,a),n[a]=!0;for(let c of e.services[a]?.dependencies??[])o(Yt(c))};for(let a of t)o(a);let i=av(e,n),s=[];return i.forEach((a,c)=>{for(let l of a)s.push({name:l,wave:c+1,dependencies:[...e.services[l]?.dependencies??[]]})}),{profile:r,steps:s,waves:i,blockers:[]}}function lo(e,t){let r={};for(let[i,s]of Object.entries(e.services))for(let a of s.dependencies){let c=Yt(a);r[c]=[...r[c]??[],i]}let n={},o=(i)=>{if(n[i])return;Cu(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 cv(e,t){let r=[...av(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 lc(e,t){let r=Object.fromEntries(lo(e,t).map((n)=>[n,!0]));return cv(e,r)}function dc(e,t){let r={};for(let n of t)Cu(e,n),r[n]=!0;return cv(e,r)}function lv(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 uc(e){return`${e.join(", ")} changed and only take effect after a full \`devctl down && devctl start\`, not a reload`}function lT(e,t,r){let n={};if(t!==""){let o=e.profiles[t];if(!o)throw P("configuration",`unknown profile "${t}"`);Object.assign(n,o.environment)}if(r.length>0)return{services:iv(e,r),env:n};if(t==="")return{services:Object.keys(e.services),env:n};return{services:iv(e,e.profiles[t]?.services??[]),env:n}}function dT(e){let t=Object.keys(e.profiles).sort();return pr(t[0]??"")}function uo(e,t){if(t==="")return{};return{...e.profiles[pr(t)]?.environment??{}}}function pc(e,t){let r=t.services??[],n=t.profile??pr("");if(r.length===0&&n===""){if(n=t.activeProfile||dT(e),n==="")throw P(Y,"no profile or services given; pass service names or add a profile")}let o=lT(e,n,r);return{services:o.services,env:o.env,profile:n}}function iv(e,t){let r={},n=[];for(let o of t){if(r[o])continue;if(!e.services[o])throw P("service_not_found",`unknown service "${o}"`);r[o]=!0,n.push(o)}return n}var sv="UNKNOWN",cn="STARTING",je="RUNNING",mr="HEALTHY",Ur="UNHEALTHY",Ar="STOPPING",Et="STOPPED",ut="FAILED",fr="RESTARTING",Tt="UNKNOWN",Bt="HEALTHY",Oe="UNHEALTHY";var At=I(()=>{dt();te()});function $v(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 Fv(e){let t=Mv(e.scriptPath),r=Mv(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=HT();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 Nv(e,t){let r=Ov(e),n=Ov(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 HT(){return[process.platform==="win32"?"npm.cmd":"npm","install","--global","@amr-m-abdelgawad/devctl@latest"]}function Mv(e){return e.replaceAll("\\","/")}function Ov(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 bc="restart the daemon with `devctl down` then start again";function Tn(e){if(!Number.isInteger(e))return rp;return Math.min(RA,Math.max(_A,e))}function My(e,t){if(t.length!==1||t<"0"||t>"9")return e;if(e.length>=EA)return t;return`${e}${t}`}function Oy(e){return e.slice(0,-1)}function $y(e,t){if(e==="")return Tn(t);let r=Number(e);if(!Number.isInteger(r))return Tn(t);return Tn(r)}function fo(e){let t=Number.parseInt(cr(e).slice(0,PA),16),r=Number.isFinite(t)?t%Iy:0;return rp+r}function Fy(e,t){return t===fo(e)}var rp=18700,Iy=600,sB,PA=8,RA=65535,_A=1024,EA=5;var np=I(()=>{na();sB=rp+Iy-1});class hr{nameMarkers;patterns;constructor(e,t){this.nameMarkers=[...qy,...e],this.patterns=t.flatMap((r)=>{try{return[new RegExp(r)]}catch{return[]}})}update(e,t){this.nameMarkers=[...qy,...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 UA(t)}}function UA(e){let t=e.toLowerCase(),r="authorization: bearer ",n=t.indexOf("authorization: bearer ");if(n>=0)return Vy(e,n+22);let o=t.indexOf("bearer ");if(o<0)return e;return Vy(e,o+7)}function Vy(e,t){let r=t;while(r<e.length&&!WA(e[r]??""))r+=1;return e.slice(0,t)+"********"+e.slice(r)}function WA(e){return e===" "||e==="\t"||e===`
|
|
60
|
+
`||e==="\r"}var qy;var si=I(()=>{qy=["PASSWORD","SECRET","TOKEN","PRIVATE_KEY","CLIENT_SECRET","API_KEY","CREDENTIAL","ACCESS_KEY","AUTH_KEY"]});var Ln;var pp=I(()=>{Ln={procedure:`---
|
|
61
61
|
name: devctl-onboard
|
|
62
62
|
description: Survey a repository \u2014 its runnable services, docker-compose, Terraform, Kubernetes manifests, .env files, task runners \u2014 and author a validated .devctl configuration for it. Use when asked to onboard a repo to devctl, add or fix .devctl/config.yaml, generate devctl services/profiles/proxy routes, or diagnose why a devctl config fails to load.
|
|
63
63
|
---
|
|
@@ -367,7 +367,7 @@ the agent to set it up" a complete path. In that state \`get_status\` reports
|
|
|
367
367
|
arguments reports the missing configuration rather than a parse error. Draft,
|
|
368
368
|
validate the candidate text, write the files, then call \`reload_config\`: setup
|
|
369
369
|
mode clears on the reload that finds a valid configuration.
|
|
370
|
-
`,authoring:"# Authoring rules the loader enforces\n\nThe JSON Schema (`schema/devctl.config.schema.json`) describes the shape.\nThis file describes what the **loader and validator actually reject** \u2014 the\nrules that turn a schema-plausible config into one that fails at\n`devctl config validate`. Check anything you are unsure of here before writing.\n\n---\n\n## Unknown fields are rejected everywhere\n\nNot warned about \u2014 rejected, with the offending path named. These are the\ncomplete allowlists.\n\n**Top level:** `version` `project` `google` `profiles` `templates` `services`\n`tasks` `proxy` `logs` `auth` `shutdown` `ui` `secrets` `doctor` `plugins`\n`environment`\n\n**Service** (and `templates.<name>`, same shape): `extends` `description`\n`command` `shell` `working_dir` `dependencies` `ports` `environment` `health`\n`identity` `logs` `restart` `startup` `capabilities` `proxy` `expose` `container` `watch` `hooks`\n\n| Section | Allowed keys |\n|---|---|\n| `project` | `name` |\n| `google` | `project_id` `region` |\n| `profiles.<name>` | `services` `environment` |\n| `service.health` | `type` `url` `address` `command` `interval_seconds` `timeout_seconds` `start_period_seconds` `unhealthy_threshold` `healthy_reset_threshold` |\n| `service.hooks` | `pre_start` `post_start` |\n| `service.container` | `image` `runtime` `ports` `env` `volumes` |\n| `service.watch` | `enabled` `paths` `debounce_ms` `ignore` |\n| `tasks.<name>` | `command` `shell` `working_dir` `dependencies` `environment` |\n| `service.identity` | `type` `mode` `service_account` |\n| `service.restart` | `enabled` `policy` `max_retries` `backoff_seconds` |\n| `service.startup` | `wait_for_healthy` `timeout_seconds` |\n| `service.logs` | `stdout` `stderr` |\n| `service.environment` | `required` `defaults` + arbitrary `KEY: value` pairs |\n| `service.expose` | `enabled` `host` `port` (or the `true` shorthand) |\n| `proxy` | `enabled` `gateway` `credentials` `listen` `token_endpoint` `routes` |\n| `proxy.listen` | `host` `port` |\n| `proxy.token_endpoint` | `enabled` `host` `port` |\n| `route` | `name` `match` `upstream` `auth` |\n| `route.match` | `host` `path` |\n| `route.upstream` | `url` `service` `port` |\n| `route.auth` | `type` `identity` `audience` `service_account` `client_id` `client_secret` `credentials` `headers` |\n| `logs` | `max_memory_events` `persistence` |\n| `logs.persistence` | `enabled` `directory` `retention_days` `max_session_logs` |\n| `auth` | `refresh_threshold_seconds` |\n| `shutdown` | `stop_services_on_exit` `grace_seconds` |\n| `ui` | `theme` `keymap` |\n| `secrets` | `extra_markers` `extra_patterns` |\n| `doctor` | `tools` (each `{ name, command }`) |\n| `plugins[]` | `path` |\n| `environment` | `sources` `secrets` |\n\nThere is no `depends_on`, `build`, `replicas`, or `env_file`. Container fields\nbelong under `service.container`, not directly on the service.\n\n---\n\n## Required, at minimum\n\n- `version: 1`. Anything else: *unsupported config version N; no migration is\n available.*\n- **At least one service.** An empty or missing `services` map fails with *at\n least one service must be defined* \u2014 so a proxy-only or profiles-only config\n is not valid.\n- Every host service needs a non-empty `command`; a container service may rely on its image command.\n\n---\n\n## Ports\n\n- **Globally unique across all services.** Two services on `8000` gives\n *duplicate port 8000 used by api and worker*. This is the single most common\n failure when onboarding a repo whose services each assumed they owned the\n default port.\n- Range 1\u201365535.\n- Forms accepted: a named map (`ports: { http: 8000 }`, preferred), a list, a\n bare number, or `auto`. `auto` is exempt from the uniqueness check \u2014\n devctl allocates at start.\n- Name the port `http` when it serves HTTP.\n- Prefer the explicit `${services.X.ports.http}` reference form. The short\n `${services.X.port}` form is only unambiguous for a single-port service: once\n the service is running it prefers the assigned port named `http`, but when it\n resolves against the configuration it takes the **first port declared**,\n whatever its name. Two forms of the same reference disagreeing on a\n multi-port service is a genuinely confusing bug to chase.\n\n## References\n\n`${services.<name>.ports.<portname>}`, `${services.<name>.port}`,\n`${services.<name>.url}`, `${services.<name>.host}`, and `${identity.user}` (the\nrunning developer's detected Google email) are the supported forms. Anything\nelse \u2014 `${env.FOO}`, `${project.name}` \u2014 throws.\n\n- The referenced service must exist and the named port must be defined, or\n validation fails with *unresolvable reference*.\n- References resolve inside service `environment` values, `defaults`, profile\n environments and dotenv values, before the process starts.\n- Use them for every cross-service URL. Hard-coded ports silently break when a\n port changes or is switched to `auto`.\n\n## Dependencies\n\n- Must name a service that exists.\n- A service may not depend on itself.\n- Cycles are rejected: *dependency cycle: a \u2192 b \u2192 a*.\n- Direction: `dependencies` means \"start these first\". `start x` walks **up**\n and starts x's dependencies; `stop x` walks **down** and stops x's\n *dependents*, never its dependencies.\n- A string dependency uses `service_started`. Use `{ service: db, condition:\n service_healthy }` to wait for the dependency's configured health check.\n\n## Commands and `shell`\n\nA command whose argv contains any of `|` `||` `&&` `;` `>` `>>` `<` `&` is\nrejected unless `shell: true`:\n\n```yaml\n# rejected\ncommand: \"npm run build && npm start\"\n\n# correct\ncommand: \"npm run build && npm start\"\nshell: true\n\n# better \u2014 no shell needed\ncommand: [npm, start]\n```\n\nPrefer argv lists. A string command is split on whitespace, which mangles\narguments containing spaces.\n\n## `working_dir`\n\nRelative to the **repository root** \u2014 the directory containing `.devctl` \u2014 not\nthe process cwd, and not the `.devctl` directory itself.\n\n---\n\n## Health\n\n| `type` | Also requires |\n|---|---|\n| `http` | `url` \u2014 omit it and you get *health.url is required for http health checks* |\n| `tcp` | `address`, **or** at least one port defined on the service |\n| `command` | `health.command` (non-empty) |\n| `process` or omitted | nothing; only checks the pid is alive |\n\nAny other `type` is rejected \u2014 *health.type must be http, tcp, process, or\ncommand* \u2014 **unless** `plugins` is non-empty. That exemption is not approval:\nplugins load after validation, so the supervisor re-checks the type at boot and\nat reload. A custom type no plugin provides fails as a *reload rejection*, not\na config error, which is much harder to diagnose. Only use a custom type when\nyou can point at the plugin that registers it.\n\nDefaults when omitted: interval 2s, timeout 2s, start period 0s, unhealthy\nthreshold 3, healthy reset threshold 10. Failures during the start period do\nnot mark the service unhealthy or consume restart budget.\n\n## Identity\n\n- `type: user` (or omitted) needs nothing else.\n- `type: service_account` (or `service`) **requires** `service_account`, and it\n must contain `@` \u2014 *service_account must be an email*.\n- `mode` is an accepted alias of `type`.\n- Any other type behaves like a custom health type: allowed only with\n `plugins` set, re-checked at boot.\n\nNever fabricate an SA email. If Terraform interpolates it beyond what you can\nresolve, leave a clearly-marked placeholder and tell the user.\n\n## Restart\n\n`policy` must be `never`, `on_failure`, or `always`. `enabled: true` with no\npolicy behaves as `on_failure`.\n\n`max_retries` is a real budget: it resets on a manual stop/start/restart and\nafter a sustained healthy run, but an automatic health-triggered restart\npreserves it across its own cycle.\n\n## Capabilities\n\nDocumentation for doctor, not behaviour. Only these are accepted:\n`google` `google_api` `iap` `network` `service_identity` `local_http`.\nAnything else is rejected.\n\n---\n\n## Proxy\n\n- `proxy.listen.port` is **required** when `proxy.enabled: true`.\n- `proxy.listen.host` must be loopback (`127.0.0.1`, `localhost`, `::1`, or `127.0.0.0/8`). `0.0.0.0` and `::` are rejected.\n- `proxy.token_endpoint.host` must be loopback; `0.0.0.0` and `::` are rejected.\n- Every route needs a `name` and either `upstream.url` or `upstream.service`.\n A service reference must name a real service and an existing port (default\n port name is `http`). `service.expose` and `proxy.gateway` synthesize\n service-reference routes; they do nothing unless `proxy.enabled` is true.\n A hand-written route of the same name wins.\n- **Route names must be unique** \u2014 including names generated from per-service\n `proxy` fragments.\n- `auth.type: iap` requires **both** `audience` and `auth.identity.type`. A\n missing identity type on an IAP route is a configuration error, not a\n default.\n- Optional `client_id` plus `client_secret` mints the user IAP ID token with\n that OAuth client instead of ADC's default client. Put `${NAME}` or\n `${env.NAME}` in `client_secret` so the value is read from the environment\n at mint time (this is not service-env interpolation). These fields are\n invalid on non-IAP routes and on IAP routes whose identity is a service\n account.\n- An identity of `service` / `service_account` requires an SA email, from\n either `auth.identity.service_account` or the route's `auth.service_account`.\n- `auth.type: none` means no auth at all \u2014 any identity left on such a route is\n ignored entirely, and will not be probed at start or by doctor.\n\n### Per-service route fragments\n\nA service's `proxy` key is one route or a list. At load they append into the\n**same** global `proxy.routes` list, named `<service>` or `<service>-<n>`:\n\n```yaml\nservices:\n api:\n command: [python3, main.py]\n proxy:\n - match: { path: /api }\n upstream: { url: \"http://127.0.0.1:8000\" }\n```\n\nThat generates a route named `api`. So a service named `api` **and** a global\nroute named `api` collide \u2014 *duplicate route name api*. Watch for this when\nmixing both styles.\n\nMatching is host + optional path prefix. There is no path rewriting or\nstripping: the matched path is forwarded to the upstream as-is.\n\n---\n\n## Modular layout\n\nWhen the main file is `.devctl/config.yaml`, these merge in automatically:\n\n```\n.devctl/services/<name>.yaml \u2192 services.<name> (FILENAME is the key)\n.devctl/profiles/<name>.yaml \u2192 profiles.<name>\n.devctl/proxy/routes.yaml \u2192 proxy.routes (see the wrapper below)\n```\n\nA service or profile file contains that object's body **only** \u2014 no `services:`\nwrapper, no `version:`. Getting this wrong reads oddly: a `services:` wrapper\ninside `.devctl/services/api.yaml` reports\n`unknown fields: services.api.services`.\n\n`proxy/routes.yaml` is the exception: it accepts either a full `proxy:` wrapper\nor a bare top-level `routes:` list. Both append to the same global route list.\n\n```yaml\n# .devctl/proxy/routes.yaml \u2014 wrapper form, also lets you set proxy.listen here\nproxy:\n routes:\n - name: invoices-api\n match: { host: invoices-api.local }\n upstream: { url: \"http://127.0.0.1:18000\" }\n```\n\n```yaml\n# .devctl/proxy/routes.yaml \u2014 bare form, equivalent for routes alone\nroutes:\n - name: invoices-api\n match: { host: invoices-api.local }\n upstream: { url: \"http://127.0.0.1:18000\" }\n```\n\nPick one. Both keys in the same file append **both** lists, which trips the\nduplicate-route-name check.\n\nOverlays merge after the repo config, later wins:\nbuilt-in defaults \u2192 repo `.devctl` \u2192 `~/.devctl/config.local.yaml` \u2192\n`.devctl/config.local.yaml` \u2014 the repo-local overlay wins over the home one.\nOverlays are presence-aware:\n`false`, `0` and\nempty collections in an overlay do override, so `proxy.enabled: false` in a\nlocal overlay genuinely turns the proxy off.\n\n---\n\n## Environment\n\nMerge order \u2014 later sources win:\n\n```\nprocess \u2192 profile \u2192 dotenv \u2192 generated \u2192 keychain \u2192 secret_manager \u2192 defaults \u2192 vars \u2192 runtime\n```\n\n`process`, `defaults`, `vars` and `runtime` always run. Listing\n`environment.sources` **adds** optional sources (`profile`, `dotenv`,\n`generated`, `keychain`, `secret_manager`) to that always-on set \u2014 it does not\nreplace it, and it does not reorder anything.\n\n- `dotenv` reads repo root then `working_dir`: `.env`, `.env.development`,\n `.env.local`, `.env.<profile>`.\n- `keychain` and `secret_manager` throw when listed and the fetch fails, so\n only list them when the repo genuinely uses them.\n- `environment.required` on a service fails the start if those keys are still\n empty after the whole merge \u2014 the right place to encode \"this cannot run\n without X\".\n\nRuntime values devctl injects: `SERVICE_PORT`, `SERVICE_HOST`,\n`DEVCTL_PROXY_URL`, `DEVCTL_SERVICE_NAME`, `DEVCTL_ENVIRONMENT`,\n`DEVCTL_TOKEN_URL`, `DEVCTL_INTERNAL_TOKEN`. Do not define these yourself.\n\n---\n\n## Templates\n\n```yaml\ntemplates:\n python-http:\n health: { type: http, interval_seconds: 2, timeout_seconds: 1 }\n logs: { stdout: true, stderr: true }\n restart: { policy: on_failure, max_retries: 2, backoff_seconds: 1 }\n\nservices:\n api:\n extends: python-http\n command: [python3, main.py]\n health: { url: \"http://127.0.0.1:8000/health\" } # merged field-by-field\n```\n\n- `extends` must name a template that exists.\n- Nested sections merge **field by field**, so a service can override one\n health field without restating the rest.\n- A template is not a service and is never started.\n\n---\n\n## Reading validation output\n\nEvery message names its path. Fix the path it names.\n\n| Message | Meaning |\n|---|---|\n| `at least one service must be defined` | empty/missing `services` |\n| `duplicate port N used by X and Y` | two services pinned the same port |\n| `services.X.command is required` | missing or empty command |\n| `services.X.command contains shell metacharacters` | add `shell: true` or use argv |\n| `services.X.dependencies: unknown service \"Y\"` | typo, or Y lives in a file you did not create |\n| `dependency cycle: a \u2192 b \u2192 a` | remove an edge |\n| `services.X.health.url is required for http health checks` | add `url`, or change the type |\n| `services.X.identity.service_account must be an email` | placeholder left unresolved |\n| `services.X.environment.K: unresolvable reference ${\u2026}` | referenced service or port name does not exist |\n| `services.X.capabilities: unknown capability \"c\"` | only the six listed above are accepted |\n| `profiles.P references unknown service \"S\"` | profile lists a service that is not defined |\n| `proxy.routes[i]: duplicate route name N` | often a per-service fragment colliding with a global route |\n| `proxy.routes[i].auth.audience is required when auth.type is iap` | IAP needs both audience and identity.type |\n| `proxy.routes[i].auth.client_id is only valid when auth.type is iap` | `client_id` / secret only apply to IAP user routes |\n| `proxy.routes[i].auth.client_id is required when client_secret is set` | secret without client_id |\n| `proxy.routes[i].auth.client_secret is required when client_id is set` | client_id needs a secret |\n| `proxy.routes[i].auth.client_id is only valid with identity.type user` | SA IAP uses generateIdToken, not a user OAuth client |\n| `proxy.listen.port is required when proxy.enabled is true` | pin a port |\n| `unsupported config version N (expected 1)` | `version:` must be `1` |\n| `unknown fields: services.a.depends_on` | not in the allowlists at the top of this file \u2014 usually a compose or k8s spelling |\n\nEvery message above is the validator's exact wording. `devctl config validate`\nprints one per line and exits 2; a clean run prints `configuration is valid`\nand exits 0.\n",discovery:`# Discovery: repository signal \u2192 devctl service
|
|
370
|
+
`,authoring:"# Authoring rules the loader enforces\n\nThe JSON Schema (`schema/devctl.config.schema.json`) describes the shape.\nThis file describes what the **loader and validator actually reject** \u2014 the\nrules that turn a schema-plausible config into one that fails at\n`devctl config validate`. Check anything you are unsure of here before writing.\n\n---\n\n## Unknown fields are rejected everywhere\n\nNot warned about \u2014 rejected, with the offending path named. These are the\ncomplete allowlists.\n\n**Top level:** `version` `project` `google` `profiles` `templates` `services`\n`tasks` `proxy` `logs` `auth` `shutdown` `ui` `secrets` `doctor` `plugins`\n`environment`\n\n**Service** (and `templates.<name>`, same shape): `extends` `description`\n`command` `shell` `working_dir` `dependencies` `ports` `environment` `health`\n`identity` `logs` `restart` `startup` `capabilities` `proxy` `expose` `container` `watch` `hooks`\n\n| Section | Allowed keys |\n|---|---|\n| `project` | `name` |\n| `google` | `project_id` `region` |\n| `profiles.<name>` | `services` `environment` |\n| `service.health` | `type` `url` `address` `command` `interval_seconds` `timeout_seconds` `start_period_seconds` `unhealthy_threshold` `healthy_reset_threshold` |\n| `service.hooks` | `pre_start` `post_start` |\n| `service.container` | `image` `runtime` `ports` `env` `volumes` |\n| `service.watch` | `enabled` `paths` `debounce_ms` `ignore` |\n| `tasks.<name>` | `command` `shell` `working_dir` `dependencies` `environment` |\n| `service.identity` | `type` `mode` `service_account` |\n| `service.restart` | `enabled` `policy` `max_retries` `backoff_seconds` |\n| `service.startup` | `wait_for_healthy` `timeout_seconds` |\n| `service.logs` | `stdout` `stderr` |\n| `service.environment` | `required` `defaults` + arbitrary `KEY: value` pairs |\n| `service.expose` | `enabled` `host` `port` (or the `true` shorthand) |\n| `proxy` | `enabled` `gateway` `credentials` `listen` `token_endpoint` `routes` |\n| `proxy.listen` | `host` `port` |\n| `proxy.token_endpoint` | `enabled` `host` `port` |\n| `route` | `name` `transport` `listen` `match` `upstream` `auth` |\n| `route.match` | `host` `path` |\n| `route.upstream` | `url` `service` `port` |\n| `route.auth` | `type` `identity` `audience` `service_account` `client_id` `client_secret` `credentials` `headers` |\n| `logs` | `max_memory_events` `persistence` |\n| `logs.persistence` | `enabled` `directory` `retention_days` `max_session_logs` |\n| `auth` | `refresh_threshold_seconds` |\n| `shutdown` | `stop_services_on_exit` `grace_seconds` |\n| `ui` | `theme` `keymap` |\n| `secrets` | `extra_markers` `extra_patterns` |\n| `doctor` | `tools` (each `{ name, command }`) |\n| `plugins[]` | `path` |\n| `environment` | `sources` `secrets` |\n\nThere is no `depends_on`, `build`, `replicas`, or `env_file`. Container fields\nbelong under `service.container`, not directly on the service.\n\n---\n\n## Required, at minimum\n\n- `version: 1`. Anything else: *unsupported config version N; no migration is\n available.*\n- **At least one service.** An empty or missing `services` map fails with *at\n least one service must be defined* \u2014 so a proxy-only or profiles-only config\n is not valid.\n- Every host service needs a non-empty `command`; a container service may rely on its image command.\n\n---\n\n## Ports\n\n- **Globally unique across all services.** Two services on `8000` gives\n *duplicate port 8000 used by api and worker*. This is the single most common\n failure when onboarding a repo whose services each assumed they owned the\n default port.\n- Range 1\u201365535.\n- Forms accepted: a named map (`ports: { http: 8000 }`, preferred), a list, a\n bare number, or `auto`. `auto` is exempt from the uniqueness check \u2014\n devctl allocates at start.\n- Name the port `http` when it serves HTTP.\n- Prefer the explicit `${services.X.ports.http}` reference form. The short\n `${services.X.port}` form is only unambiguous for a single-port service: once\n the service is running it prefers the assigned port named `http`, but when it\n resolves against the configuration it takes the **first port declared**,\n whatever its name. Two forms of the same reference disagreeing on a\n multi-port service is a genuinely confusing bug to chase.\n\n## References\n\n`${services.<name>.ports.<portname>}`, `${services.<name>.port}`,\n`${services.<name>.url}`, `${services.<name>.host}`, and `${identity.user}` (the\nrunning developer's detected Google email) are the supported forms. Anything\nelse \u2014 `${env.FOO}`, `${project.name}` \u2014 throws.\n\n- The referenced service must exist and the named port must be defined, or\n validation fails with *unresolvable reference*.\n- References resolve inside service `environment` values, `defaults`, profile\n environments and dotenv values, before the process starts.\n- Use them for every cross-service URL. Hard-coded ports silently break when a\n port changes or is switched to `auto`.\n\n## Dependencies\n\n- Must name a service that exists.\n- A service may not depend on itself.\n- Cycles are rejected: *dependency cycle: a \u2192 b \u2192 a*.\n- Direction: `dependencies` means \"start these first\". `start x` walks **up**\n and starts x's dependencies; `stop x` walks **down** and stops x's\n *dependents*, never its dependencies.\n- A string dependency uses `service_started`. Use `{ service: db, condition:\n service_healthy }` to wait for the dependency's configured health check.\n\n## Commands and `shell`\n\nA command whose argv contains any of `|` `||` `&&` `;` `>` `>>` `<` `&` is\nrejected unless `shell: true`:\n\n```yaml\n# rejected\ncommand: \"npm run build && npm start\"\n\n# correct\ncommand: \"npm run build && npm start\"\nshell: true\n\n# better \u2014 no shell needed\ncommand: [npm, start]\n```\n\nPrefer argv lists. A string command is split on whitespace, which mangles\narguments containing spaces.\n\n## `working_dir`\n\nRelative to the **repository root** \u2014 the directory containing `.devctl` \u2014 not\nthe process cwd, and not the `.devctl` directory itself.\n\n---\n\n## Health\n\n| `type` | Also requires |\n|---|---|\n| `http` | `url` \u2014 omit it and you get *health.url is required for http health checks* |\n| `tcp` | `address`, **or** at least one port defined on the service |\n| `command` | `health.command` (non-empty) |\n| `process` or omitted | nothing; only checks the pid is alive |\n\nAny other `type` is rejected \u2014 *health.type must be http, tcp, process, or\ncommand* \u2014 **unless** `plugins` is non-empty. That exemption is not approval:\nplugins load after validation, so the supervisor re-checks the type at boot and\nat reload. A custom type no plugin provides fails as a *reload rejection*, not\na config error, which is much harder to diagnose. Only use a custom type when\nyou can point at the plugin that registers it.\n\nDefaults when omitted: interval 2s, timeout 2s, start period 0s, unhealthy\nthreshold 3, healthy reset threshold 10. Failures during the start period do\nnot mark the service unhealthy or consume restart budget.\n\n## Identity\n\n- `type: user` (or omitted) needs nothing else.\n- `type: service_account` (or `service`) **requires** `service_account`, and it\n must contain `@` \u2014 *service_account must be an email*.\n- `mode` is an accepted alias of `type`.\n- Any other type behaves like a custom health type: allowed only with\n `plugins` set, re-checked at boot.\n\nNever fabricate an SA email. If Terraform interpolates it beyond what you can\nresolve, leave a clearly-marked placeholder and tell the user.\n\n## Restart\n\n`policy` must be `never`, `on_failure`, or `always`. `enabled: true` with no\npolicy behaves as `on_failure`.\n\n`max_retries` is a real budget: it resets on a manual stop/start/restart and\nafter a sustained healthy run, but an automatic health-triggered restart\npreserves it across its own cycle.\n\n## Capabilities\n\nDocumentation for doctor, not behaviour. Only these are accepted:\n`google` `google_api` `iap` `network` `service_identity` `local_http`.\nAnything else is rejected.\n\n---\n\n## Proxy\n\n- `proxy.listen.port` is **required** when `proxy.enabled: true`.\n- `proxy.listen.host` must be loopback (`127.0.0.1`, `localhost`, `::1`, or `127.0.0.0/8`). `0.0.0.0` and `::` are rejected.\n- `proxy.token_endpoint.host` must be loopback; `0.0.0.0` and `::` are rejected.\n- Every route needs a `name` and either `upstream.url` or `upstream.service`.\n A service reference must name a real service and an existing port (default\n port name is `http`). `service.expose` and `proxy.gateway` synthesize\n service-reference routes; they do nothing unless `proxy.enabled` is true.\n A hand-written route of the same name wins.\n- **Route names must be unique** \u2014 including names generated from per-service\n `proxy` fragments.\n- `auth.type: iap` requires **both** `audience` and `auth.identity.type`. A\n missing identity type on an IAP route is a configuration error, not a\n default.\n- Optional `client_id` plus `client_secret` mints the user IAP ID token with\n that OAuth client instead of ADC's default client. Put `${NAME}` or\n `${env.NAME}` in `client_secret` so the value is read from the environment\n at mint time (this is not service-env interpolation). These fields are\n invalid on non-IAP routes and on IAP routes whose identity is a service\n account.\n- An identity of `service` / `service_account` requires an SA email, from\n either `auth.identity.service_account` or the route's `auth.service_account`.\n- `auth.type: none` means no auth at all \u2014 any identity left on such a route is\n ignored entirely, and will not be probed at start or by doctor.\n\n### Per-service route fragments\n\nA service's `proxy` key is one route or a list. At load they append into the\n**same** global `proxy.routes` list, named `<service>` or `<service>-<n>`:\n\n```yaml\nservices:\n api:\n command: [python3, main.py]\n proxy:\n - match: { path: /api }\n upstream: { url: \"http://127.0.0.1:8000\" }\n```\n\nThat generates a route named `api`. So a service named `api` **and** a global\nroute named `api` collide \u2014 *duplicate route name api*. Watch for this when\nmixing both styles.\n\nMatching is host + optional path prefix. There is no path rewriting or\nstripping: the matched path is forwarded to the upstream as-is.\n\n---\n\n## Modular layout\n\nWhen the main file is `.devctl/config.yaml`, these merge in automatically:\n\n```\n.devctl/services/<name>.yaml \u2192 services.<name> (FILENAME is the key)\n.devctl/profiles/<name>.yaml \u2192 profiles.<name>\n.devctl/proxy/routes.yaml \u2192 proxy.routes (see the wrapper below)\n```\n\nA service or profile file contains that object's body **only** \u2014 no `services:`\nwrapper, no `version:`. Getting this wrong reads oddly: a `services:` wrapper\ninside `.devctl/services/api.yaml` reports\n`unknown fields: services.api.services`.\n\n`proxy/routes.yaml` is the exception: it accepts either a full `proxy:` wrapper\nor a bare top-level `routes:` list. Both append to the same global route list.\n\n```yaml\n# .devctl/proxy/routes.yaml \u2014 wrapper form, also lets you set proxy.listen here\nproxy:\n routes:\n - name: invoices-api\n match: { host: invoices-api.local }\n upstream: { url: \"http://127.0.0.1:18000\" }\n```\n\n```yaml\n# .devctl/proxy/routes.yaml \u2014 bare form, equivalent for routes alone\nroutes:\n - name: invoices-api\n match: { host: invoices-api.local }\n upstream: { url: \"http://127.0.0.1:18000\" }\n```\n\nPick one. Both keys in the same file append **both** lists, which trips the\nduplicate-route-name check.\n\nOverlays merge after the repo config, later wins:\nbuilt-in defaults \u2192 repo `.devctl` \u2192 `~/.devctl/config.local.yaml` \u2192\n`.devctl/config.local.yaml` \u2014 the repo-local overlay wins over the home one.\nOverlays are presence-aware:\n`false`, `0` and\nempty collections in an overlay do override, so `proxy.enabled: false` in a\nlocal overlay genuinely turns the proxy off.\n\n---\n\n## Environment\n\nMerge order \u2014 later sources win:\n\n```\nprocess \u2192 profile \u2192 dotenv \u2192 generated \u2192 keychain \u2192 secret_manager \u2192 defaults \u2192 vars \u2192 runtime\n```\n\n`process`, `defaults`, `vars` and `runtime` always run. Listing\n`environment.sources` **adds** optional sources (`profile`, `dotenv`,\n`generated`, `keychain`, `secret_manager`) to that always-on set \u2014 it does not\nreplace it, and it does not reorder anything.\n\n- `dotenv` reads repo root then `working_dir`: `.env`, `.env.development`,\n `.env.local`, `.env.<profile>`.\n- `keychain` and `secret_manager` throw when listed and the fetch fails, so\n only list them when the repo genuinely uses them.\n- `environment.required` on a service fails the start if those keys are still\n empty after the whole merge \u2014 the right place to encode \"this cannot run\n without X\".\n\nRuntime values devctl injects: `SERVICE_PORT`, `SERVICE_HOST`,\n`DEVCTL_PROXY_URL`, `DEVCTL_SERVICE_NAME`, `DEVCTL_ENVIRONMENT`,\n`DEVCTL_TOKEN_URL`, `DEVCTL_INTERNAL_TOKEN`. Do not define these yourself.\n\n---\n\n## Templates\n\n```yaml\ntemplates:\n python-http:\n health: { type: http, interval_seconds: 2, timeout_seconds: 1 }\n logs: { stdout: true, stderr: true }\n restart: { policy: on_failure, max_retries: 2, backoff_seconds: 1 }\n\nservices:\n api:\n extends: python-http\n command: [python3, main.py]\n health: { url: \"http://127.0.0.1:8000/health\" } # merged field-by-field\n```\n\n- `extends` must name a template that exists.\n- Nested sections merge **field by field**, so a service can override one\n health field without restating the rest.\n- A template is not a service and is never started.\n\n---\n\n## Reading validation output\n\nEvery message names its path. Fix the path it names.\n\n| Message | Meaning |\n|---|---|\n| `at least one service must be defined` | empty/missing `services` |\n| `duplicate port N used by X and Y` | two services pinned the same port |\n| `services.X.command is required` | missing or empty command |\n| `services.X.command contains shell metacharacters` | add `shell: true` or use argv |\n| `services.X.dependencies: unknown service \"Y\"` | typo, or Y lives in a file you did not create |\n| `dependency cycle: a \u2192 b \u2192 a` | remove an edge |\n| `services.X.health.url is required for http health checks` | add `url`, or change the type |\n| `services.X.identity.service_account must be an email` | placeholder left unresolved |\n| `services.X.environment.K: unresolvable reference ${\u2026}` | referenced service or port name does not exist |\n| `services.X.capabilities: unknown capability \"c\"` | only the six listed above are accepted |\n| `profiles.P references unknown service \"S\"` | profile lists a service that is not defined |\n| `proxy.routes[i]: duplicate route name N` | often a per-service fragment colliding with a global route |\n| `proxy.routes[i].auth.audience is required when auth.type is iap` | IAP needs both audience and identity.type |\n| `proxy.routes[i].auth.client_id is only valid when auth.type is iap` | `client_id` / secret only apply to IAP user routes |\n| `proxy.routes[i].auth.client_id is required when client_secret is set` | secret without client_id |\n| `proxy.routes[i].auth.client_secret is required when client_id is set` | client_id needs a secret |\n| `proxy.routes[i].auth.client_id is only valid with identity.type user` | SA IAP uses generateIdToken, not a user OAuth client |\n| `proxy.listen.port is required when proxy.enabled is true` | pin a port |\n| `unsupported config version N (expected 1)` | `version:` must be `1` |\n| `unknown fields: services.a.depends_on` | not in the allowlists at the top of this file \u2014 usually a compose or k8s spelling |\n\nEvery message above is the validator's exact wording. `devctl config validate`\nprints one per line and exits 2; a clean run prints `configuration is valid`\nand exits 0.\n",discovery:`# Discovery: repository signal \u2192 devctl service
|
|
371
371
|
|
|
372
372
|
How to turn what a repository contains into the service list you draft from.
|
|
373
373
|
Read the signals in roughly this order \u2014 the earlier ones describe *how the
|
|
@@ -594,7 +594,7 @@ Rules for turning that into YAML:
|
|
|
594
594
|
a full one. Remember that a bare \`devctl start\` falls back to the first
|
|
595
595
|
configured profile, and the TUI's default is the first profile
|
|
596
596
|
alphabetically \u2014 so name them with that in mind.
|
|
597
|
-
`}});var
|
|
597
|
+
`}});var zy;var Yy=I(()=>{zy=[{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
|
|
|
@@ -1794,7 +1794,7 @@ The OIDC plugin is intentionally a reference implementation: it supports client
|
|
|
1794
1794
|
- [Environment](environment.md)
|
|
1795
1795
|
- [Authentication](authentication.md)
|
|
1796
1796
|
- [Security](security.md)
|
|
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### Extra token headers\n\nSome IAP-protected upstreams want the minted token under an additional header, not just `Authorization: Bearer \u2026`. `auth.headers` injects extra request headers on a token-minting route; `${token}` in a value is replaced with the same token used for the bearer:\n\n```yaml\n auth:\n type: iap\n audience: 507686272917-0dpd...\n headers:\n identity-token: \"${token}\" # same token, second header\n x-forwarded-client: gateway # a plain literal is passed through\n```\n\nApplied only on `iap` / `service_account` routes (there is no token on a `none` route). This lets the proxy fully satisfy an upstream's auth expectations without changing the upstream or the calling service.\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### Extra token headers\n\nSome IAP-protected upstreams want the minted token under an additional header, not just `Authorization: Bearer \u2026`. `auth.headers` injects extra request headers on a token-minting route; `${token}` in a value is replaced with the same token used for the bearer:\n\n```yaml\n auth:\n type: iap\n audience: 507686272917-0dpd...\n headers:\n identity-token: \"${token}\" # same token, second header\n x-forwarded-client: gateway # a plain literal is passed through\n```\n\nApplied only on `iap` / `service_account` routes (there is no token on a `none` route). This lets the proxy fully satisfy an upstream's auth expectations without changing the upstream or the calling service.\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## gRPC routes (Temporal, and other h2 clients behind IAP)\n\nA `grpc` route is a dedicated loopback HTTP/2 (h2c) listener that forwards every gRPC stream to one upstream over HTTP/2 + TLS, injecting the route's IAP token per RPC. It's for clients that speak gRPC and can't go through the HTTP proxy \u2014 a Temporal worker, say \u2014 so they stay entirely token-free.\n\n```yaml\nproxy:\n enabled: true\n listen: { host: 127.0.0.1, port: 8080 } # the HTTP proxy (still required when enabled)\n routes:\n - name: temporal\n transport: grpc\n listen: { host: 127.0.0.1, port: 7233 } # the local address the client dials\n upstream: { url: \"https://temporal.internal.example.com:443\" }\n auth:\n type: iap\n audience: 507686272917-0dpd...\n client_id: 507686272917-4j6f...\n credentials: ~/.devctl/iap-credentials.json\n```\n\nThe client connects plaintext to the local port and does nothing else \u2014 no token, no refresh:\n\n```python\nclient = await Client.connect(\"127.0.0.1:7233\", namespace=\"prod\", tls=False)\n```\n\ndevctl adds `Authorization: Bearer <fresh id-token>` (plus any `auth.headers`) to each RPC's HTTP/2 headers, mints and refreshes it with the same machinery as HTTP routes (`audience` / `client_id` / `credentials`), and relays the response and gRPC trailers. Because every RPC carries the current token, expiry is handled with no timer in the app.\n\nNotes:\n\n- Each grpc route needs its own loopback `listen.port`, distinct from the HTTP proxy and every other grpc route, and an `https://` upstream (the IAP leg is TLS).\n- Injection only happens on an `iap` / `service_account` route; a `none` grpc route is a plain forwarder.\n- This targets a **self-hosted Temporal behind a GCP IAP HTTPS load balancer**. Temporal Cloud (mTLS + API key) is not covered by this route type.\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
|
|
1798
1798
|
|
|
1799
1799
|
From a repository that should be orchestrated:
|
|
1800
1800
|
|
|
@@ -2204,80 +2204,80 @@ Plugins can register extra health types. \`capabilities\` document intent (\`loc
|
|
|
2204
2204
|
- [Environment](environment.md)
|
|
2205
2205
|
- [Configuration](configuration.md)
|
|
2206
2206
|
- [Logs](logs.md)
|
|
2207
|
-
`},{path:"docs/troubleshooting.md",title:"Troubleshooting",body:"# Troubleshooting\n\n| Symptom | What to do |\n|---------|------------|\n| `gcloud` not installed | Install the Cloud SDK only if you need Google identity. Local-only services still run |\n| ADC unavailable | `/auth login` or `gcloud auth application-default login`, then `devctl auth status` / `/auth refresh` |\n| Wrong project | Set `google.project_id` or check `gcloud config get-value core/project`. Identity shows the source |\n| Permission denied | Ask an admin for the missing IAM role; `devctl doctor` names the resource |\n| Cannot impersonate SA | Need `roles/iam.serviceAccountTokenCreator` on that service account (group binding preferred) |\n| IAP authentication failure | Confirm audience, IAP client, and that the identity matches the route. If `client_id` is set, the ADC refresh token must belong to that OAuth client |\n| Port already in use | Doctor lists the holder. Stop a leftover, or change config. Running your own services will also show as \u201Cin use\u201D |\n| Service crashes | Open Logs, filter `ERROR` (`e`), restart with `R` |\n| Health check failure | Confirm the health URL/port; `process` checks only PID liveness |\n| Proxy unavailable / missing listen port | Pin `proxy.listen.port` (required when `proxy.enabled` is true \u2014 validate exits **2**). Then `devctl proxy start` or TUI `n`. Starting with port `0` exits **7**. Bind is loopback only |\n| Token expired | Automatic refresh uses `auth.refresh_threshold_seconds`; run `devctl auth refresh`. Open Doctor if ADC itself expired |\n| Token audience incorrect | Set `auth.audience` on the IAP route; Doctor flags missing audiences |\n| IAP used a user token for an SA route | Confirm the route identity is `service_account`; Doctor probes impersonated IAP separately |\n| Leftover process after crash | Reopen `devctl` \u2014 adopt only when pid + command + cwd + startTime match `~/.devctl/state/<hash>/state.json`. A port-only leftover is never attached |\n| `devctl attach` fails | No supervisor. Use `devctl start` first; attach never starts one |\n| `devctl status` looks empty | If the socket is down, status prints persisted state and exits 0 when nothing is running. The TUI still starts a supervisor; leftover PIDs from the previous session appear on the idle dashboard |\n| Start exits 5 or 6 | 5 = spawn failed; 6 = health never passed. Doctor then Logs |\n| `start` brought up extra services | Empty start uses the active or first profile **alphabetically**, plus dependencies \u2014 YAML key order does not matter (demo: `backend`, not `data`). Pass `--profile` or explicit names to stay narrower |\n| Docker / Podman missing or daemon down | A service in config declares `container` (the demo's `postgres` always does, even if you never start `data`). Install and start that runtime, or drop the service. Default profiles do not start postgres |\n| Doctor offers to kill a busy port that is Docker | It should not: ports owned by a running container service are healthy, and Doctor never offers to terminate the Docker or Podman daemon. Re-run Doctor after the container is up |\n| MCP tools work but nothing starts | `get_status` reports `setup_mode: true` \u2014 there is no `.devctl` yet. Have the agent call `get_setup_guide`, `search_docs`, and `validate_config`, write the files, then `reload_config` |\n| `devctl exec --print-env` hides values | Secret-like names are redacted unless you also pass `--reveal` (TUI: `/exec <svc> --print-env --reveal` or `/reveal`). MCP `exec_service` never reveals them |\n| TUI env pane looks incomplete | It now loads the same resolved map as `devctl exec --print-env`. If the chip says `config fallback`, the daemon `exec` call failed \u2014 check `/daemon` and that the supervisor is up |\n| Supervisor will not start | `devctl daemon logs` or TUI `/daemon` is the bootstrap stderr, not the service log bus |\n| TUI stale / not updating | TUI follows the event bus (20\u201350ms batch). Quit and let a new supervisor start if an old one is still listening |\n| Reload needs a restart | `devctl reload` and `/reload` list services whose command, env, ports, or identity changed |\n| Configuration invalid | `devctl config validate` \u2014 unknown fields, cycles, and missing refs fail closed. TUI `v` / `/buffer` validates before write |\n| Config on disk is broken but the TUI still opens fine | Expected: it attached to an already-running daemon and is showing its `config_snapshot` (last-known-good), not a fresh reparse of the broken file. Fix the file and `/reload` |\n| `devctl update` says unavailable | GitHub Releases API could not be reached. For npm: `npm install --global @amr-m-abdelgawad/devctl@latest`. Homebrew and GitHub binaries: see [installation](installation.md) |\n| MCP agent cannot connect | Listener is off by default. `/mcp` or `devctl mcp --on`. URL is loopback only; snippets include the bearer token |\n| `devctl: command not found` | Run `npm install --global @amr-m-abdelgawad/devctl`, then ensure npm's global binary directory is on `PATH`. See [Installation](installation.md) |\n| Bundled Bun runtime was not installed | Reinstall the npm package without `--ignore-scripts`; Bun uses its install script to select the correct platform runtime |\n\nInternal `devctl` logs appear in the Logs screen with source `devctl`.\n\n## Related\n\n- [Doctor](doctor.md)\n- [Logs](logs.md)\n- [CLI](cli.md)\n- [How it fits together](overview.md)\n"},{path:"docs/tui.md",title:"TUI",body:"# TUI\n\nThe TUI is [OpenTUI](https://opentui.com/docs/) (`@opentui/core` + `@opentui/react`). It attaches to the supervisor and never owns child processes.\n\n```bash\ncd examples/demo-platform\nnpx @amr-m-abdelgawad/devctl@latest\n```\n\nFrom a source checkout (`bun` on PATH):\n\n```bash\ncd examples/demo-platform\nbun run ../../app/src/bin.ts\n```\n\nIf a supervisor session already exists, the TUI attaches to it. Preferences: `tui.json` / `DEVCTL_TUI_CONFIG` \u2014 see [Building from source](typescript.md).\n\n## First run\n\nWith no `.devctl` configuration the TUI opens **setup**: \u201CNo configuration found. Would you like to run setup? **[Enter] Setup [Esc] Exit**\u201D. Enter starts the same 9-step wizard as `devctl setup` (OpenTUI fields, then write and attach the daemon \u2014 no process restart). Invalid existing YAML still refuses overwrite.\n\nIf a `.devctl/config.yaml` exists but fails to parse or validate, the TUI shows **Configuration error** with the actual error instead \u2014 pressing Enter here does not run setup, since that would silently overwrite the file the error is about. Fix the file and restart devctl, or run `devctl config validate` for the same error from the CLI.\n\nWhen services exist but none are running, the dashboard empty state:\n\n- `enter` starts the default profile (first profile name alphabetically) after a plan overlay\n- `n` / `x` start or stop the highlighted row (or the space-selected set)\n- a **lifecycle panel** shows start and stop waves; later start waves wait for health and do not run if a wave fails\n- the panel stays open until `esc` so you can read the result\n- `o` picks a profile, then confirms start\n\n## Quit\n\n`q` / `/exit` / `esc` twice:\n\n| `shutdown.stop_services_on_exit` | Behavior |\n|----------------------------------|----------|\n| `true` | Stop managed services and leave |\n| `false` | Detach immediately |\n| unset | Confirm: `enter` stops services, `d` detaches (daemon stays), `k` stops the daemon and leaves services (`devctl down --keep-services`), `esc` stays |\n\n**Detach** (`d`) leaves the supervisor running. **`/down --keep-services`** (or quit `k`) stops the supervisor and persists PIDs so a later start can adopt them. **`/down`** stops services and the supervisor. `/stop` only stops selected services.\n\n## Interaction model\n\nKeyboard-first. Chords use **command** on macOS and **ctrl** on Linux and Windows. Help, the status bar, and empty-state hints label the modifier for the OS you are on.\n\n| Input | What it does |\n|-------|----------------|\n| `/` | Command overlay \u2014 ranked as you type (name, alias, fuzzy, then description). `/start api` still lists `start`. `\u2191`/`\u2193` to move, `enter` to run |\n| `command+p` / `ctrl+p` | Same command overlay as `/` |\n| `command+x` / `ctrl+x` | Leader key (2s), then a shortcut \u2014 keymap overlay |\n| `?` | Grouped help \u2014 `j`/`k` scroll when the list is taller than the terminal |\n| `tab` / `shift+tab` / `1`\u2013`4` | Cycle or jump the **four nav tabs**. Other screens are `/auth`, `/credentials`, `/doctor`, `/config`, `/profiles`, `/setup`, `/stats`, `/settings`, `/mcp`. On a secondary screen, `tab` returns to the dashboard. When the strip is wider than the terminal it slides (`\u2039` `\u203A`). |\n| `s` `l` `a` `p` `d` `c` `u` | Direct letter nav when no overlay owns keys (services, logs, identity, proxy, doctor, config, setup) |\n| `r` | Refresh snapshot (doctor `r` re-runs checks) |\n| `R` | Restart selected services |\n| `j` `k` / arrows | Move selection |\n| `enter` | Start (empty dashboard) or open service detail |\n| `space` | Multi-select a service |\n| `esc` | Back / close overlay. Twice (when nothing else is open) asks to quit |\n| `f` | Focus log search (`g` jumps to latest). Remap with `keybinds.search` |\n| `z` | Expand logs to fill the terminal. `z` or `esc` exits |\n| `w` | Cycle log wrap: clip, unwrap the selected row, or wrap every long line |\n| `command+c` / `ctrl+c` | Copy the highlighted selection (drag with the mouse). Remap with `keybinds.copy` |\n| `command+=` / `ctrl+=` (and `-` / `0`) | Display size (padding/row height, not the terminal font) |\n| `esc` `esc` | Twice to quit when no overlay or back target is open. The OS copy chord does not quit |\n| Mouse | Click nav, click a service, scroll logs (toggle in Settings) |\n\nThe status bar only lists keys that work **on the current screen**. There is no idle command row \u2014 `/` and the OS palette chord open the command overlay.\n\n## Nav tabs (4)\n\n1. dashboard \xB7 2. services \xB7 3. logs \xB7 4. proxy\n\nEverything else is a slash command (or a letter jump): `/auth`, `/credentials`, `/doctor`, `/config`, `/profiles`, `/setup`, `/stats`, `/settings`. **MCP** is `/mcp`, `/agent`, or Settings \u2192 **MCP \u2192 Settings page**.\n\n## Screens\n\n- **Dashboard** \u2014 services, proxy, live log tail. Identity lives on `/auth`; ADC status is in the header. When nothing is running, a **last session** panel shows leftover PIDs from the previous supervisor (same data `devctl status` prints when the socket is down)\n- **Services** \u2014 list plus a live inspector: status chips, two-column facts, then a scrollable **resolved** env pane (dotenv, profile, secrets, plugins, runtime ports). Narrow terminals stack the panes. `enter` opens the full detail screen\n- **Service detail** \u2014 same inspector; env pane is focused so `j`/`k` scroll. `/reveal` shows secrets. `n`/`x`/`R`/`l`\n- **Logs** \u2014 ANSI color codes are stripped so wrap uses visible width; `w` cycles clip / wrap selected / wrap all. `\\\\` / `/split` opens a second pane on the same live stream (independent service filter, shared search). `/trace <id>` or Enter on a log details request id jumps search to that id. See [Logs](logs.md)\n- **Identity** \u2014 user, project, source, ADC, gcloud, configured SAs, impersonation AVAILABLE/UNAVAILABLE, IAP (no tokens). `/auth login` suspends the TUI, runs `gcloud auth application-default login` on the real terminal, then restores the TUI. `/auth logout` revokes ADC without leaving the screen\n- **Credentials** \u2014 store backend and entry names only. Tokens stay in the OS keychain or `~/.devctl/credentials`\n- **Proxy** \u2014 status + routes (match and upstream wrap instead of clipping); click a route for full details. `n` start / `x` stop\n- **Doctor** \u2014 re-runs on every visit; \u2713 / ! / \u2717 with hints. `enter` on a busy host port asks to stop that process; it never offers to kill the Docker or Podman daemon. `r` reruns\n- **Config** \u2014 merged view including **tasks**. `v` / `/buffer` opens a validate/save overlay on `cfg.configPath` (invalid YAML is not written; `esc` discards). `e` / `/edit` still opens `$EDITOR` / `DEVCTL_EDITOR`. `/diff` shows provenance (`devctl config diff`). `/reload` re-reads after an external edit\n- **Profiles** \u2014 members; `enter` selects and offers start\n- **Setup** \u2014 onboarding checklist. First-run with no config still opens here\n- **Settings** \u2014 grouped prefs: theme, display size, mouse, leader timeout, **MCP settings page**, about, reset. `\u2190`/`\u2192` writes the highlighted cycle or toggles mouse. Reset asks before restoring defaults. Saves to `~/.devctl/tui.json` unless `DEVCTL_TUI_CONFIG` is set\n- **MCP** \u2014 Listen `[ ON ]` / `[ OFF ]`, port stepper `\u2039 N \u203A`, per-agent **Copy JSON** / **Copy TOML**, and a **Tools** list grouped by purpose (inspect, logs, diagnostics, control, setup) with each tool marked `read` or `write`; `space` enables or disables the highlighted one, all on by default. Off by default. See [MCP](mcp.md)\n\n`/reveal` toggles secret env values for this session only. The header shows `secrets shown`.\n\n## Slash commands\n\n```text\n/start [service\u2026] start selection, args, or the current profile\n/stop [service\u2026]\n/down [--keep-services] stop the supervisor (and services unless --keep-services)\n/restart [--cascade|-c] named services only; cascade also restarts dependents\n R with dependents: Enter = named, c = cascade\n/run [task] one-off task; empty /run opens a picker. Output is in Logs under task:<name>\n/exec [service] -- <command\u2026>\n empty /exec opens a service picker, then type the command\n/exec <service> --print-env [--reveal]\n resolved env (dotenv, profile, secrets, plugins, ports), not config-only vars\n/logs /services /auth /credentials /proxy /mcp /doctor /config /profiles /setup\n/stats system and service statistics (sparklines when the supervisor has samples)\n/split second log pane (`\\\\`); `|` focuses the other pane\n/trace <id> set log search to a request_id / trace_id\n/dashboard return home\n/themes [name] picker with live preview; enter saves to ~/.devctl/tui.json\n/settings theme, mouse, display size, MCP page\n/filter toggle ERROR+\n/pause freeze the live log stream\n/reveal show or hide secret env values\n/wrap cycle log wrap (selected / all / clip)\n/copy copy visible logs to the clipboard\n/export [path] write filtered logs to ~/.devctl/exports (or the given path)\n/exports open the export folder\n/regex /since /until /history /edit /buffer\n/clear\n/refresh\n/reload reload .devctl\n/diff winning config sources and what they shadowed (`devctl config diff`)\n/import compose [path] [--write]\n/daemon supervisor bootstrap stderr (`devctl daemon logs`)\n/auth login|logout|refresh\n/update install a newer GitHub Release when the method is known (npm/Homebrew)\n/version current version, then the same update check (does not install)\n/exit /quit /q\n```\n\nAliases include `/up`, `/identity`, `/creds`, `/agent`, `/init`, `/home`, `/prefs`, `/task`, `/provenance`, `/bootstrap`. `/down` is no longer an alias of `/stop`.\n\n## Leader key\n\nDefault leader is `command+x` on macOS and `ctrl+x` elsewhere (2 second timeout). Then:\n\n```text\nn start x stop R restart (c cascade if dependents) s services l logs\na auth p proxy d doctor c config o profiles\nt themes e export r refresh i setup h dashboard\nq quit z fullscreen\n```\n\nOverride in `tui.json` (`keybinds`) or `DEVCTL_TUI_CONFIG`.\n\n## Layout\n\n- **Header** \u2014 product + version as text, then project and profile; chips only for running count, live proxy, MCP when on, ADC, and secrets-shown\n- **Nav** \u2014 the four primary tabs; the active tab is highlighted, not filled\n- **Body** \u2014 dashboard or a focused screen\n- **Command overlay** \u2014 `/` and `command+p` / `ctrl+p` open the same grouped list with a real OpenTUI input\n- **Status bar** \u2014 live/paused, last human result, contextual keys\n\nStatus is never color-only: `\u2713` healthy, `\u25CF` running, `!` warning, `\u2717` failed, `\u25CB` stopped.\n\n## Themes\n\n`/themes` opens a picker with live preview. Built-ins:\n\n- Product: `devctl` (default), `ember`\n- Common dark: `tokyonight`, `catppuccin`, `nord`, `gruvbox`, `kanagawa`, `dracula`, `onedark`, `monokai`, `rose-pine`, `everforest`, `github-dark`, `iceberg`, `ayu-dark`, `oxocarbon`, `night-owl`\n- Light: `catppuccin-latte`, `solarized-light`\n- Other: `solarized-dark`, `terminal` (black + VGA ANSI chrome; aliases `ansi`, `xterm`, `console`), `system` (follows macOS `AppleInterfaceStyle` / `COLORFGBG`; light uses Solarized Light)\n\nAliases: `mocha` \u2192 Catppuccin Mocha, `latte`, `one-dark`, `solarized` (dark), `rosepine`, `github`, `ayu`, `night owl`.\n\nMCP agent chips use brand colors (Claude terracotta, Cursor blue, Kilo gold, Codex green) with light/dark variants.\n\n## Related\n\n- [Logs](logs.md)\n- [MCP](mcp.md)\n- [Building from source](typescript.md)\n- [CLI](cli.md)\n"},{path:"docs/typescript.md",title:"Building from source",body:'# Building from source\n\nThe application lives in `app/`. Runtime is [Bun](https://bun.sh). The TUI is [OpenTUI](https://opentui.com/docs/).\n\n```bash\nexport PATH="$HOME/.bun/bin:$PATH"\ncd app\nbun install\nbun run src/bin.ts --help\nbun test\nbun run check:coverage\nbunx tsc --noEmit\nbun run check:architecture\nbun run check:dead\nbun run check:dup\n```\n\nFrom the repository root (after `bun install` in `app/`):\n\n```bash\nbun run app/src/bin.ts\ncd app && bun link # optional: `devctl` on PATH\n```\n\n## Layout\n\n| Path | Role |\n|------|------|\n| `app/src/bin.ts` | Entry |\n| `app/src/bootstrap/` | Composition roots (daemon + client) |\n| `app/src/application/client-runtime.ts` | Injected CLI/TUI operations and controller contract |\n| `app/src/presentation/cli/` | Commander CLI |\n| `app/src/presentation/tui/` | OpenTUI screens and overlays |\n| `app/src/presentation/tui/hooks/` | Client queries, command execution, and TUI state |\n| `app/src/presentation/tui/helpers/` | Screen-specific formatting, navigation, logs, and plan helpers |\n| `app/src/presentation/mcp/` | Streamable HTTP MCP server |\n| `app/src/domain/` | Service, identity, config, log, session, and preference types and policies |\n| `app/src/adapters/config/` | Discover, decode, merge, validate |\n| `app/src/adapters/process/` | Host process runtime |\n| `app/src/adapters/google/` | Google / IAP / tokens |\n| `app/src/adapters/daemon/supervisor.ts` | Daemon host (socket, recovery, watch, listeners) |\n| `app/src/adapters/rpc/controller.ts` | Local supervisor or socket / named-pipe client |\n| `app/tui.json` | Starter TUI preferences |\n\nLayer rules: [architecture.md](architecture.md). Check with `bun run check:architecture`. Coverage: `bun run check:coverage` (aggregate funcs/lines; OpenTUI screens and hooks are ignored). Unused files and dependencies: `bun run check:dead` ([Knip](https://knip.dev)). Copy-paste clones: `bun run check:dup` ([jscpd](https://jscpd.dev)).\n\nThere is no separate Go tree.\n\n## TUI preferences\n\nConfiguration is **`tui.json` or `tui.jsonc`**: `theme`, `keybinds`, `leader_timeout`, `font_size`, `mouse`, `scroll_speed`, `log_timestamps`, `log_metadata`, `mcp_enabled`, `mcp_port`, `mcp_disabled_tools`.\n\n`mcp_disabled_tools` is a deny-list of MCP tool names (empty means every tool is available). See [MCP](mcp.md).\n\nSearch order:\n\n```mermaid\nflowchart TB\n env{"DEVCTL_TUI_CONFIG<br/>or OPENCODE_TUI_CONFIG exists?"}\n env -->|yes| only["Use that file only"]\n env -->|no| first["First existing of<br/>./tui.jsonc \xB7 .devctl/tui.jsonc \xB7 ~/.devctl/tui.jsonc"]\n first --> merge{"~/.devctl/tui.json is a different path?"}\n merge -->|yes| overlay["Merge user file on top"]\n merge -->|no| done["Use that file"]\n```\n\nSettings writes go to `~/.devctl/tui.json` unless the env override is set (then changes apply for this session only).\n\n`keybinds` merge with the built-in defaults, so you only override what you change. Defaults use `cmd` on macOS and `ctrl` on Linux/Windows (`command+c` / `ctrl+c` in the TUI).\n\n```json\n{\n "theme": "devctl",\n "leader_timeout": 2000,\n "keybinds": {\n "leader": "ctrl+x",\n "command_list": "ctrl+p"\n },\n "mouse": true,\n "mcp_enabled": false,\n "mcp_disabled_tools": []\n}\n```\n\n## Tests\n\n```bash\ncd app && bun test\n```\n\nIntegration tests that need Google stay skipped unless credentials are present.\n\n## Related\n\n- [Installation](installation.md)\n- [TUI](tui.md)\n- [How it fits together](overview.md)\n- [Contributing](../CONTRIBUTING.md)\n'}]});function BA(){return[{path:"skills/devctl-onboard/SKILL.md",title:"Onboard procedure",body:Tn.procedure},{path:"skills/devctl-onboard/references/authoring.md",title:"Onboard authoring",body:Tn.authoring},{path:"skills/devctl-onboard/references/discovery.md",title:"Onboard discovery",body:Tn.discovery}]}function qy(){return[...Uy,...BA()]}function Vy(e){let t=e.trim();if(t==="")throw Error("path is required");let r=qy(),n=r.find((s)=>s.path===t),o=n?[n]:r.filter((s)=>s.path.endsWith(`/${t}`));if(o.length!==1)throw Error(`no unique doc for path "${t}"; available: ${r.map((s)=>s.path).join(", ")}`);let i=o[0];return{path:i.path,title:i.title,body:i.body}}function UA(e){return e.toLowerCase().split(/[^a-z0-9_]+/).filter((t)=>t.length>=HA)}function WA(e,t){let r=e.title.toLowerCase(),n=e.path.toLowerCase(),o=e.body.toLowerCase(),i=0;for(let a of t){if(r.includes(a))i+=OA;if(n.includes(a))i+=$A;let c=o.split(a).length-1;i+=Math.min(c,FA)}let s=t.join(" ");if(s.length>=jA&&o.includes(s))i+=NA;return i}function GA(e,t){let r=e.split(`
|
|
2208
|
-
`),n=0,o=-1;for(let s=0;s<r.length;s++){let a=r.slice(s,s+
|
|
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+
|
|
2210
|
-
`).trim();if(i.length<=Gy)return i;return`${i.slice(0,Gy).trimEnd()}\u2026`}function KA(e){if(e===void 0||Number.isNaN(e))return IA;return Math.min(MA,Math.max(1,Math.floor(e)))}function zy(e,t){let r=e.trim(),n=UA(r);if(n.length===0)throw Error("query is required");let o=KA(t),i=qy().map((s)=>({path:s.path,title:s.title,score:WA(s,n),snippet:GA(s.body,n)})).filter((s)=>s.score>0).sort((s,a)=>a.score-s.score||s.path.localeCompare(a.path)).slice(0,o);return{query:r,hits:i}}var IA=5,MA=10,OA=12,$A=8,FA=8,NA=15,Gy=400,Ky=5,HA=2,jA=4;var Yy=I(()=>{sp();Wy()});import{existsSync as qA,readFileSync as VA}from"fs";function An(e,t){return!(t??[]).includes(e)}function Jy(e){return _c.filter((t)=>An(t.name,e))}function Rc(e){return _c.some((t)=>t.name===e)}function ps(e){return new mr(e.secrets.extra_markers,e.secrets.extra_patterns)}function Xy(e){return Object.values(e.services).map((t)=>({name:t.name,state:t.state,health:t.health,ports:t.ports,pid:t.pid,last_error:t.last_error}))}function YA(e,t){let r=e.config(),n=r.services[t];if(!n)throw Error(`unknown service ${t}`);let i=e.status().services[t],s=ps(r);return{name:t,state:i?.state,health:i?.health,pid:i?.pid,last_error:i?.last_error,command:n.command.args,cwd:n.working_dir,ports:i?.ports??Object.fromEntries(n.ports.map((a)=>[a.name,a.value])),environment:s.redactMap({...n.environment.defaults,...n.environment.vars}),container:n.container?{...n.container,env:s.redactMap(n.container.env)}:void 0}}function Qy(e){return{session_id:e.session_id,repo_root:e.repo_root,profile:e.profile,setup_mode:e.setup_mode===!0?!0:void 0,identity:{user:e.identity.user,project:e.identity.project,adc:e.identity.adc,iap:e.identity.iap,service_accounts:e.identity.service_accounts,service_account_status:e.identity.service_account_status},proxy:{running:e.proxy.running,address:e.proxy.address,routes:e.proxy.routes},logs:e.logs,mcp:e.mcp?{running:e.mcp.running,address:e.mcp.address,port:e.mcp.port}:{running:!1}}}async function Zy(e,t){let r=typeof t.service==="string"?t.service:"",n=typeof t.since==="string"?t.since:"",o=typeof t.cursor==="string"?t.cursor:void 0,i=await e.logsPage({services:r===""?[]:[r],level:typeof t.level==="string"?t.level:"",search:typeof t.search==="string"?t.search:"",source:typeof t.source==="string"?t.source:"",since:n,until:typeof t.until==="string"?t.until:"",cursor:o,direction:o?"forward":void 0,limit:zA}),s=ps(e.config());return{events:i.events.map((a)=>({timestamp:a.timestamp,service:a.service,source:a.source,level:a.level,message:s.redactText(a.message),pid:a.pid})),truncated:i.hasPrev,has_more:i.hasNext,next_since:i.events[i.events.length-1]?.timestamp??n,next_cursor:i.nextCursor,session_changed:i.sessionChanged}}function JA(e){return Object.entries(e.profiles).sort(([t],[r])=>t.localeCompare(r)).map(([t,r])=>({name:t,services:r.services}))}function oi(e){return e.trim()===""?void 0:e}function XA(e){let t={name:e.name,match:e.match,upstream:e.upstream.url!==""?e.upstream.url:oi(e.upstream.service??"")?`service:${e.upstream.service}${e.upstream.port?`/${e.upstream.port}`:""}`:"",auth:e.auth.type},r=oi(e.auth.identity.type),n=oi(e.auth.audience),o=oi(e.auth.identity.service_account||e.auth.service_account),i=oi(e.auth.client_id),s=Ba(e.auth.client_secret),a=oi(e.auth.credentials??"");if(r)t.identity=r;if(n)t.audience=n;if(o)t.service_account=o;if(i)t.client_id=i;if(s)t.client_secret=s;if(a)t.credentials=a;let c=Object.keys(e.auth.headers??{});if(c.length>0)t.headers=c;return t}function eb(e){let t=Object.entries(e.services).map(([r,n])=>({name:r,description:n.description,command:n.command.args,cwd:n.working_dir,ports:n.ports.map((o)=>({name:o.name,value:o.auto?0:o.value,auto:o.auto})),dependencies:n.dependencies,container:n.container?{image:n.container.image,runtime:n.container.runtime||"docker",ports:n.container.ports,volumes:n.container.volumes}:void 0}));return{project:e.project.name,config_path:e.configPath,repo_root:e.repoRoot,services:t,proxy:{enabled:e.proxy.enabled,gateway:e.proxy.gateway,credentials:e.proxy.credentials,listen:{host:e.proxy.listen.host,port:e.proxy.listen.port},routes:e.proxy.routes.map((r)=>XA(r))}}}function QA(e){let t=ps(e);return{entries:Pn(e).map((r)=>{let n=typeof r.value==="string"?r.value:JSON.stringify(r.value);return{...r,value:t.redactMap({[r.path]:n})[r.path]}})}}function Pc(e){if(!Array.isArray(e))return[];return e.filter((t)=>typeof t==="string")}async function tb(e,t,r){switch(t){case"list_services":return Xy(e.status());case"get_service":if(typeof r.name!=="string"||r.name==="")throw Error("name is required");return YA(e,r.name);case"get_status":return Qy(e.status());case"get_logs":return Zy(e,r);case"list_profiles":return JA(e.config());case"get_config":return eb(e.config());case"get_config_sources":return QA(e.config());case"run_doctor":return e.doctor();case"start_services":return e.start({services:Pc(r.services),profile:typeof r.profile==="string"&&r.profile!==""?r.profile:void 0});case"stop_services":return await e.stop(Pc(r.services)),{ok:!0};case"restart_services":return await e.restart(Pc(r.services),r.cascade===!0),{ok:!0};case"reload_config":return e.reload();case"run_task":{if(typeof r.name!=="string"||r.name==="")throw Error("name is required");let n=await e.runTask(r.name),o=ps(e.config());return{...n,stdout:o.redactText(n.stdout),stderr:o.redactText(n.stderr)}}case"start_proxy":return await e.startProxy(),{ok:!0};case"stop_proxy":return await e.stopProxy(),{ok:!0};case"exec_service":{if(typeof r.service!=="string"||r.service==="")throw Error("service is required");if(!e.exec)throw Error("exec is unavailable");let n=await e.exec(r.service,Pc(r.command),r.print_env===!0),o=ps(e.config());return{...n,stdout:o.redactText(n.stdout),stderr:o.redactText(n.stderr),environment:n.environment?o.redactMap(n.environment):void 0}}case"get_setup_guide":return ZA(r);case"search_docs":return zy(typeof r.query==="string"?r.query:"",typeof r.limit==="number"?r.limit:void 0);case"get_doc":return Vy(typeof r.path==="string"?r.path:"");case"validate_config":return eL(e,r);default:throw Error(`unknown tool ${t}`)}}function ZA(e){let t=typeof e.section==="string"?e.section:"procedure",r=t in Tn?t:"procedure";return{section:r,text:Tn[r],sections:Object.keys(Tn)}}function eL(e,t){let r=e.config();if(typeof t.text==="string"){let o=e.validateConfigText(t.text);return{valid:o.length===0,issues:o,source:"candidate",config_path:r.configPath}}if(!qA(r.configPath))return{valid:!1,issues:[`no configuration at ${r.configPath}`],source:"disk",config_path:r.configPath,setup_mode:!0};let n=e.validateConfigText(VA(r.configPath,"utf8"));return{valid:n.length===0,issues:n,source:"disk",config_path:r.configPath}}function rb(e){return ap.includes(e)}async function nb(e,t){switch(t){case"devctl://status":return Qy(e.status());case"devctl://services":return Xy(e.status());case"devctl://logs":return Zy(e,{});case"devctl://config":return eb(e.config());case"devctl://doctor":return e.doctor();default:throw Error(`unknown resource ${t}`)}}var zA=200,ap,cp,_c;var fs=I(()=>{ni();Yy();sp();ap=["devctl://status","devctl://services","devctl://logs","devctl://config","devctl://doctor"],cp=["inspect","logs","diagnostics","control","setup"],_c=[{name:"list_services",label:"List services",summary:"Name, state, health, ports, pid",category:"inspect",description:"List services with state, health, ports, pid, and last error",inputSchema:{type:"object",properties:{},additionalProperties:!1}},{name:"get_service",label:"Service detail",summary:"One service with command, cwd, ports",category:"inspect",description:"One service plus command, cwd, and ports. Environment values are redacted.",inputSchema:{type:"object",properties:{name:{type:"string"}},required:["name"],additionalProperties:!1}},{name:"get_status",label:"Status",summary:"Profile, identity, proxy, log counts",category:"inspect",description:"Profile, session, identity flags (no tokens), proxy, and log counts",inputSchema:{type:"object",properties:{},additionalProperties:!1}},{name:"get_logs",label:"Read logs",summary:"Filtered log pages, secrets redacted",category:"logs",description:"Recent log lines, optionally filtered. Capped at 200 events per page. Secrets are redacted. Pass cursor=next_cursor to page forward with no duplicate or same-millisecond-lost events; since/until are plain timestamp filters for a fresh query, not a follow cursor.",inputSchema:{type:"object",properties:{service:{type:"string"},level:{type:"string"},search:{type:"string"},source:{type:"string"},since:{type:"string",description:"Only events at or after this timestamp"},until:{type:"string",description:"Only events at or before this timestamp"},cursor:{type:"string",description:"Opaque cursor from a previous response's next_cursor; continues forward from exactly there"}},additionalProperties:!1}},{name:"list_profiles",label:"List profiles",summary:"Configured profiles and their members",category:"inspect",description:"Configured profiles and their member services",inputSchema:{type:"object",properties:{},additionalProperties:!1}},{name:"get_config",label:"Read config",summary:"Merged project summary, no secret values",category:"inspect",description:"Merged project summary: services, routes, and proxy listen paths. Route auth includes type, identity, audience, and optional IAP client_id. client_secret is returned only as a ${ENV} template, never a resolved secret.",inputSchema:{type:"object",properties:{},additionalProperties:!1}},{name:"get_config_sources",label:"Config sources",summary:"Winning and shadowed config sources",category:"inspect",description:"List effective configuration values with their winning source/layer and any shadowed sources. Secret-like values are redacted.",inputSchema:{type:"object",properties:{},additionalProperties:!1}},{name:"run_doctor",label:"Run doctor",summary:"Environment diagnostics",category:"diagnostics",description:"Run environment diagnostics",inputSchema:{type:"object",properties:{},additionalProperties:!1}},{name:"start_services",label:"Start services",summary:"Start named services or a profile",category:"control",mutates:!0,description:"Start named services, or a profile when names are omitted. Empty start uses profile, then the active session profile, then the first configured profile \u2014 never every service.",inputSchema:{type:"object",properties:{services:{type:"array",items:{type:"string"}},profile:{type:"string",description:"Profile to start when services is omitted"}},additionalProperties:!1}},{name:"stop_services",label:"Stop services",summary:"Stop services and their dependents",category:"control",mutates:!0,description:"Stop named services, or all started services when names are omitted. Also stops every service that transitively depends on a named one (never a named service's own dependencies, which other running services may still need).",inputSchema:{type:"object",properties:{services:{type:"array",items:{type:"string"}}},additionalProperties:!1}},{name:"restart_services",label:"Restart services",summary:"Restart services, optionally cascading",category:"control",mutates:!0,description:"Restart named services. By default this touches only the named services, not anything that depends on them. Pass cascade=true to also restart their transitive dependents (the same set stop_services would affect).",inputSchema:{type:"object",properties:{services:{type:"array",items:{type:"string"}},cascade:{type:"boolean",description:"Also restart transitive dependents; default false restarts only the named services"}},additionalProperties:!1}},{name:"reload_config",label:"Reload config",summary:"Re-read .devctl configuration",category:"control",mutates:!0,description:"Reload .devctl configuration",inputSchema:{type:"object",properties:{},additionalProperties:!1}},{name:"run_task",label:"Run task",summary:"Run a named one-off task",category:"control",mutates:!0,description:"Run a named task from configuration. Output is also written to the log ring as task:<name>.",inputSchema:{type:"object",properties:{name:{type:"string"}},required:["name"],additionalProperties:!1}},{name:"start_proxy",label:"Start proxy",summary:"Start the local reverse proxy",category:"control",mutates:!0,description:"Start the local reverse proxy. Same as CLI `devctl proxy start` and TUI start on the proxy screen.",inputSchema:{type:"object",properties:{},additionalProperties:!1}},{name:"stop_proxy",label:"Stop proxy",summary:"Stop the local reverse proxy",category:"control",mutates:!0,description:"Stop the local reverse proxy and leave it suppressed until start_proxy (or CLI/TUI start) runs again.",inputSchema:{type:"object",properties:{},additionalProperties:!1}},{name:"exec_service",label:"Execute in service",summary:"Run a command in a resolved service context",category:"control",mutates:!0,description:"Run an arbitrary command with a service's fully resolved environment and working directory, whether or not it is running. Output and print_env values are redacted.",inputSchema:{type:"object",properties:{service:{type:"string"},command:{type:"array",items:{type:"string"}},print_env:{type:"boolean",description:"Return the resolved environment without executing a command"}},required:["service"],additionalProperties:!1}},{name:"get_setup_guide",label:"Setup guide",summary:"How to author a .devctl for this repo",category:"setup",description:"The devctl onboarding guide: how to survey a repository and author a .devctl configuration for it. Read section=procedure first; read section=authoring BEFORE writing any YAML (it carries the rules the loader rejects on, which the JSON Schema does not state); read section=discovery for mapping compose/package.json/pyproject/Terraform/k8s/.env to services. Write the files with your own editing tools \u2014 this server does not write them \u2014 then check your work with validate_config.",inputSchema:{type:"object",properties:{section:{type:"string",enum:["procedure","authoring","discovery"],description:"Which part of the guide to return; defaults to procedure"}},additionalProperties:!1}},{name:"search_docs",label:"Search docs",summary:"Search the embedded product documentation",category:"setup",description:"Search the compiled-in product documentation (docs/*.md plus the onboarding skill). Returns ranked pages with short snippets for discovery. To read a full page, pass a hit's `path` to get_doc. Use this for IAP, proxy, MCP, configuration, and similar topics; use get_setup_guide for the full onboarding procedure.",inputSchema:{type:"object",properties:{query:{type:"string",description:"Keywords or a short phrase to search for"},limit:{type:"integer",description:"Maximum hits to return (default 5, max 10)"}},required:["query"],additionalProperties:!1}},{name:"get_doc",label:"Get doc",summary:"Read a full documentation page",category:"setup",description:"Return the complete text of one embedded documentation page. Pass the `path` from a search_docs hit (e.g. docs/proxy.md) \u2014 search_docs only returns short snippets, so use this to read the whole page. A basename like proxy.md also resolves when unambiguous.",inputSchema:{type:"object",properties:{path:{type:"string",description:"Doc path from a search_docs hit, e.g. docs/proxy.md"}},required:["path"],additionalProperties:!1}},{name:"validate_config",label:"Validate config",summary:"Check configuration or a draft for errors",category:"setup",description:"Validate devctl configuration and return the exact issues the loader would report. With no arguments, validates what is on disk. Pass text to validate a candidate config.yaml before writing it \u2014 the candidate is run through the real load pipeline (modular services/profiles, overlays, templates), so it works even when no configuration exists yet. This is the only way to validate over MCP; there is no CLI round-trip needed.",inputSchema:{type:"object",properties:{text:{type:"string",description:"Candidate config.yaml contents to validate instead of the file on disk"}},additionalProperties:!1}}]});function db(e){return{...e,runDoctor:(t,r,n)=>e.runDoctor.execute(t,r,n),startupPlan:(t,r,n)=>e.getStartupPlan.execute(t,r,n),shutdownPlan:(t,r)=>e.getShutdownPlan.execute(t,r),shutdownPlanExact:(t,r)=>ic(t,r),resolveStartRequest:(t,r)=>e.resolveStart.execute(t,{...r,profile:r.profile?ur(r.profile):void 0,activeProfile:r.activeProfile===void 0?void 0:ur(r.activeProfile)})}}var ub=I(()=>{At()});function Tc(e){let t=e.toLowerCase().trim(),r=fL[t];if(r)return r;return St.includes(t)?t:"devctl"}function mL(){if(process.env.DEVCTL_THEME_LIGHT==="1")return!0;if(process.env.DEVCTL_THEME_LIGHT==="0")return!1;if(process.platform==="darwin")try{let t=Bun.spawnSync(["defaults","read","-g","AppleInterfaceStyle"],{stdout:"pipe",stderr:"pipe"}),r=t.stdout.toString();if(t.exitCode===0&&r.toLowerCase().includes("dark"))return!1;if(t.exitCode!==0)return!0}catch{}let e=process.env.COLORFGBG??"";if(e.includes(";15")||e.endsWith(";7"))return!0;if(e.includes(";0")||e.endsWith(";8"))return!1;return!1}function pb(e){let t=Tc(e);if(t==="system"){if(mL())return{...dp["solarized-light"],name:"system"};return dp.system}return dp[t]}function hr(e){switch(e.toUpperCase()){case"HEALTHY":case"OK":return"\u2713";case"UNHEALTHY":case"WARNING":case"WARN":return"!";case"FAILED":case"ERROR":return"\u2717";case"STOPPED":case"UNKNOWN":return"\u25CB";case"RUNNING":case"STARTING":case"RESTARTING":return"\u25CF";default:return"\u25CB"}}function mo(e,t){return ys(e)>=vs?Ac:t}function ys(e){let t=e.startsWith("#")?e.slice(1):e,r=Number.parseInt(t,kL);if(Number.isNaN(r))return 0;let n=r>>CL&up,o=r>>PL&up,i=r&up;return(n*bL+o*xL+i*wL)/SL}function _L(e){let t=vL;for(let r=0;r<e.length;r+=1)t^=e.charCodeAt(r),t=Math.imul(t,yL);return t>>>0}function Lc(e){return ys(e.background)>=vs}function fp(e){if(!e||!/^#[0-9a-f]{6}$/i.test(e))return!1;return ys(e)<vs}function Dc(e,t){let r=RL[e];return Lc(t)?r.light:r.dark}function fb(e,t){return ys(Dc(e,t))>=vs?EL:TL}function Ze(e,t){if(e===""||e==="all")return t.muted;let r=ys(t.background)>=vs?gL:hL;return r[_L(e)%r.length]??t.info}function AL(e,t){switch(t.toUpperCase()){case"ERROR":case"FATAL":return e.error;case"WARN":case"WARNING":return e.warning;case"INFO":return e.text;case"DEBUG":case"TRACE":return e.muted;default:return e.text}}function mb(e,t,r){if(r==="search")return e.search;if(r==="string")return e.accent;if(r==="number")return e.info;if(r==="keyword")return e.error;return AL(e,t)}function et(e,t){switch(t.toUpperCase()){case"HEALTHY":case"OK":return e.success;case"UNHEALTHY":case"WARNING":case"WARN":return e.warning;case"FAILED":case"ERROR":return e.error;case"RUNNING":case"STARTING":case"RESTARTING":return e.primary;case"INFO":return e.info;case"FATAL":return e.error;case"DEBUG":case"TRACE":case"UNKNOWN":return e.muted;default:return e.muted}}var St,pp,dp,fL,hL,gL,vL=2166136261,yL=16777619,bL=299,xL=587,wL=114,SL=1000,vs=160,kL=16,up=255,CL=16,PL=8,Ac="#1A1A1A",RL,EL="#1A1A1A",TL="#F5F5F5";var tt=I(()=>{St=["devctl","ember","tokyonight","catppuccin","catppuccin-latte","nord","gruvbox","kanagawa","dracula","onedark","monokai","solarized-dark","solarized-light","rose-pine","everforest","github-dark","iceberg","ayu-dark","oxocarbon","night-owl","terminal","voltage","voltage-light","ironclad","ironclad-light","signal","signal-light","system"],pp={devctl:"forest + mint, website palette",ember:"warm orange",tokyonight:"Tokyo Night storm blues",catppuccin:"Catppuccin Mocha","catppuccin-latte":"Catppuccin Latte (light)",nord:"Arctic Nord frost",gruvbox:"Gruvbox dark hard",kanagawa:"Kanagawa wave / gold",dracula:"classic Dracula purple",onedark:"Atom / VS Code One Dark",monokai:"Monokai Sublime","solarized-dark":"Solarized dark","solarized-light":"Solarized light","rose-pine":"Ros\xE9 Pine moon",everforest:"Everforest dark medium","github-dark":"GitHub dimmed canvas",iceberg:"Iceberg navy","ayu-dark":"Ayu Mirage-adjacent dark",oxocarbon:"IBM Oxocarbon","night-owl":"Sarah Drasner Night Owl",terminal:"black + VGA ANSI chrome",voltage:"max contrast, electric neon (dark)","voltage-light":"max contrast, electric neon (light)",ironclad:"max contrast, steel & amber (dark)","ironclad-light":"max contrast, steel & amber (light)",signal:"max contrast, traffic-light semantics (dark)","signal-light":"max contrast, traffic-light semantics (light)",system:"neutral dark (system default)"},dp={tokyonight:{name:"tokyonight",primary:"#82AAFF",accent:"#C792EA",error:"#FF5370",warning:"#FFCB6B",search:"#FFCB6B",success:"#C3E88D",info:"#89DDFF",text:"#F4F7FF",muted:"#A8B2CC",background:"#0F111A",panel:"#1A1B2E",element:"#24283B",highlight:"#2E3350",inverse:"#0F111A",border:"#3D4466",borderActive:"#82AAFF"},catppuccin:{name:"catppuccin",primary:"#89B4FA",accent:"#F5C2E7",error:"#F38BA8",warning:"#F9E2AF",search:"#F9E2AF",success:"#A6E3A1",info:"#89DCEB",text:"#E4E8F7",muted:"#A6A9BE",background:"#11111B",panel:"#1E1E2E",element:"#313244",highlight:"#45475A",inverse:"#11111B",border:"#585B70",borderActive:"#89B4FA"},"catppuccin-latte":{name:"catppuccin-latte",primary:"#1E66F5",accent:"#EA76CB",error:"#D20F39",warning:"#DF8E1D",search:"#DF8E1D",success:"#40A02B",info:"#179299",text:"#4C4F69",muted:"#6C6F85",background:"#EFF1F5",panel:"#E6E9EF",element:"#DCE0E8",highlight:"#CCD0DA",inverse:"#EFF1F5",border:"#9CA0B0",borderActive:"#1E66F5"},nord:{name:"nord",primary:"#88C0D0",accent:"#B48EAD",error:"#BF616A",warning:"#EBCB8B",search:"#EBCB8B",success:"#A3BE8C",info:"#8FBCBB",text:"#F2F5FA",muted:"#A4AEBF",background:"#2E3440",panel:"#3B4252",element:"#434C5E",highlight:"#4C566A",inverse:"#2E3440",border:"#5E6A7D",borderActive:"#88C0D0"},gruvbox:{name:"gruvbox",primary:"#FE8019",accent:"#FABD2F",error:"#FB4934",warning:"#FE8019",search:"#FE8019",success:"#B8BB26",info:"#8EC07C",text:"#F2E5BC",muted:"#C4B394",background:"#1D2021",panel:"#282828",element:"#3C3836",highlight:"#504945",inverse:"#1D2021",border:"#665C54",borderActive:"#FE8019"},kanagawa:{name:"kanagawa",primary:"#7FB4CA",accent:"#E6C384",error:"#E82424",warning:"#FFA066",search:"#FFA066",success:"#98BB6C",info:"#7FB4CA",text:"#E8E4C9",muted:"#B4C0D6",background:"#16161D",panel:"#1F1F28",element:"#2A2A37",highlight:"#363646",inverse:"#16161D",border:"#54546D",borderActive:"#E6C384"},dracula:{name:"dracula",primary:"#BD93F9",accent:"#FF79C6",error:"#FF5555",warning:"#F1FA8C",search:"#F1FA8C",success:"#50FA7B",info:"#8BE9FD",text:"#F8F8F2",muted:"#8B9DC9",background:"#21222C",panel:"#282A36",element:"#343746",highlight:"#44475A",inverse:"#21222C",border:"#6272A4",borderActive:"#BD93F9"},onedark:{name:"onedark",primary:"#61AFEF",accent:"#C678DD",error:"#E06C75",warning:"#E5C07B",search:"#E5C07B",success:"#98C379",info:"#56B6C2",text:"#ABB2BF",muted:"#9DA5B4",background:"#21252B",panel:"#282C34",element:"#2C323C",highlight:"#3E4451",inverse:"#21252B",border:"#4B5263",borderActive:"#61AFEF"},monokai:{name:"monokai",primary:"#66D9EF",accent:"#AE81FF",error:"#F92672",warning:"#E6DB74",search:"#E6DB74",success:"#A6E22E",info:"#66D9EF",text:"#F8F8F2",muted:"#A39E80",background:"#1D1E19",panel:"#272822",element:"#3E3D32",highlight:"#49483E",inverse:"#1D1E19",border:"#75715E",borderActive:"#F92672"},"solarized-dark":{name:"solarized-dark",primary:"#268BD2",accent:"#6C71C4",error:"#DC322F",warning:"#B58900",search:"#B58900",success:"#859900",info:"#2AA198",text:"#93A1A1",muted:"#586E75",background:"#002B36",panel:"#073642",element:"#0A4A58",highlight:"#1A5A68",inverse:"#002B36",border:"#586E75",borderActive:"#268BD2"},"solarized-light":{name:"solarized-light",primary:"#268BD2",accent:"#D33682",error:"#DC322F",warning:"#CB4B16",search:"#CB4B16",success:"#859900",info:"#2AA198",text:"#586E75",muted:"#839496",background:"#FDF6E3",panel:"#EEE8D5",element:"#E4DCC8",highlight:"#D5CDB6",inverse:"#FDF6E3",border:"#93A1A1",borderActive:"#268BD2"},"rose-pine":{name:"rose-pine",primary:"#C4A7E7",accent:"#EBBCBA",error:"#EB6F92",warning:"#F6C177",search:"#F6C177",success:"#31748F",info:"#9CCFD8",text:"#E0DEF4",muted:"#908CAA",background:"#191724",panel:"#1F1D2E",element:"#26233A",highlight:"#403D52",inverse:"#191724",border:"#6E6A86",borderActive:"#C4A7E7"},everforest:{name:"everforest",primary:"#7FBBB3",accent:"#D699B6",error:"#E67E80",warning:"#DBBC7F",search:"#DBBC7F",success:"#A7C080",info:"#83C092",text:"#D3C6AA",muted:"#9DA9A0",background:"#232A2E",panel:"#2D353B",element:"#343F44",highlight:"#3D484D",inverse:"#232A2E",border:"#4F585E",borderActive:"#A7C080"},"github-dark":{name:"github-dark",primary:"#58A6FF",accent:"#A371F7",error:"#F85149",warning:"#D29922",search:"#D29922",success:"#3FB950",info:"#79C0FF",text:"#E6EDF3",muted:"#8B949E",background:"#0D1117",panel:"#161B22",element:"#21262D",highlight:"#30363D",inverse:"#0D1117",border:"#30363D",borderActive:"#58A6FF"},iceberg:{name:"iceberg",primary:"#84A0C6",accent:"#A093C7",error:"#E27878",warning:"#E2A478",search:"#E2A478",success:"#B4BE82",info:"#89B8C2",text:"#C6C8D1",muted:"#9AA0B8",background:"#161821",panel:"#1E2132",element:"#2A3150",highlight:"#3D425B",inverse:"#161821",border:"#444B71",borderActive:"#84A0C6"},"ayu-dark":{name:"ayu-dark",primary:"#59C2FF",accent:"#FFB454",error:"#D95757",warning:"#FF8F40",search:"#FF8F40",success:"#C2D94C",info:"#95E6CB",text:"#B3B1AD",muted:"#8B939C",background:"#0A0E14",panel:"#0D1017",element:"#131721",highlight:"#1B212C",inverse:"#0A0E14",border:"#2D3640",borderActive:"#FFB454"},oxocarbon:{name:"oxocarbon",primary:"#78A9FF",accent:"#EE5396",error:"#FA4D56",warning:"#F1C21B",search:"#F1C21B",success:"#42BE65",info:"#3DDBD9",text:"#F2F4F8",muted:"#A8A8A8",background:"#161616",panel:"#262626",element:"#393939",highlight:"#525252",inverse:"#161616",border:"#6F6F6F",borderActive:"#78A9FF"},"night-owl":{name:"night-owl",primary:"#82AAFF",accent:"#C792EA",error:"#EF5350",warning:"#ECC48D",search:"#ECC48D",success:"#ADDB67",info:"#7FDBCA",text:"#D6DEEB",muted:"#8FA4A4",background:"#011627",panel:"#0B2942",element:"#1D3B53",highlight:"#1E4B6B",inverse:"#011627",border:"#5F7E97",borderActive:"#82AAFF"},terminal:{name:"terminal",primary:"#55FFFF",accent:"#FF55FF",error:"#FF5555",warning:"#FFFF55",search:"#FFFF55",success:"#55FF55",info:"#5555FF",text:"#C0C0C0",muted:"#808080",background:"#000000",panel:"#000000",element:"#000000",highlight:"#404040",inverse:"#000000",border:"#555555",borderActive:"#55FFFF"},voltage:{name:"voltage",primary:"#00E5FF",accent:"#FF3EA5",error:"#FF1744",warning:"#FFEA00",search:"#FFEA00",success:"#00E676",info:"#40C4FF",text:"#FFFFFF",muted:"#C0C0C0",background:"#000000",panel:"#000000",element:"#0D0D0D",highlight:"#262626",inverse:"#000000",border:"#808080",borderActive:"#FFFFFF"},"voltage-light":{name:"voltage-light",primary:"#0057B7",accent:"#C2185B",error:"#D50000",warning:"#FF6D00",search:"#FF6D00",success:"#2E7D32",info:"#00838F",text:"#000000",muted:"#424242",background:"#FFFFFF",panel:"#FFFFFF",element:"#EDEDED",highlight:"#D6D6D6",inverse:"#FFFFFF",border:"#616161",borderActive:"#000000"},ironclad:{name:"ironclad",primary:"#4FC3F7",accent:"#FFD54F",error:"#FF5252",warning:"#FFB300",search:"#FFB300",success:"#69F0AE",info:"#64B5F6",text:"#FFFFFF",muted:"#BDBDBD",background:"#000000",panel:"#000000",element:"#101010",highlight:"#242424",inverse:"#000000",border:"#757575",borderActive:"#4FC3F7"},"ironclad-light":{name:"ironclad-light",primary:"#01579B",accent:"#E65100",error:"#B71C1C",warning:"#EF6C00",search:"#EF6C00",success:"#1B5E20",info:"#0277BD",text:"#000000",muted:"#4A4A4A",background:"#FFFFFF",panel:"#FFFFFF",element:"#EFEFEF",highlight:"#DADADA",inverse:"#FFFFFF",border:"#6E6E6E",borderActive:"#01579B"},signal:{name:"signal",primary:"#29B6F6",accent:"#AB47BC",error:"#FF0000",warning:"#FFC400",search:"#FFC400",success:"#00E400",info:"#00B8D4",text:"#FFFFFF",muted:"#B3B3B3",background:"#000000",panel:"#000000",element:"#0A0A0A",highlight:"#202020",inverse:"#000000",border:"#8A8A8A",borderActive:"#29B6F6"},"signal-light":{name:"signal-light",primary:"#01579B",accent:"#6A1B9A",error:"#C62828",warning:"#F9A825",search:"#F9A825",success:"#2E7D32",info:"#00796B",text:"#000000",muted:"#3D3D3D",background:"#FFFFFF",panel:"#FFFFFF",element:"#ECECEC",highlight:"#D2D2D2",inverse:"#FFFFFF",border:"#5C5C5C",borderActive:"#01579B"},system:{name:"system",primary:"#60A5FA",accent:"#C084FC",error:"#F87171",warning:"#FBBF24",search:"#FBBF24",success:"#34D399",info:"#22D3EE",text:"#F9FAFB",muted:"#C4CAD3",background:"#030712",panel:"#111827",element:"#1F2937",highlight:"#374151",inverse:"#030712",border:"#4B5563",borderActive:"#60A5FA"},devctl:{name:"devctl",primary:"#77DDBA",accent:"#9EE9CF",error:"#F28B91",warning:"#E8C586",search:"#E8C586",success:"#91D6A0",info:"#8EC8D8",text:"#DEEEE5",muted:"#A4B9AE",background:"#15201D",panel:"#202923",element:"#293B32",highlight:"#365448",inverse:"#15201D",border:"#526D60",borderActive:"#77DDBA"},ember:{name:"ember",primary:"#FF9F43",accent:"#2EE6FF",error:"#FF4D6D",warning:"#FFE566",search:"#FFE566",success:"#3DFF9A",info:"#2EE6FF",text:"#F7F9FF",muted:"#B0B8CC",background:"#07090F",panel:"#12161F",element:"#1A2130",highlight:"#243044",inverse:"#0B0D12",border:"#334155",borderActive:"#FF9F43"}},fL={opencode:"ember",light:"system",mocha:"catppuccin",latte:"catppuccin-latte","one-dark":"onedark","one dark":"onedark",solarized:"solarized-dark",rosepine:"rose-pine","rose pine":"rose-pine",github:"github-dark",ayu:"ayu-dark","night owl":"night-owl",ansi:"terminal",xterm:"terminal",console:"terminal",classic:"terminal",vga:"terminal",contrast:"voltage","contrast-light":"voltage-light",hc:"voltage","hc-light":"voltage-light",a11y:"voltage","a11y-light":"voltage-light"};hL=["#7AA2F7","#9ECE6A","#E0AF68","#BB9AF7","#2AC3DE","#FF9E64","#F7768E","#73DACA","#C0CAF5","#FF79C6","#7DCFFF","#C3E88D"],gL=["#2E5AAC","#3D7A1F","#9A6B12","#6B3FA0","#0E7A86","#B85C1A","#B4234A","#1A7A6D","#4A5080","#9A3480","#1565C0","#4A7C19"];RL={claude:{dark:"#E08A6A",light:"#B54328"},cursor:{dark:"#B8D4FF",light:"#1E4EBF"},kilo:{dark:"#F0C94A",light:"#8A6400"},codex:{dark:"#4ADE80",light:"#0B7A4B"}}});var Rr=I(()=>{ta()});function vb(e){return e!==void 0}function hb(e){let t=e/1000;if(Number.isInteger(t))return`${t}s`;return`${e}ms`}function gp(e,t,r){let n=e.indexOf(t),i=((n<0?0:n)+r+e.length)%e.length;return e[i]??e[0]}function yb(e,t){let r=[...St],n=r.includes(e)?e:hp;return gp(r,n,t)}function bb(e,t){let r=[...LL],n=r.reduce((o,i)=>Math.abs(i-e)<Math.abs(o-e)?i:o,r[0]);return gp(r,n,t)}function bs(e){let t=[...mp];return t.reduce((r,n)=>Math.abs(n-e)<Math.abs(r-e)?n:r,t[0])}function xs(e,t){return gp([...mp],bs(e),t)}function ws(e){let t=bs(e),r=gb[t]??gb[Lo];return{px:t,...r}}function ho(e){return e.label==="compact"}function Ss(e){return e.steps<=2}function xb(e){return mp.map((r,n)=>n<e?"\u2588":"\u2591").join(" ")}function Ic(e){return ws(e).label}function wb(e){let t=e.locked?"Applies this session only. DEVCTL_TUI_CONFIG overrides the user file.":`Saved to ${e.configPath}.`;return[{id:"theme",group:"Appearance",kind:"cycle",name:"Theme",value:e.themeName,hint:"\u2190 \u2192 save enter picker",detail:`Arrows write the theme to the user file. Enter opens the picker. ${t}`},{id:"font",group:"Appearance",kind:"cycle",name:"Display size",value:Ic(e.fontSize),hint:"\u2190 \u2192 save",detail:`Scales content padding and list-row height. Default keeps a thin rule between chrome and panes. Compact sits toolbars flush against borders. Header, nav, and status stay one line. Does not change the terminal font. ${t}`},{id:"mouse",group:"Input",kind:"toggle",name:"Mouse",value:e.mouse?"on":"off",hint:"space or enter toggle",detail:`Clicks on nav and lists. Restart the TUI after a change. ${t}`},{id:"leader",group:"Input",kind:"cycle",name:"Leader",value:hb(e.leaderMs),hint:"\u2190 \u2192 save",detail:`How long ${It("x")} waits for the next key. ${t}`},{id:"mcp",group:"MCP",kind:"page",name:"Settings page",value:e.mcpRunning?"running \u2192 /mcp":"off \u2192 /mcp",hint:"enter open the MCP page",detail:"Opens a dedicated MCP page (also /mcp or /agent). Start or stop the localhost server, change the port, and copy Claude, Cursor, Codex, and Kilo Code config."},{id:"file",group:"About",kind:"info",name:"File",value:e.configPath,hint:"read-only",detail:e.locked?`Using ${e.configPath} from DEVCTL_TUI_CONFIG.`:`User preferences live in ${e.configPath}.`},{id:"version",group:"About",kind:"info",name:"Version",value:jt,hint:"read-only",detail:er()},{id:"reset",group:"About",kind:"action",name:"Reset",value:"defaults",hint:"enter confirm reset",detail:`Restore theme ${hp}, display ${Ic(Lo)}, mouse on, leader ${hb(sd)}. Enter asks first. ${t}`}]}function Mc(){return{theme:hp,mouse:!0,leader_timeout:sd,font_size:Lo}}function Sb(e){return["Appearance","Input","MCP","About"].flatMap((r)=>{let n=e.filter((o)=>o.group===r);if(n.length===0)return[];return[{group:r,items:n}]})}function vp(e,t){if(e.length===0)return 0;return Math.min(Math.max(t,0),e.length-1)}function Ln(e,t){return e[vp(e,t)]}function yp(e,t){return e??t}var LL,mp,hp="devctl",gb;var Er=I(()=>{tt();Rr();LL=[1000,2000,3000],mp=[12,14,16,18,20,22];gb={12:{pad:0,rowH:1,gap:0,steps:1,label:"compact"},14:{pad:0,rowH:1,gap:0,steps:2,label:"default"},16:{pad:1,rowH:1,gap:1,steps:3,label:"comfortable"},18:{pad:1,rowH:2,gap:1,steps:4,label:"large"},20:{pad:2,rowH:2,gap:1,steps:5,label:"xl"},22:{pad:2,rowH:2,gap:2,steps:6,label:"xxl"}}});import{createContext as DL,useContext as IL}from"react";function ye(){return IL(bp)}var bp;var Mt=I(()=>{Er();Rr();bp=DL(ws(Lo))});function Re(e,t){if(t<=0)return"";if(e.length<=t)return e;if(t===1)return"\u2026";return`${e.slice(0,t-1)}\u2026`}function oe(e,t){if(t<=0)return"";if(e.length>=t)return Re(e,t);return e.padEnd(t)}function si(e,t=20){let r=Number.isFinite(e)?Math.min(1,Math.max(0,e)):0,n=Math.round(r*t);return"\u2588".repeat(n)+"\u2591".repeat(Math.max(0,t-n))}function ks(e){if(!Number.isFinite(e)||e<xp)return"< 1s";let t=Math.floor(e/kb),r=Math.floor(e%kb/Sp),n=Math.floor(e%Sp/wp),o=Math.floor(e%wp/xp);if(t>0)return`${t}d ${r}h`;if(r>0)return`${r}h ${n}m`;if(n>0)return`${n}m ${String(o).padStart(2,"0")}s`;return`${o}s`}function Cs(e){if(!Number.isFinite(e)||e<0)return"\u2014";let t=e/ML;if(t>=Cb)return`${(t/Cb).toFixed(1)}G`;if(t>=1)return`${Math.round(t)}M`;return`${Math.round(e)}K`}function Pb(e){if(!Number.isFinite(e)||e<0)return"\u2014";return`${e.toFixed(1)}%`}var xp=1000,wp,Sp,kb,ML=1024,Cb=1024;var Ot=I(()=>{wp=60*xp,Sp=60*wp,kb=24*Sp});function Cp(e){return e>=FL}function Pp(e){return e>=NL}function _p(e){return e>=HL}function Nc(e,t=0){return Math.max(1,e-Rb-t*2)}function Eb(e){let t=_b+Tr+$t;if(Cp(e))t+=Ur;if(Pp(e))t+=$c;if(_p(e))t+=an;return Math.max(Fc,e-t)}function Hc(e,t,r){if(r)return e;let n=t.reduce((a,c)=>Math.max(a,c.length),0),o=Math.min(kp,Math.max(Fc,n+OL)),i=_b+Tr+$t+o+Rb+$L,s=Math.max(Oc,Math.floor(e*jL));return Math.min(s,Math.max(Oc,i))}function Tb(e){if(!e)return"";return Object.keys(e.profiles).sort()[0]??""}function jc(e){if(!e)return!0;let t=Object.values(e.services);if(t.length===0)return!0;return!t.some((r)=>vo(r))}function Rp(e,t){if(e.length>0)return e;return[...t]}function Bc(e,t){if(e.length>0)return[...e];return t===""?[]:[t]}function Uc(e){return e.command.args.join(" ")||"\u2014"}function Es(e,t){let r=go(t);if(r!=="")return r;if(e.ports.length===0)return"\u2014";return e.ports.map((n)=>`${n.name}:${n.auto?"auto":n.value}`).join(" ")}function Wc(e){let t=e.health.type||"none",r=e.health.url||e.health.address;return r===""?t:`${t} ${r}`}function Gc(e,t){if(t?.identity)return t.identity;let r=e.identity.type||"none",n=e.identity.service_account;return n===""?r:`${r} ${n}`}function Kc(e){let t=e.restart.policy||"none";if(e.restart.max_retries>0)return`${t} \xD7${e.restart.max_retries}`;return t}function Ab(e,t,r,n,o){let i={...e.environment.defaults,...e.environment.vars},a=BL(o??i,t,r,n);return[...new Set([...Object.keys(a),...e.environment.required])].map((l)=>({key:l,value:a[l]??"",required:e.environment.required.includes(l),fromConfig:Object.prototype.hasOwnProperty.call(i,l)})).sort((l,d)=>l.fromConfig===d.fromConfig?l.key.localeCompare(d.key):l.fromConfig?-1:1)}function Lb(e,t){if(!e||e.processes.length===0)return;if(t!==void 0&&t!==""&&e.session_id===t)return;return e}function go(e){if(!e)return"";if(e.ports.http!==void 0)return String(e.ports.http);let t=Object.values(e.ports)[0];return t!==void 0?String(t):""}function tr(e){if(!e)return"STOPPED";return Jo(e)}function BL(e,t,r,n){if(t)return e;return new mr(r,n).redactMap(e)}function Ts(e){return UL.has(e)}function vo(e){if(!e)return!1;return Ts(e.state)}var _b=8,Tr=12,Ur=10,$c=8,an=8,Ps=9,_s=7,Rs=8,Fc=12,kp=40,$t=2,OL=2,Rb=2,$L=2,Oc=34,FL=48,NL=60,HL=72,jL=0.48,UL;var Lt=I(()=>{At();ni();UL=new Set(["RUNNING","STARTING","RESTARTING","HEALTHY","UNHEALTHY"])});function Yc(e){return e===ma?"\u2014":e}function Ep(e,t,r){let n=r?Ib:WL+Ib+Math.max(0,t)*2;return Math.max(1,e-n)}function VL(e){if(e.startsWith('"')||e.startsWith("'"))return"string";if(/^\d+$/.test(e))return"number";return"keyword"}function Jc(e){return e.replace(zL,"")}function Fb(e){try{return JSON.stringify(JSON.parse(e),null,2)}catch{return e}}function Xc(e,t){let r=Math.max(1,t),n=Jc(e).replace(/\r\n/g,`
|
|
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 QA(){return[{path:"skills/devctl-onboard/SKILL.md",title:"Onboard procedure",body:Ln.procedure},{path:"skills/devctl-onboard/references/authoring.md",title:"Onboard authoring",body:Ln.authoring},{path:"skills/devctl-onboard/references/discovery.md",title:"Onboard discovery",body:Ln.discovery}]}function Qy(){return[...zy,...QA()]}function Zy(e){let t=e.trim();if(t==="")throw Error("path is required");let r=Qy(),n=r.find((s)=>s.path===t),o=n?[n]:r.filter((s)=>s.path.endsWith(`/${t}`));if(o.length!==1)throw Error(`no unique doc for path "${t}"; available: ${r.map((s)=>s.path).join(", ")}`);let i=o[0];return{path:i.path,title:i.title,body:i.body}}function ZA(e){return e.toLowerCase().split(/[^a-z0-9_]+/).filter((t)=>t.length>=JA)}function eL(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+=qA;if(n.includes(a))i+=VA;let c=o.split(a).length-1;i+=Math.min(c,zA)}let s=t.join(" ");if(s.length>=XA&&o.includes(s))i+=YA;return i}function tL(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+Xy).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+Xy).join(`
|
|
2210
|
+
`).trim();if(i.length<=Jy)return i;return`${i.slice(0,Jy).trimEnd()}\u2026`}function rL(e){if(e===void 0||Number.isNaN(e))return GA;return Math.min(KA,Math.max(1,Math.floor(e)))}function eb(e,t){let r=e.trim(),n=ZA(r);if(n.length===0)throw Error("query is required");let o=rL(t),i=Qy().map((s)=>({path:s.path,title:s.title,score:eL(s,n),snippet:tL(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 GA=5,KA=10,qA=12,VA=8,zA=8,YA=15,Jy=400,Xy=5,JA=2,XA=4;var tb=I(()=>{pp();Yy()});import{existsSync as nL,readFileSync as oL}from"fs";function Dn(e,t){return!(t??[]).includes(e)}function rb(e){return Lc.filter((t)=>Dn(t.name,e))}function Dc(e){return Lc.some((t)=>t.name===e)}function ys(e){return new hr(e.secrets.extra_markers,e.secrets.extra_patterns)}function nb(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 sL(e,t){let r=e.config(),n=r.services[t];if(!n)throw Error(`unknown service ${t}`);let i=e.status().services[t],s=ys(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 ob(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 ib(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:iL}),s=ys(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 aL(e){return Object.entries(e.profiles).sort(([t],[r])=>t.localeCompare(r)).map(([t,r])=>({name:t,services:r.services}))}function ai(e){return e.trim()===""?void 0:e}function cL(e){let t={name:e.name,match:e.match,upstream:e.upstream.url!==""?e.upstream.url:ai(e.upstream.service??"")?`service:${e.upstream.service}${e.upstream.port?`/${e.upstream.port}`:""}`:"",auth:e.auth.type},r=ai(e.auth.identity.type),n=ai(e.auth.audience),o=ai(e.auth.identity.service_account||e.auth.service_account),i=ai(e.auth.client_id),s=za(e.auth.client_secret),a=ai(e.auth.credentials??"");if(r)t.identity=r;if(n)t.audience=n;if(o)t.service_account=o;if(i)t.client_id=i;if(s)t.client_secret=s;if(a)t.credentials=a;let c=Object.keys(e.auth.headers??{});if(c.length>0)t.headers=c;return t}function sb(e){let t=Object.entries(e.services).map(([r,n])=>({name:r,description:n.description,command:n.command.args,cwd:n.working_dir,ports:n.ports.map((o)=>({name:o.name,value:o.auto?0:o.value,auto:o.auto})),dependencies:n.dependencies,container:n.container?{image:n.container.image,runtime:n.container.runtime||"docker",ports:n.container.ports,volumes:n.container.volumes}:void 0}));return{project:e.project.name,config_path:e.configPath,repo_root:e.repoRoot,services:t,proxy:{enabled:e.proxy.enabled,gateway:e.proxy.gateway,credentials:e.proxy.credentials,listen:{host:e.proxy.listen.host,port:e.proxy.listen.port},routes:e.proxy.routes.map((r)=>cL(r))}}}function lL(e){let t=ys(e);return{entries:Rn(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 Ac(e){if(!Array.isArray(e))return[];return e.filter((t)=>typeof t==="string")}async function ab(e,t,r){switch(t){case"list_services":return nb(e.status());case"get_service":if(typeof r.name!=="string"||r.name==="")throw Error("name is required");return sL(e,r.name);case"get_status":return ob(e.status());case"get_logs":return ib(e,r);case"list_profiles":return aL(e.config());case"get_config":return sb(e.config());case"get_config_sources":return lL(e.config());case"run_doctor":return e.doctor();case"start_services":return e.start({services:Ac(r.services),profile:typeof r.profile==="string"&&r.profile!==""?r.profile:void 0});case"stop_services":return await e.stop(Ac(r.services)),{ok:!0};case"restart_services":return await e.restart(Ac(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=ys(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,Ac(r.command),r.print_env===!0),o=ys(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 dL(r);case"search_docs":return eb(typeof r.query==="string"?r.query:"",typeof r.limit==="number"?r.limit:void 0);case"get_doc":return Zy(typeof r.path==="string"?r.path:"");case"validate_config":return uL(e,r);default:throw Error(`unknown tool ${t}`)}}function dL(e){let t=typeof e.section==="string"?e.section:"procedure",r=t in Ln?t:"procedure";return{section:r,text:Ln[r],sections:Object.keys(Ln)}}function uL(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(!nL(r.configPath))return{valid:!1,issues:[`no configuration at ${r.configPath}`],source:"disk",config_path:r.configPath,setup_mode:!0};let n=e.validateConfigText(oL(r.configPath,"utf8"));return{valid:n.length===0,issues:n,source:"disk",config_path:r.configPath}}function cb(e){return mp.includes(e)}async function lb(e,t){switch(t){case"devctl://status":return ob(e.status());case"devctl://services":return nb(e.status());case"devctl://logs":return ib(e,{});case"devctl://config":return sb(e.config());case"devctl://doctor":return e.doctor();default:throw Error(`unknown resource ${t}`)}}var iL=200,mp,fp,Lc;var bs=I(()=>{si();tb();pp();mp=["devctl://status","devctl://services","devctl://logs","devctl://config","devctl://doctor"],fp=["inspect","logs","diagnostics","control","setup"],Lc=[{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 gb(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)=>dc(t,r),resolveStartRequest:(t,r)=>e.resolveStart.execute(t,{...r,profile:r.profile?pr(r.profile):void 0,activeProfile:r.activeProfile===void 0?void 0:pr(r.activeProfile)})}}var vb=I(()=>{At()});function Mc(e){let t=e.toLowerCase().trim(),r=CL[t];if(r)return r;return St.includes(t)?t:"devctl"}function PL(){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 yb(e){let t=Mc(e);if(t==="system"){if(PL())return{...gp["solarized-light"],name:"system"};return gp.system}return gp[t]}function gr(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 go(e,t){return Cs(e)>=ks?Oc:t}function Cs(e){let t=e.startsWith("#")?e.slice(1):e,r=Number.parseInt(t,ML);if(Number.isNaN(r))return 0;let n=r>>OL&vp,o=r>>$L&vp,i=r&vp;return(n*AL+o*LL+i*DL)/IL}function FL(e){let t=EL;for(let r=0;r<e.length;r+=1)t^=e.charCodeAt(r),t=Math.imul(t,TL);return t>>>0}function $c(e){return Cs(e.background)>=ks}function bp(e){if(!e||!/^#[0-9a-f]{6}$/i.test(e))return!1;return Cs(e)<ks}function Fc(e,t){let r=NL[e];return $c(t)?r.light:r.dark}function bb(e,t){return Cs(Fc(e,t))>=ks?HL:jL}function Ze(e,t){if(e===""||e==="all")return t.muted;let r=Cs(t.background)>=ks?_L:RL;return r[FL(e)%r.length]??t.info}function BL(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 xb(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 BL(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,yp,gp,CL,RL,_L,EL=2166136261,TL=16777619,AL=299,LL=587,DL=114,IL=1000,ks=160,ML=16,vp=255,OL=16,$L=8,Oc="#1A1A1A",NL,HL="#1A1A1A",jL="#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"],yp={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)"},gp={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"}},CL={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"};RL=["#7AA2F7","#9ECE6A","#E0AF68","#BB9AF7","#2AC3DE","#FF9E64","#F7768E","#73DACA","#C0CAF5","#FF79C6","#7DCFFF","#C3E88D"],_L=["#2E5AAC","#3D7A1F","#9A6B12","#6B3FA0","#0E7A86","#B85C1A","#B4234A","#1A7A6D","#4A5080","#9A3480","#1565C0","#4A7C19"];NL={claude:{dark:"#E08A6A",light:"#B54328"},cursor:{dark:"#B8D4FF",light:"#1E4EBF"},kilo:{dark:"#F0C94A",light:"#8A6400"},codex:{dark:"#4ADE80",light:"#0B7A4B"}}});var Lr=I(()=>{aa()});function kb(e){return e!==void 0}function wb(e){let t=e/1000;if(Number.isInteger(t))return`${t}s`;return`${e}ms`}function Sp(e,t,r){let n=e.indexOf(t),i=((n<0?0:n)+r+e.length)%e.length;return e[i]??e[0]}function Cb(e,t){let r=[...St],n=r.includes(e)?e:wp;return Sp(r,n,t)}function Pb(e,t){let r=[...UL],n=r.reduce((o,i)=>Math.abs(i-e)<Math.abs(o-e)?i:o,r[0]);return Sp(r,n,t)}function Ps(e){let t=[...xp];return t.reduce((r,n)=>Math.abs(n-e)<Math.abs(r-e)?n:r,t[0])}function Rs(e,t){return Sp([...xp],Ps(e),t)}function _s(e){let t=Ps(e),r=Sb[t]??Sb[Io];return{px:t,...r}}function vo(e){return e.label==="compact"}function Es(e){return e.steps<=2}function Rb(e){return xp.map((r,n)=>n<e?"\u2588":"\u2591").join(" ")}function Nc(e){return _s(e).label}function _b(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:Nc(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:wb(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:tr()},{id:"reset",group:"About",kind:"action",name:"Reset",value:"defaults",hint:"enter confirm reset",detail:`Restore theme ${wp}, display ${Nc(Io)}, mouse on, leader ${wb(ud)}. Enter asks first. ${t}`}]}function Hc(){return{theme:wp,mouse:!0,leader_timeout:ud,font_size:Io}}function Eb(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 kp(e,t){if(e.length===0)return 0;return Math.min(Math.max(t,0),e.length-1)}function In(e,t){return e[kp(e,t)]}function Cp(e,t){return e??t}var UL,xp,wp="devctl",Sb;var Dr=I(()=>{tt();Lr();UL=[1000,2000,3000],xp=[12,14,16,18,20,22];Sb={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 WL,useContext as GL}from"react";function be(){return GL(Pp)}var Pp;var Mt=I(()=>{Dr();Lr();Pp=WL(_s(Io))});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 li(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 Ts(e){if(!Number.isFinite(e)||e<Rp)return"< 1s";let t=Math.floor(e/Tb),r=Math.floor(e%Tb/Ep),n=Math.floor(e%Ep/_p),o=Math.floor(e%_p/Rp);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 As(e){if(!Number.isFinite(e)||e<0)return"\u2014";let t=e/KL;if(t>=Ab)return`${(t/Ab).toFixed(1)}G`;if(t>=1)return`${Math.round(t)}M`;return`${Math.round(e)}K`}function Lb(e){if(!Number.isFinite(e)||e<0)return"\u2014";return`${e.toFixed(1)}%`}var Rp=1000,_p,Ep,Tb,KL=1024,Ab=1024;var Ot=I(()=>{_p=60*Rp,Ep=60*_p,Tb=24*Ep});function Ap(e){return e>=zL}function Lp(e){return e>=YL}function Dp(e){return e>=JL}function Wc(e,t=0){return Math.max(1,e-Ib-t*2)}function Mb(e){let t=Db+Ir+$t;if(Ap(e))t+=Wr;if(Lp(e))t+=Bc;if(Dp(e))t+=ln;return Math.max(Uc,e-t)}function Gc(e,t,r){if(r)return e;let n=t.reduce((a,c)=>Math.max(a,c.length),0),o=Math.min(Tp,Math.max(Uc,n+qL)),i=Db+Ir+$t+o+Ib+VL,s=Math.max(jc,Math.floor(e*XL));return Math.min(s,Math.max(jc,i))}function Ob(e){if(!e)return"";return Object.keys(e.profiles).sort()[0]??""}function Kc(e){if(!e)return!0;let t=Object.values(e.services);if(t.length===0)return!0;return!t.some((r)=>bo(r))}function Ip(e,t){if(e.length>0)return e;return[...t]}function qc(e,t){if(e.length>0)return[...e];return t===""?[]:[t]}function Vc(e){return e.command.args.join(" ")||"\u2014"}function Ms(e,t){let r=yo(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 zc(e){let t=e.health.type||"none",r=e.health.url||e.health.address;return r===""?t:`${t} ${r}`}function Yc(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 Jc(e){let t=e.restart.policy||"none";if(e.restart.max_retries>0)return`${t} \xD7${e.restart.max_retries}`;return t}function $b(e,t,r,n,o){let i={...e.environment.defaults,...e.environment.vars},a=QL(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 Fb(e,t){if(!e||e.processes.length===0)return;if(t!==void 0&&t!==""&&e.session_id===t)return;return e}function yo(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 rr(e){if(!e)return"STOPPED";return Qo(e)}function QL(e,t,r,n){if(t)return e;return new hr(r,n).redactMap(e)}function Os(e){return ZL.has(e)}function bo(e){if(!e)return!1;return Os(e.state)}var Db=8,Ir=12,Wr=10,Bc=8,ln=8,Ls=9,Ds=7,Is=8,Uc=12,Tp=40,$t=2,qL=2,Ib=2,VL=2,jc=34,zL=48,YL=60,JL=72,XL=0.48,ZL;var Lt=I(()=>{At();si();ZL=new Set(["RUNNING","STARTING","RESTARTING","HEALTHY","UNHEALTHY"])});function el(e){return e===xa?"\u2014":e}function Mp(e,t,r){let n=r?Hb:eD+Hb+Math.max(0,t)*2;return Math.max(1,e-n)}function oD(e){if(e.startsWith('"')||e.startsWith("'"))return"string";if(/^\d+$/.test(e))return"number";return"keyword"}function tl(e){return e.replace(iD,"")}function Wb(e){try{return JSON.stringify(JSON.parse(e),null,2)}catch{return e}}function rl(e,t){let r=Math.max(1,t),n=tl(e).replace(/\r\n/g,`
|
|
2211
2211
|
`).replace(/\r/g,`
|
|
2212
2212
|
`).split(`
|
|
2213
|
-
`),o=[];for(let i of n)if(i.length<=r)o.push(i);else o.push(...YL(i,r));return o.length>0?o:[""]}function Nb(e,t,r){let n=e.length>0?[...e]:[""];if(r||n.length<=1)return{visible:n,mark:"",folded:!1,hidden:0};let o=n.length-1,i=` ${GL}${o}`,s=Math.max(1,t-i.length);return{visible:[Re(n[0]??"",s)],mark:i,folded:!0,hidden:o}}function Hb(e,t){return e==="all"||e==="focus"&&t}function Qc(e){if(e==="clip")return"focus";if(e==="focus")return"all";return"clip"}function Tp(e,t,r,n=Ar){if(e.length===0)return{start:0,items:[],newer:0};if(!t){let a=Math.max(0,e.length-n);return{start:a,items:e.slice(a),newer:0}}let o=e.length-1,i=Math.max(0,Math.min(r,o)),s=e.slice(i,i+n);return{start:i,items:s,newer:Math.max(0,e.length-i-s.length)}}function Zc(e,t=Ar){return Math.max(0,e-t)}function Ap(e,t,r,n){let o=Math.max(t-1,0);if(e<0&&r>0)return{selected:0,startDelta:Math.max(-r,e)};if(e>o&&n>0)return{selected:o,startDelta:Math.min(n,e-o)};return{selected:Math.min(o,Math.max(0,e)),startDelta:0}}function el(e){if(e==="all")return"wrap all";if(e==="focus")return"wrap selected";return"clip"}function YL(e,t){let r=[],n=e;while(n.length>t){let o=n.slice(0,t),i=JL(o);if(i>0)r.push(n.slice(0,i).trimEnd()),n=n.slice(i).trimStart();else r.push(n.slice(0,t)),n=n.slice(t)}if(n.length>0)r.push(n);return r}function JL(e){let t=e.lastIndexOf(" "),r=e.lastIndexOf("\t"),n=e.lastIndexOf(","),o=Math.max(t,r,n);if(o<Math.floor(e.length*KL))return-1;return e[o]===","?o+1:o}function XL(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function QL(e,t=!1){let r=e.trim();if(r==="")return;if(t){let n=Mi(r);if(n)return new RegExp(n.source,n.flags.includes("g")?n.flags:`${n.flags}g`)}return new RegExp(XL(r),"gi")}function ZL(e,t){let r=e.map((a)=>a.text).join(""),n=[];t.lastIndex=0;let o=t.exec(r);while(o){let a=o[0]??"";if(a.length>0)n.push({start:o.index,end:o.index+a.length});if(a.length===0)t.lastIndex+=1;o=t.exec(r)}if(n.length===0)return e;let i=[],s=0;for(let a of e){let c=s,l=s+a.text.length,d=0;for(let u of n){let f=Math.max(u.start,c),p=Math.min(u.end,l);if(p<=f)continue;let m=f-c,h=p-c;if(m>d)i.push({text:a.text.slice(d,m),kind:a.kind});i.push({text:a.text.slice(m,h),kind:"search"}),d=h}if(d<a.text.length)i.push({text:a.text.slice(d),kind:a.kind});s=l}return i.filter((a)=>a.text!=="")}function jb(e,t="",r=!1){let n=Jc(e),o=[],i=new RegExp(qL.source,"gi"),s=0,a=i.exec(n);while(a){let d=a[0]??"";if(a.index>s)o.push({text:n.slice(s,a.index),kind:"text"});o.push({text:d,kind:VL(d)}),s=a.index+d.length,a=i.exec(n)}if(s<n.length)o.push({text:n.slice(s),kind:"text"});let c=o.length===0?[{text:n,kind:"text"}]:o,l=QL(t,r);return l?ZL(c,l):c}function Bb(e){let t=e.showTimestamps?Vc:0,r=e.showMeta?qc:0;return Math.max(Mb,e.width-t-e.serviceWidth-zc-r-Ob-As*$b)}function Ub(e,t){let r=t.reduce((s,a)=>Math.max(s,a.length),0),n=Vc+zc+Ob+Mb+As*$b,o=Math.max(Db,e-n),i=Math.max(Db,r);return Math.min(kp,o,i)}function ai(e){return eD.has(e)}function Gb(e,t,r,n){let o=r===""?t:t.filter((a)=>a.timestamp>=r);if(o.length===0)return e;let i=Math.max(1,n);if(o.length>=i)return o.slice(-i);let s=Math.max(0,e.length+o.length-i);return e.slice(s).concat(o)}function Kb(e,t){let r=t.length>0?t[t.length-1].seq:-1,n=e.filter((o)=>o.seq>r);return[...t,...n]}function qb(e,t){if(t.length===0)return e;let r=new Set(e.map((o)=>o.seq)),n=t.filter((o)=>!r.has(o.seq));return n.length===0?e:[...n,...e]}function Lp(e,t,r){return e&&t<=0&&r}function Vb(e){return e.filter((t)=>t.level==="ERROR"||t.level==="FATAL").length}function yo(e,t){let r=t.services?.filter((l)=>l!=="")??[],n=t.service??"",o=(t.search??"").trim(),i=t.source??"",s=t.since??"",a=t.until??"",c;if(o!=="")if(t.regex===!0){let l=Mi(o);if(l)c=(d)=>l.test(d);else c=(d)=>d.toLowerCase().includes(o.toLowerCase())}else{let l=o.toLowerCase();c=(d)=>d.toLowerCase().includes(l)}return e.filter((l)=>{if(r.length>0&&!r.includes(l.service))return!1;if(n!==""&&l.service!==n)return!1;if(i!==""&&l.source!==i)return!1;if(t.systemLogs===!1&&ai(l.source))return!1;if(s!==""&&l.timestamp<s)return!1;if(a!==""&&l.timestamp>a)return!1;if(t.errorOnly===!0&&l.level!=="ERROR"&&l.level!=="FATAL")return!1;if(!c)return!0;return c(l.message)||c(l.service)})}function tD(e,t){let r={};for(let i of e)r[i.service]=(r[i.service]??0)+1;let n=t.map((i)=>({name:i,count:r[i]??0})),o=Object.keys(r).filter((i)=>!t.includes(i)).sort().map((i)=>({name:i,count:r[i]??0}));return[...n,...o]}function zb(e,t,r=[]){let n=tl(e,t,r);return[{name:"",count:t.length},...tD(t,n)]}function rD(e,t){let r=e.map((o)=>({name:o,count:t[o]??0})),n=Object.keys(t).filter((o)=>!e.includes(o)).sort().map((o)=>({name:o,count:t[o]??0}));return[...r,...n]}function Yb(e,t,r=[]){let n=Object.keys(t.byService).map((i)=>({service:i})),o=tl(e,n,r);return[{name:"",count:t.total},...rD(o,t.byService)]}function Jb(e,t){if(e<=0)return"none started";return`${e}/${t} running`}function tl(e,t,r=[]){let n=[...e],o=(i)=>{if(i!==""&&!n.includes(i))n.push(i)};for(let i of r)o(i);for(let i of t)o(i.service);return n}function Dp(e,t,r){let n=["",...e],o=n.indexOf(t),s=((o<0?0:o)+r+n.length)%n.length;return n[s]??""}var qc=8,Vc=9,zc=7,Db=10,Mb=16,Ob=2,As=1,Ar=200,WL=2,Ib=1,GL="\u25B8",$b=2,KL=0.4,qL,zL,eD,Wb;var gr=I(()=>{ga();Ot();Lt();qL=/("[^"]*"|'[^']*'|\b(?:ERROR|FATAL|WARN(?:ING)?|FAIL(?:ED)?)\b|\b\d{3,5}\b)/gi;zL=/\x1b\[[0-9;?]*[ -/]*[@-~]|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|\x1b[@-Z\\-_]/g;eD=new Set(["auth","devctl","proxy"]),Wb=["devctl","mcp","auth"]});function ci(e){return e.length+oD}function Xb(e,t){if(t<sD)return Re(e,3);if(t<iD)return Re(e,4);return e}function Qb(e,t,r){let n=e.length;if(n===0||r<=0)return{start:0,end:-1};let o=t>=0&&t<n?t:0,i=Ip(e,o,r);if(i.start===0&&i.end===n-1)return i;let s=o>0?rl:0,a=o<n-1?rl:0,c=s+a,l=Ip(e,o,Math.max(e[o]??0,r-c)),d=(s>0&&l.start===0?rl:0)+(a>0&&l.end===n-1?rl:0);if(d===0)return l;return Ip(e,o,Math.max(e[o]??0,r-c+d))}function Ip(e,t,r){let n=e.length,o=t,i=t,s=e[t]??0;if(s>=r)return{start:t,end:t};while(o>0||i<n-1){let a=o>0?e[o-1]??0:Number.POSITIVE_INFINITY,c=i<n-1?e[i+1]??0:Number.POSITIVE_INFINITY,l=o>0&&s+a<=r,d=i<n-1&&s+c<=r;if(!l&&!d)break;let u=t-(o-1),f=i+1-t;if(l&&(!d||u<=f))o-=1,s+=a;else if(d)i+=1,s+=c;else break}return{start:o,end:i}}function Zb(e){if(e==="detail")return bo.findIndex((t)=>t.id==="services");return bo.findIndex((t)=>t.id===e)}function ex(e,t){if(e==="doctor")return t.doctor;if(e==="settings")return t.settings;if(e==="profiles")return t.profiles;if(e==="dashboard"||e==="services")return t.services;if(e==="logs")return t.logs??0;if(e==="mcp")return t.mcp??0;if(e==="setup")return nD;if(e==="config")return t.config??0;return 0}function tx(e){if(e.length===1&&e>="1"&&e<="9")return bo[Number(e)-1]?.id;return}var nD=9,oD=2,rl=2,iD=80,sD=56,bo,Dn;var In=I(()=>{Ot();bo=[{id:"dashboard",label:"dashboard"},{id:"services",label:"services"},{id:"logs",label:"logs"},{id:"proxy",label:"proxy"}];Dn=bo.map((e)=>e.id)});function ox(e,t=!0){let r=e<nl?rx+1:rx,n=t?pD:0;return r+n+dD+n+uD+n}function di(e){return Math.max(cD,e-lD*2)}function ix(e){let t=e.width<aD,r=e.proxyOn?`\u25CF ${Re(e.proxyAddress,t?10:18)}`:"";return[{label:t?`${e.running}/${e.total}`:Jb(e.running,e.total),tone:e.running>0?"success":"idle"},{label:r,tone:"info",hide:!e.proxyOn},{label:"MCP",tone:"info",hide:!e.mcpOn},{label:e.adc?t?"ADC":"ADC ok":t?"!ADC":"ADC missing",tone:e.adc?"success":"error"},{label:t?"sec":"secrets shown",tone:"warning",hide:!e.reveal}]}function nx(e){return e.key==="/"&&e.label==="command"}function fD(e){return e.key.length+e.label.length+3}function sx(e,t){let r=e.filter(nx),n=e.filter((s)=>!nx(s)),o=[],i=0;for(let s of[...r,...n]){let a=fD(s);if(i+a>t)break;o.push(s),i+=a}return o}function ol(e,t,r,n,o="center",i=!0){let s=Math.min(r,Math.max(1,e-4)),a=Math.min(ox(e,i),Math.max(0,t-1)),c=Math.min(n,Math.max(1,t-a)),l=Math.max(0,Math.min(Math.floor((e-s)/2),Math.max(0,e-s))),d=o==="bottom"?t-2-c:Math.floor((t-c)/2),u=Math.max(0,Math.min(d,t-c));return{left:l,top:u,width:s,height:c}}function ax(e){if(e==="")return"idle";if(hD.test(e))return"error";if(e.includes("session only"))return"warning";if(mD.test(e))return"success";return"info"}function cx(e,t,r){if(e==="restart-cascade")return{title:"Restart dependents?",body:`${r?.services?.join(", ")||"selected services"} has dependents. Enter restarts only the named services. Press c to cascade (same as /restart --cascade).`};if(e==="quit")return{title:"Quit",body:"Stop managed services and leave the TUI? Press d to detach (daemon stays). Press k to stop the daemon and leave services running (devctl down --keep-services)."};if(e==="reload")return{title:"Reload applied",body:t===""?"Configuration changed. Restart marked services?":`Restart required: ${t}`};if(e==="reset-prefs")return{title:"Reset preferences",body:"Restore theme, display size, mouse, and leader timeout to defaults? This overwrites your saved tui.json values."};if(e==="free-port"){let n=r?.port??0,o=r?.process||"process",i=r?.pid??0;return{title:`Free port ${n}`,body:`Stop ${o} (pid ${i}) so port ${n} can be used? This sends SIGTERM, then SIGKILL if it stays up.`}}return{title:"Start profile",body:t===""?"Start the configured services?":`Start profile ${t}?`}}function lx(e){if(e==="quit")return[{key:"enter",label:"stop services"},{key:"d",label:"detach"},{key:"k",label:"down, keep services"},{key:"esc",label:"stay"}];if(e==="restart-cascade")return[{key:"enter",label:"named only"},{key:"c",label:"cascade"},{key:"esc",label:"stay"}];return[{key:"enter",label:"confirm"},{key:"esc",label:"stay"}]}function dx(e){let t=e.payload?.error;return typeof t==="string"&&t!==""?t:"configuration reload failed"}var li=100,nl=90,aD=60,cD=8,lD=6,rx=1,dD=1,uD=1,pD=1,oW,mD,hD;var vr=I(()=>{Ot();gr();In();oW=ox(nl-1);mD=/^(started|stopped|restarted|refreshed|cleared|copied|exported|jumped|proxy started|proxy stopped|secrets|re-running|theme |mouse |leader |display |restored)/i,hD=/\b(fail|error|unknown|not found|no configuration|blocked|already in use|is using port)\b/i});function il(){return[{name:"start",aliases:["up"],desc:"Start selected services or the current profile",leader:"n",group:"services",usage:"<service>"},{name:"stop",aliases:[],desc:"Stop selected services",leader:"x",group:"services",usage:"<service>"},{name:"restart",aliases:[],desc:"Restart selected services",leader:"R",group:"services",usage:"<service>",suggest:[{token:"--cascade",desc:"Restart selected services and their dependents"}]},{name:"run",aliases:["task"],desc:"Run a one-off task; empty /run opens a picker",leader:"",group:"services",usage:"<task>"},{name:"exec",aliases:[],desc:"Run a command in a service context; empty /exec opens a picker",leader:"",group:"services",usage:"<service>"},{name:"services",aliases:["s"],desc:"Open the services screen",leader:"s",group:"nav"},{name:"logs",aliases:["l"],desc:"Open the log viewer",leader:"l",group:"nav"},{name:"auth",aliases:["identity","a"],desc:"Open identity",leader:"a",group:"nav",suggest:[{token:"login",desc:"Run gcloud ADC login"},{token:"logout",desc:"Revoke application-default credentials"},{token:"refresh",desc:"Probe identities"}]},{name:"credentials",aliases:["creds"],desc:"Open credential store status",leader:"",group:"nav"},{name:"reload",aliases:[],desc:"Reload configuration",leader:"",group:"ui"},{name:"proxy",aliases:["p"],desc:"Open the proxy screen",leader:"p",group:"nav"},{name:"mcp",aliases:["agent"],desc:"Open the MCP server screen for coding agents",leader:"",group:"nav"},{name:"doctor",aliases:["d"],desc:"Run environment diagnostics",leader:"d",group:"nav"},{name:"stats",aliases:["metrics"],desc:"View system and service statistics",leader:"m",group:"nav"},{name:"config",aliases:["c"],desc:"View merged configuration",leader:"c",group:"nav"},{name:"import",aliases:[],desc:"Preview or write a Compose mapping",leader:"",group:"ui",suggest:[{token:"compose",desc:"Preview a Compose mapping; add --write to save"}]},{name:"diff",aliases:["provenance"],desc:"Show winning config sources and what they shadowed",leader:"",group:"ui"},{name:"daemon",aliases:["bootstrap"],desc:"Show supervisor bootstrap logs (same file as devctl daemon logs)",leader:"",group:"app"},{name:"update",aliases:[],desc:"Install a newer GitHub Release when the install method is known",leader:"",group:"app"},{name:"profiles",aliases:["o"],desc:"Select a development profile",leader:"o",group:"nav"},{name:"setup",aliases:["init"],desc:"Open setup guidance",leader:"i",group:"nav"},{name:"dashboard",aliases:["home"],desc:"Return to the dashboard",leader:"h",group:"nav"},{name:"themes",aliases:["theme"],desc:"List available themes",leader:"t",group:"ui"},{name:"settings",aliases:["prefs","preferences"],desc:"Open TUI settings (theme, mouse, MCP page)",leader:"",group:"ui"},{name:"help",aliases:["?"],desc:"Show the help dialog",leader:"",group:"ui"},{name:"refresh",aliases:[],desc:"Refresh status and logs",leader:"r",group:"ui"},{name:"regex",aliases:[],desc:"Toggle regex log search",leader:"",group:"logs"},{name:"since",aliases:[],desc:"Filter logs after an ISO timestamp",leader:"",group:"logs",usage:"<timestamp>"},{name:"until",aliases:[],desc:"Filter logs before an ISO timestamp",leader:"",group:"logs",usage:"<timestamp>"},{name:"history",aliases:[],desc:"Load a persisted log session",leader:"",group:"logs",usage:"<session>"},{name:"edit",aliases:[],desc:"Open configuration in $EDITOR",leader:"",group:"ui"},{name:"buffer",aliases:[],desc:"Edit configuration in a validate/save buffer",leader:"",group:"ui"},{name:"pause",aliases:[],desc:"Pause or resume live logs",leader:"",group:"logs"},{name:"fullscreen",aliases:["zen","expand"],desc:"Expand logs to fill the terminal",leader:"z",group:"logs"},{name:"split",aliases:[],desc:"Split the logs screen into two service panes",leader:"",group:"logs"},{name:"trace",aliases:[],desc:"Search logs for a request or trace id",leader:"",group:"logs",usage:"<id>"},{name:"filter",aliases:[],desc:"Toggle ERROR+ log filter",leader:"",group:"logs"},{name:"system",aliases:["internal"],desc:"Show or hide internal auth/mcp/devctl/proxy logs",leader:"",group:"logs"},{name:"reveal",aliases:[],desc:"Reveal or hide secret environment values",leader:"",group:"ui"},{name:"wrap",aliases:[],desc:"Cycle log wrap: selected, all lines, or clip",leader:"",group:"logs"},{name:"copy",aliases:[],desc:"Copy the highlighted selection to the clipboard",leader:"",group:"ui"},{name:"export",aliases:[],desc:"Write filtered logs to ~/.devctl/exports",leader:"e",group:"logs"},{name:"exports",aliases:["open-exports"],desc:"Open the log export folder",leader:"",group:"logs"},{name:"clear",aliases:["new"],desc:"Clear the on-screen log buffer",leader:"",group:"logs"},{name:"version",aliases:["v"],desc:"Show the current devctl version",leader:"",group:"app"},{name:"down",aliases:[],desc:"Stop the supervisor",leader:"",group:"app",suggest:[{token:"--keep-services",desc:"Stop the supervisor and leave processes running"}]},{name:"exit",aliases:["quit","q"],desc:"Exit (detach or stop services)",leader:"q",group:"app"}]}function xo(e){let t=e.trim().toLowerCase().replace(/^\//,""),r=t.indexOf(" ");return r===-1?t:t.slice(0,r)}function hx(e){let t=xo(e);if(t==="")return il();return il().map((r)=>({command:r,score:AD(r,t)})).filter((r)=>r.score>0).sort((r,n)=>n.score-r.score||r.command.name.localeCompare(n.command.name)).map((r)=>r.command)}function AD(e,t){if(t==="")return 1;let r=e.name.toLowerCase(),n=e.aliases.map((o)=>o.toLowerCase());return DD(r,n,t)||ID(r,n,t)||MD(r,n,t)||OD(r,n,t)||LD(e.suggest,t)||$D(e.desc.toLowerCase(),t)}function LD(e,t){if(!e||e.length===0)return 0;let r=e.map((n)=>n.token.toLowerCase());if(r.some((n)=>n===t||n.replace(/^-+/,"")===t))return px;if(t.length<mx)return 0;return r.some((n)=>n.includes(t)||n.replace(/^-+/,"").startsWith(t))?fx:0}function DD(e,t,r){if(e===r)return gD;return t.includes(r)?vD:0}function ID(e,t,r){if(e.startsWith(r))return yD;if(t.some((i)=>i.startsWith(r)))return bD;let n=e.split("-");return n.length>1&&n.some((i)=>i.startsWith(r))?xD:0}function MD(e,t,r){if(r.length<mx)return 0;if(e.includes(r))return px;return t.some((n)=>n.includes(r))?fx:0}function OD(e,t,r){if(r.length<PD)return 0;let n=ux(e,r);if(n>0)return wD+Math.min(n,TD);return t.some((o)=>ux(o,r)>0)?SD:0}function ux(e,t){let r=0,n=0,o=0;for(let i=0;i<t.length;i+=1){let s=t[i];if(s===void 0)return 0;let a=e.indexOf(s,r);if(a<0)return 0;if(r>0&&a===r)n+=1,o+=2+n;else n=0,o+=a===0?4:1;r=a+1}return o}function $D(e,t){if(t.length<_D)return 0;if(e.split(/[^a-z0-9+]+/).filter((n)=>n.length>=RD).some((n)=>n.startsWith(t)))return kD;return t.length>=ED&&e.includes(t)?CD:0}function Wr(e){let t=e.trim().toLowerCase().replace(/^\//,"").split(" ")[0]??"";return il().find((r)=>r.name===t||r.aliases.includes(t))}function Mp(e){return e.trim().replace(/^\//,"").split(/\s+/).slice(1).filter((r)=>r!=="")}function gx(e){return il().find((r)=>r.leader===e)?.name??""}function vx(e){let t=e.includes("--cascade")||e.includes("-c");return{services:e.filter((n)=>n!=="--cascade"&&n!=="-c"),cascade:t}}function yx(e){let t=e.includes("--print-env"),r=e.includes("--reveal"),n=e.filter((o)=>o!=="--print-env"&&o!=="--reveal"&&o!=="--");return{service:n[0]??"",printEnv:t,reveal:r,command:n.slice(1)}}var gD=100,vD=90,yD=80,bD=70,xD=60,px=50,fx=45,wD=30,SD=28,kD=20,CD=10,mx=2,PD=3,_D=4,RD=4,ED=4,TD=9;function Sx(e){return e==="detail"?"services":e}function kx(e){let t=Dn.indexOf(Sx(e));if(t<0)return"dashboard";return Dn[(t+1)%Dn.length]??"dashboard"}function Cx(e){let t=Dn.indexOf(Sx(e));if(t<0)return"dashboard";return Dn[(t-1+Dn.length)%Dn.length]??"dashboard"}function $p(e){return["services","nav","logs","ui","app","tasks"].flatMap((r)=>{let n=e.filter((o)=>o.group===r);if(n.length===0)return[];return[{group:r,items:n}]})}function sl(e){return e.hint?`${e.name} ${e.hint}`:e.name}function Fp(e){return e.name}function Px(e){if(e.hint)return`${e.hint} \xB7 ${e.desc}`;let t=[e.usage,...(e.suggest??[]).map((r)=>r.token)].filter((r)=>Boolean(r));if(t.length===0)return e.desc;return`${e.desc} \xB7 ${t.join(" \xB7 ")}`}function _x(e){let t=e.reduce((n,o)=>Math.max(n,Fp(o).length),0),r=Math.min(Op,Math.max(t,1));return FD+r+ND}function Rx(e){return e.hint?`${e.name} ${e.hint} `:`${e.name} `}function Ex(e,t){if(e.hint)return e.hint.split(/\s+/);return Wr(t)?.name===e.name?Mp(t):[]}function bx(e,t){if(t==="")return!0;let r=t.toLowerCase(),n=e.toLowerCase();return n.startsWith(r)||n.includes(r)}function HD(e,t){if(e.name==="start"||e.name==="stop"||e.name==="restart")return(t.services??[]).map((r)=>({token:r,desc:`${e.name} ${r}`}));if(e.name==="run")return(t.tasks??[]).map((r)=>({token:r,desc:`run ${r}`}));if(e.name==="exec")return(t.services??[]).map((r)=>({token:r,desc:`exec in ${r}`}));return[]}function xx(e,t){return{...e,hint:t.token,desc:t.desc,usage:void 0,suggest:void 0}}function jD(e,t,r={}){let n=Mp(t).join(" "),o=t.trim().includes(" ")||t.endsWith(" "),i=xo(t),s=[];for(let a of e){if(s.push({...a,hint:void 0}),i==="")continue;let c=a.name===i||a.aliases.includes(i),l=c?n:i;for(let d of a.suggest??[])if(bx(d.token,l))s.push(xx(a,d));if(!c||!o)continue;for(let d of HD(a,r))if(bx(d.token,n))s.push(xx(a,d))}return s}function Tx(e,t={}){let r=hx(e),n=xo(e)===""?$p(r).flatMap((o)=>o.items):r;return jD(n,e,t)}function Np(e,t,r,n){let o=xo(t),i=[...e].sort().map((s)=>({name:s,aliases:[],desc:n,leader:"",group:r}));if(o==="")return i;return i.filter((s)=>s.name.toLowerCase().includes(o))}function Ax(e,t,r=Zr()){if(t==="slash")return[{key:"\u2191\u2193",label:"suggest"},{key:"tab",label:"complete"},{key:"enter",label:"run"},{key:"esc",label:"cancel"}];if(t==="themes")return[Gr,{key:"\u2191\u2193",label:"move"},{key:"enter",label:"save"},{key:"esc",label:"revert"}];if(t==="help")return[Gr,{key:"j/k",label:"scroll"},{key:"esc",label:"close"}];if(t==="log-details"||t==="scroll-text")return[Gr,{key:"j/k",label:"scroll"},{key:Ei(r),label:"copy"},{key:"esc",label:"close"}];if(t==="confirm")return[{key:"enter",label:"confirm"},{key:"esc",label:"stay"}];if(t==="plan")return[Gr,{key:"esc",label:"back to dashboard"},{key:"enter",label:"done"}];if(t==="leader")return[Gr,...Hp()];if(t==="config-edit")return[{key:It("s"),label:"save"},{key:"esc",label:"discard"}];return BD(e,r)}function Hp(){return[{key:"n",label:"start"},{key:"x",label:"stop"},{key:"R",label:"restart"},{key:"s",label:"services"},{key:"l",label:"logs"},{key:"t",label:"themes"},{key:"q",label:"quit"}]}function BD(e,t){let r=[{key:Ei(t),label:"copy"},Gr,{key:"?",label:"help"}];switch(e){case"dashboard":return[{key:"space",label:"select"},{key:"*",label:"all"},{key:"-",label:"none"},{key:"enter",label:"start or open"},{key:"n",label:"start"},{key:"x",label:"stop"},{key:"r",label:"refresh"},{key:"R",label:"restart"},{key:"\u2190\u2192",label:"log filter"},{key:"g",label:"latest"},{key:"z",label:"full logs"},{key:"i",label:"internal logs"},{key:It("l"),label:"clear logs"},{key:"j/k",label:"move"},...r];case"services":return[{key:"enter",label:"detail"},{key:"space",label:"select"},{key:"*",label:"all"},{key:"-",label:"none"},{key:"n",label:"start"},{key:"x",label:"stop"},{key:"r",label:"refresh"},{key:"R",label:"restart"},...r];case"detail":return[{key:"j/k",label:"scroll env"},{key:"n",label:"start"},{key:"x",label:"stop"},{key:"o",label:"config"},{key:"l",label:"logs"},{key:"esc",label:"back"},...r];case"logs":return[{key:"\u2190\u2192",label:"filter"},{key:"e",label:"errors"},{key:"i",label:"internal logs"},{key:It("l"),label:"clear logs"},{key:"g",label:"latest"},{key:"f",label:"search"},{key:"t",label:"time"},{key:"m",label:"meta"},{key:"w",label:"wrap"},{key:"j/k",label:"move"},{key:"p",label:"pause"},{key:"z",label:"full screen"},{key:"\\",label:"split"},{key:"/exports",label:"open folder"},...r];case"profiles":return[{key:"space",label:"set current"},{key:"enter",label:"set and start"},{key:"j/k",label:"move"},...r];case"proxy":return[{key:"n",label:"start proxy"},{key:"x",label:"stop proxy"},...r];case"mcp":return[{key:"j/k",label:"move"},{key:"space",label:"start or copy"},{key:"\u2190\u2192",label:"change port"},{key:"enter",label:"start or copy"},...r];case"config":return[{key:"enter",label:"run task"},{key:"v",label:"buffer"},{key:"e",label:"editor"},{key:"/diff",label:"sources"},{key:"/reload",label:"reload"},{key:"j/k",label:"scroll or select task"},...r];case"setup":return[{key:"j/k",label:"steps"},{key:"enter",label:"continue"},{key:"esc",label:"back"},{key:"esc\xD72",label:"quit"},...r];case"doctor":return[{key:"r",label:"run doctor again"},{key:"j/k",label:"move"},{key:"enter",label:"fix port"},...r];case"auth":return[{key:"r",label:"probe identities"},{key:"/auth login",label:"ADC login"},{key:"/auth logout",label:"revoke ADC"},{key:"/auth refresh",label:"probe"},...r];case"settings":return[{key:"j/k",label:"move"},{key:"\u2190\u2192",label:"save"},{key:"enter",label:"apply or open page"},{key:"space",label:"toggle"},...r];default:return[{key:"tab",label:"screens"},...r]}}function Lx(e,t,r){if(r<=t||e<t)return 0;return Math.min(e-t+1,r-t)}function wx(e,t,r){let n=e.slice(t,r+1),o=new Set(n.map((i)=>i.group));return n.length+o.size}function Dx(e,t,r){if(e.length===0)return[];let n=Math.min(Math.max(t,0),e.length-1),o=n,i=n;for(;;){let s=i+1<e.length&&wx(e,o,i+1)<=r,a=o>0&&wx(e,o-1,i)<=r;if(s)i+=1;else if(a)o-=1;else break}return e.slice(o,i+1)}function ui(e,t){if(e.length===0)return;let r=Math.min(Math.max(t,0),e.length-1);return e[r]}var Gr,FD=3,ND=2,Op=22;var wo=I(()=>{Rr();In();Gr={key:"/",label:"command"}});function cl(e,t){let r=t??Object.keys(e?.services??{});return{running:r.filter((o)=>vo(e?.services[o])).length,total:r.length}}function Ix(e,t){let r=0,n=0,o=0,i=0,s=0,a=0;for(let c of e){let l=t?.services[c];if(l?.health==="HEALTHY")o+=1;let d=l?.state??"STOPPED";if(d==="FAILED")i+=1;else if(d==="STOPPING")s+=1;else if(d==="STARTING"||d==="RESTARTING")n+=1;else if(Ts(d))r+=1;else a+=1}return{total:e.length,live:r+n,running:r,starting:n,healthy:o,failed:i,stopping:s,stopped:a}}function Mx(e,t){if(!Number.isFinite(e)||!Number.isFinite(t)||t<=0)return 0;return e/t}function VD(e,t,r){if(!Number.isFinite(e))return"success";if(e>r)return"error";if(e>t)return"warning";return"success"}function zD(e,t){return VD(Mx(e,t),UD,WD)}function Wp(e,t=24){if(e.length===0||t<=0)return"";let r=e.length>t?e.slice(e.length-t):e,n=Math.max(...r,0);if(n<=0)return Ls[0]?.repeat(r.length)??"";return r.map((o)=>{let i=Number.isFinite(o)?Math.min(1,Math.max(0,o/n)):0,s=Math.min(Ls.length-1,Math.round(i*(Ls.length-1)));return Ls[s]??Ls[0]}).join("")}function YD(e,t){let r=t>0?e/t:1;if(r<KD)return"error";if(r<GD)return"warning";return"success"}function jp(e){if(!Number.isFinite(e)||e<0)return"\u2014";return e.toFixed(2)}function JD(e){if(!Number.isFinite(e)||e<0)return"\u2014";return`${Math.round(e*100)}%`}function Ox(e,t=Date.now()){if(!e?.startTime)return"\u2014";let r=new Date(e.startTime).getTime();if(!Number.isFinite(r))return"\u2014";return ks(t-r)}function $x(e,t=qD){return Object.entries(e).sort((r,n)=>n[1]-r[1]||r[0].localeCompare(n[0])).slice(0,t)}function Fx(e,t=1){return Math.max(24,e-nI-t*2-oI-iI-sI)}function Nx(e){let t=e>=XD,r=e>=QD,n=e>=ZD,o=e>=eI,i=e>=tI,s=e>=rI,a=Tr+$t;if(t)a+=Ur+$t;if(r)a+=_s+$t;if(n)a+=Rs+$t;if(o)a+=Ps+$t;if(i)a+=ll+$t;if(s)a+=an;return{name:Math.max(Fc,e-a),health:t,cpu:r,mem:n,up:o,rst:i,pid:s}}function Hx(e){let t=Number.isFinite(e)&&e>0?e:0;return{ratio:Math.min(1,t),label:JD(t)}}function jx(e,t=lI){return`[${si(e.ratio,t)}] ${e.label.padStart(4)}`}function Gp(e,t){let r=e.reduce((l,d)=>Math.max(l,d.what.length),4),n=e.reduce((l,d)=>Math.max(l,d.reading.length),7),o=e.some((l)=>l.meter)?dI:0,i=pi*(o>0?3:2),s=Math.min(aI,Math.max(Bp,r)),a=Math.min(cI,Math.max(Up,n)),c=t-s-a-o-i;if(c<al&&a>Up)a=Math.max(Up,a-(al-c)),c=t-s-a-o-i;if(c<al&&s>Bp)s=Math.max(Bp,s-(al-c)),c=t-s-a-o-i;return{what:s,reading:a,meaning:Math.max(0,c),meter:o}}function Bx(e){return uI.has((e?.health.type??"").toLowerCase())}function Ux(e){if(e==="darwin")return"macOS";if(e==="win32")return"Windows";if(e==="linux")return"Linux";return e||"this computer"}function Wx(e,t){let r=Math.max(1,t),n=zD(e,r),o=n==="error"?"overloaded":n==="warning"?"busy":"not busy",i=n==="error"?`${r} cores are queued up \u2014 last minute ${jp(e)}`:n==="warning"?`${r} cores nearly full \u2014 last minute ${jp(e)}`:`${r} cores. Last minute ${jp(e)} (under ${r} is fine)`;return{what:"CPU work",reading:o,meaning:i,tone:n,meter:Hx(Mx(e,r))}}function Gx(e,t){let r=YD(e,t),n=`${Cs(e)} of ${Cs(t)}`,o=r==="error"?"Almost no RAM left for new work":r==="warning"?"RAM is getting tight":"RAM the computer can still give out",i=t>0?Math.max(0,(t-e)/t):0;return{what:"RAM leftover",reading:n,meaning:o,tone:r,meter:Hx(i)}}function Kx(e){let t=e?.state??"STOPPED";if(t==="FAILED")return"crashed";if(t==="STOPPING")return"stopping";if(t==="STARTING"||t==="RESTARTING")return"starting";if(Ts(t))return"up";return"off"}function qx(e){if(!e||!Ts(e.state))return"\u2014";if(e.health==="HEALTHY")return"ready";if(e.health==="UNHEALTHY")return"failing";if(e.state==="STARTING"||e.state==="RESTARTING")return"waiting";return"checking"}function Vx(e){if(e==="keychain")return"password store";if(e==="file")return"local file";return e||"unknown store"}function zx(e,t){let r=[{what:"Started",reading:String(e.live),meaning:e.live===0?"nothing is running yet":"process is up",tone:e.live>0?"success":"muted"}];if(t)r.push({what:"Ready",reading:String(e.healthy),meaning:e.live===0?"start services first":"passed their health check",tone:e.live>0&&e.healthy<e.live?"warning":e.healthy>0?"success":"muted"});if(e.starting>0)r.push({what:"Still starting",reading:String(e.starting),meaning:"not ready for traffic yet",tone:"warning"});if(e.stopping>0)r.push({what:"Stopping",reading:String(e.stopping),meaning:"shutting down",tone:"warning"});return r.push({what:"Crashed",reading:String(e.failed),meaning:e.failed>0?"open the service for the error":"none",tone:e.failed>0?"error":"muted"}),r.push({what:"Not started",reading:String(e.stopped),meaning:e.stopped>0?"off until you start them":"all are up",tone:"muted"}),r}var UD=0.85,WD=1.5,GD=0.2,KD=0.1,qD=5,Ls="\u2581\u2582\u2583\u2584\u2585\u2586\u2587\u2588",XD=52,QD=64,ZD=72,eI=82,tI=90,rI=100,nI=2,oI=2,iI=2,sI=1,ll=8,Bp=8,aI=22,Up=6,cI=28,al=12,pi=2,lI=10,dI=18,uI;var dl=I(()=>{Ot();Lt();uI=new Set(["http","tcp","command"])});import{useEffect as pI,useRef as fI}from"react";import{jsxDEV as me,Fragment as hI}from"@opentui/react/jsx-dev-runtime";function mI(e,t){switch(t){case"primary":return{bg:e.primary,fg:mo(e.primary,e.inverse)};case"accent":return{bg:e.accent,fg:Lc(e)?Ac:mo(e.accent,e.inverse)};case"success":return{bg:e.success,fg:mo(e.success,e.inverse)};case"warning":return{bg:e.warning,fg:Lc(e)?Ac:mo(e.warning,e.inverse)};case"error":return{bg:e.error,fg:mo(e.error,e.inverse)};case"info":return{bg:e.info,fg:mo(e.info,e.inverse)};case"muted":return{bg:e.element,fg:e.text};case"ghost":return{bg:e.element,fg:e.muted};default:return{bg:e.element,fg:e.text}}}function Yx(e){let{palette:t,dir:r}=e;return me("box",{width:2,height:1,flexShrink:0,overflow:"hidden",children:me("text",{fg:t.muted,children:r==="left"?"\u2039":"\u203A"},void 0,!1,void 0,this)},void 0,!1,void 0,this)}function Ut(e){let{palette:t,children:r,backgroundColor:n,edge:o="bottom"}=e,i=ye(),s=e.ruled??!ho(i),a=n??t.element,c=me("box",{flexShrink:0,flexDirection:"column",backgroundColor:a,overflow:"hidden",children:r},void 0,!1,void 0,this);if(!s)return c;return me("box",{flexShrink:0,flexDirection:"column",overflow:"hidden",border:[...o==="both"?["top","bottom"]:[o]],borderStyle:"single",borderColor:t.border,children:c},void 0,!1,void 0,this)}function ul(e){let{palette:t,items:r,active:n,width:o,onPick:i,emphasis:s="ghost"}=e,a=r.map((u)=>ci(u.label)),c=Qb(a,n,Math.max(1,o)),l=c.start>0,d=c.end>=0&&c.end<r.length-1;return me("box",{height:1,flexDirection:"row",overflow:"hidden",backgroundColor:t.element,children:[l?me(Yx,{palette:t,dir:"left"},void 0,!1,void 0,this):null,r.map((u,f)=>{if(f<c.start||f>c.end)return null;let p=f===n,m=s==="fill"&&p;return me(Be,{palette:t,label:u.label,tone:m?"primary":"ghost",bg:m?void 0:p?t.highlight:t.element,fg:m?void 0:p?t.primary:u.color??t.muted,onMouseDown:()=>i(f)},u.id,!1,void 0,this)}),d?me(Yx,{palette:t,dir:"right"},void 0,!1,void 0,this):null]},void 0,!0,void 0,this)}function Be(e){let{palette:t,label:r,tone:n="idle",fg:o,bg:i,onMouseDown:s}=e,a=mI(t,n);return me("box",{height:1,paddingLeft:1,paddingRight:1,backgroundColor:i??a.bg,overflow:"hidden",flexShrink:0,onMouseDown:s,children:me("text",{fg:o??a.fg,children:r},void 0,!1,void 0,this)},void 0,!1,void 0,this)}function it(e){let{palette:t,hints:r}=e;return me("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,children:r.map((n)=>me("box",{height:1,paddingLeft:1,paddingRight:1,flexShrink:0,overflow:"hidden",children:me("text",{wrapMode:"none",children:[me("span",{fg:t.primary,children:n.key},void 0,!1,void 0,this),me("span",{fg:t.muted,children:` ${n.label}`},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},`${n.key}-${n.label}`,!1,void 0,this))},void 0,!1,void 0,this)}function ut(e){let{palette:t,items:r,hints:n,ruled:o=!0}=e,i=me("box",{height:1,flexDirection:"row",overflow:"hidden",backgroundColor:t.element,children:[r.map((s,a)=>me(Be,{palette:t,label:s.text,tone:s.tone,onMouseDown:s.onMouseDown},`${s.text}-${a}`,!1,void 0,this)),n&&n.length>0?me(hI,{children:[me("box",{flexGrow:1,backgroundColor:t.element},void 0,!1,void 0,this),me(it,{palette:t,hints:n},void 0,!1,void 0,this)]},void 0,!0,void 0,this):null]},void 0,!0,void 0,this);if(!o)return i;return me(Ut,{palette:t,backgroundColor:t.element,children:i},void 0,!1,void 0,this)}function pt(e){let{palette:t,title:r,termW:n,termH:o,preferW:i,preferH:s,anchor:a="center",borderColor:c,bottomTitle:l,gap:d,children:u}=e,f=ho(ye()),p=ol(n,o,i,s,a,!f);return me("box",{position:"absolute",left:p.left,top:p.top,width:p.width,height:p.height,border:!0,borderStyle:Kp,borderColor:c??t.borderActive,backgroundColor:t.panel,padding:f?0:1,title:r,titleColor:t.primary,bottomTitle:l,gap:f?0:d,flexDirection:"column",overflow:"hidden",children:u},void 0,!1,void 0,this)}function Ue(e){return{rootOptions:{flexGrow:1,height:"100%",overflow:"hidden",backgroundColor:e.panel},viewportOptions:{backgroundColor:e.panel},contentOptions:{backgroundColor:e.panel},scrollbarOptions:{trackOptions:{foregroundColor:e.primary,backgroundColor:e.element}}}}function rt(e,t){if(!e)return;e.scrollBy({x:0,y:t})}function fi(e,t){let r=fI(null);return pI(()=>{let n=r.current;if(!n||e<0)return;let o=requestAnimationFrame(()=>{n.scrollChildIntoView(`${t}-${e}`)});return()=>cancelAnimationFrame(o)},[t,e]),r}function kt(e){let t=ye(),r=Ss(t),n=e.scroll?me("scrollbox",{focused:!1,stickyScroll:!1,scrollX:!1,style:Ue(e.palette),children:me("box",{flexDirection:"column",overflow:"hidden",children:e.children},void 0,!1,void 0,this)},void 0,!1,void 0,this):e.children;return me("box",{flexGrow:1,border:!0,borderStyle:Kp,borderColor:e.borderColor??e.palette.border,title:e.title,titleColor:e.palette.primary,paddingLeft:t.pad,paddingRight:t.pad,paddingTop:r?0:t.pad,paddingBottom:r?0:t.pad,backgroundColor:e.palette.panel,flexDirection:"column",overflow:"hidden",children:n},void 0,!1,void 0,this)}function So(e){let{palette:t,label:r,value:n,tone:o="text"}=e,i=ye(),s=o==="success"?t.success:o==="warning"?t.warning:o==="error"?t.error:o==="muted"?t.muted:t.text;return me("box",{height:i.rowH,flexDirection:"row",overflow:"hidden",children:[me("box",{width:14,flexShrink:0,overflow:"hidden",children:me("text",{fg:t.muted,children:r},void 0,!1,void 0,this)},void 0,!1,void 0,this),me("box",{flexGrow:1,overflow:"hidden",children:me("text",{fg:s,wrapMode:"none",children:n},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function pl(e){let{palette:t,title:r,body:n,hint:o}=e;return me("box",{paddingLeft:1,paddingRight:1,flexDirection:"column",overflow:"hidden",border:!0,borderStyle:Kp,borderColor:t.borderActive,titleColor:t.primary,children:[me("text",{fg:t.primary,children:r},void 0,!1,void 0,this),me("text",{fg:t.text,wrapMode:"word",children:n},void 0,!1,void 0,this),o?me("text",{fg:t.muted,wrapMode:"word",children:o},void 0,!1,void 0,this):null]},void 0,!0,void 0,this)}var Kp="rounded";var ke=I(()=>{Mt();vr();In();Er();tt()});import{jsxDEV as $e,Fragment as vI}from"@opentui/react/jsx-dev-runtime";function Xx(e){let{palette:t,cfg:r,snap:n,google:o,profile:i,reveal:s,width:a}=e,c=cl(n,r?Object.keys(r.services):void 0),l=n?.proxy.running===!0,d=o?.adcAvailable===!0,u=a<nl,f=Re(r?.project.name||"local",u?18:22),p=Re(i||n?.profile||"no profile",16),m=ix({width:a,running:c.running,total:c.total,proxyOn:l,proxyAddress:n?.proxy.address??"",mcpOn:n?.mcp?.running===!0,adc:d,reveal:s}).filter((b)=>!b.hide&&b.label!=="").map((b)=>$e(Be,{palette:t,label:b.label,tone:b.tone},b.label,!1,void 0,this)),h=Ss(ye()),v=$e("box",{height:1,flexGrow:1,overflow:"hidden",backgroundColor:t.panel,paddingLeft:1,children:$e("text",{wrapMode:"none",children:[$e("span",{fg:t.primary,children:er()},void 0,!1,void 0,this),$e("span",{fg:t.muted,children:` ${f}`},void 0,!1,void 0,this),$e("span",{fg:t.accent,children:` ${p}`},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this);return $e(Ut,{palette:t,backgroundColor:t.panel,ruled:!h,children:$e("box",{height:u?2:1,flexDirection:"column",backgroundColor:t.panel,overflow:"hidden",children:[$e("box",{height:1,flexDirection:"row",overflow:"hidden",children:[v,u?null:m]},void 0,!0,void 0,this),u?$e("box",{height:1,flexDirection:"row",overflow:"hidden",backgroundColor:t.panel,children:m},void 0,!1,void 0,this):null]},void 0,!0,void 0,this)},void 0,!1,void 0,this)}function Qx(e){let{palette:t,screen:r,width:n,onSelect:o}=e,i=n>=Jx+gI,s=i?Math.max(1,n-Jx):n,a=bo.map((l)=>({id:l.id,label:Xb(l.label,s)})),c=Zb(r);return $e(Ut,{palette:t,backgroundColor:t.element,children:$e("box",{height:1,flexDirection:"row",overflow:"hidden",backgroundColor:t.element,children:[$e(ul,{palette:t,items:a,active:c,width:s,onPick:(l)=>{let d=bo[l];if(d)o(d.id)}},void 0,!1,void 0,this),i?$e(vI,{children:[$e("box",{flexGrow:1,backgroundColor:t.element},void 0,!1,void 0,this),$e(it,{palette:t,hints:[Gr]},void 0,!1,void 0,this)]},void 0,!0,void 0,this):null]},void 0,!0,void 0,this)},void 0,!1,void 0,this)}function Zx(e){let{palette:t,screen:r,overlay:n,status:o,paused:i,errorOnly:s,width:a,copyKey:c}=e,l=Ss(ye()),d=Math.max(18,Math.floor(a*0.42)),u=sx(Ax(r,n,c),d),f=ax(o);return $e(Ut,{palette:t,backgroundColor:t.panel,edge:"top",ruled:!l,children:$e("box",{height:1,backgroundColor:t.panel,overflow:"hidden",children:$e(ut,{ruled:!1,palette:t,items:[{text:r,tone:"ghost"},{text:i?"PAUSED":"LIVE",tone:i?"warning":"success"},...s?[{text:"ERROR+",tone:"error"}]:[],...o===""?[]:[{text:Re(o,Math.max(16,a-d-28)),tone:f}]],hints:u},void 0,!1,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this)}function ze(e){return $e("box",{flexGrow:1,padding:1,overflow:"hidden",children:$e(pl,{palette:e.palette,title:e.title,body:e.body,hint:e.hint},void 0,!1,void 0,this)},void 0,!1,void 0,this)}function fl(e){return $e("box",{flexGrow:1,padding:1,overflow:"hidden",children:$e("text",{fg:e.palette.text,children:e.label},void 0,!1,void 0,this)},void 0,!1,void 0,this)}function ew(e){return $e("box",{flexGrow:1,padding:1,flexDirection:"column",overflow:"hidden",children:[$e("text",{fg:e.palette.error,children:e.title},void 0,!1,void 0,this),$e("text",{fg:e.palette.text,wrapMode:"word",children:e.body},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}var Jx,gI=8;var Yt=I(()=>{Mt();vr();wo();Ot();In();dl();ke();Er();Jx=Gr.key.length+Gr.label.length+3});function yI(){if(process.platform==="darwin")return[["pbcopy"]];if(process.platform==="win32")return[["clip"]];return[["wl-copy"],["xclip","-selection","clipboard"],["xsel","--clipboard","--input"]]}function bI(){return yI().filter((e)=>{let t=e[0];return typeof t==="string"&&Bun.which(t)!==null})}function xI(e){return`\x1B]52;c;${Buffer.from(e,"utf8").toString("base64")}\x07`}function tw(){if(process.platform==="linux")return"install wl-clipboard (Wayland) or xclip (X11)";return"no clipboard helper found"}async function ml(e){let t=bI(),r;for(let n of t)try{await SI(n,e);return}catch(o){r=o instanceof Error?o:Error(String(o))}if(process.platform!=="win32")try{await wI(e);return}catch(n){r=n instanceof Error?n:Error(String(n))}throw Te(lt,"clipboard unavailable",r?`${tw()} (${r.message})`:tw())}async function wI(e){let t=xI(e);try{await Bun.write("/dev/tty",t)}catch{if(!process.stderr.write(t))throw Error("terminal clipboard write failed")}}async function SI(e,t){let r=Bun.spawn(e,{stdin:"pipe",stdout:"ignore",stderr:"ignore"});r.stdin.write(t),await r.stdin.end();let n=await r.exited;if(n!==0)throw Error(`${e[0]} exited ${n}`)}var qp=I(()=>{te()});function Vp(e){return e==="slash"||e==="config-edit"||e==="setup-wizard"}function rw(e,t){return hn(e,t.keybinds.leader??Ao("x"))}function nw(e,t){return hn(e,t.keybinds.command_list??Ao("p"))}function cn(e,t){return hn(e,t.keybinds.command??"/")||e.name==="/"}function ow(e,t){return hn(e,t.keybinds.help??"?")||e.sequence==="?"}function iw(e,t){return hn(e,t.keybinds.search??"f")}function sw(e){return(e.name??"").toLowerCase()==="q"&&!e.ctrl&&!e.meta&&!e.super&&!e.alt&&!e.option}function aw(e){let t=e.name??"";return t==="R"||e.shift===!0&&t.toLowerCase()==="r"}function cw(e){return Or(e)&&(e.name??"").toLowerCase()==="l"}function lw(e,t){return hn(e,t.keybinds.copy??Zr())}function dw(e,t,r=kI){return t>0&&e-t<=r}function Mn(e,t,r,n){return hn(e,t.keybinds[r]??n)}function Ds(e){let t=(e.name??"").toLowerCase();return t==="pagedown"||Or(e)&&t==="d"}function Is(e){let t=(e.name??"").toLowerCase();return t==="pageup"||Or(e)&&t==="u"}var kI=2000;var hl=I(()=>{Rr()});function mi(e){return`http://127.0.0.1:${e}/mcp`}function zp(e){if(e==="")return{};return{Authorization:`Bearer ${e}`}}function Yp(e,t){return JSON.stringify({mcpServers:{devctl:{type:"http",url:e,headers:zp(t)}}},null,2)}function Jp(e,t){return JSON.stringify({mcpServers:{devctl:{url:e,headers:zp(t)}}},null,2)}function Xp(e,t){return JSON.stringify({mcp:{devctl:{type:"remote",url:e,headers:zp(t),enabled:!0}}},null,2)}function Qp(e,t){let r=t===""?"":`
|
|
2213
|
+
`),o=[];for(let i of n)if(i.length<=r)o.push(i);else o.push(...sD(i,r));return o.length>0?o:[""]}function Gb(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=` ${tD}${o}`,s=Math.max(1,t-i.length);return{visible:[_e(n[0]??"",s)],mark:i,folded:!0,hidden:o}}function Kb(e,t){return e==="all"||e==="focus"&&t}function nl(e){if(e==="clip")return"focus";if(e==="focus")return"all";return"clip"}function Op(e,t,r,n=Mr){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 ol(e,t=Mr){return Math.max(0,e-t)}function $p(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 il(e){if(e==="all")return"wrap all";if(e==="focus")return"wrap selected";return"clip"}function sD(e,t){let r=[],n=e;while(n.length>t){let o=n.slice(0,t),i=aD(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 aD(e){let t=e.lastIndexOf(" "),r=e.lastIndexOf("\t"),n=e.lastIndexOf(","),o=Math.max(t,r,n);if(o<Math.floor(e.length*rD))return-1;return e[o]===","?o+1:o}function cD(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function lD(e,t=!1){let r=e.trim();if(r==="")return;if(t){let n=Ni(r);if(n)return new RegExp(n.source,n.flags.includes("g")?n.flags:`${n.flags}g`)}return new RegExp(cD(r),"gi")}function dD(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 m=Math.max(u.start,c),p=Math.min(u.end,l);if(p<=m)continue;let f=m-c,h=p-c;if(f>d)i.push({text:a.text.slice(d,f),kind:a.kind});i.push({text:a.text.slice(f,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 qb(e,t="",r=!1){let n=tl(e),o=[],i=new RegExp(nD.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:oD(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=lD(t,r);return l?dD(c,l):c}function Vb(e){let t=e.showTimestamps?Qc:0,r=e.showMeta?Xc:0;return Math.max(jb,e.width-t-e.serviceWidth-Zc-r-Bb-$s*Ub)}function zb(e,t){let r=t.reduce((s,a)=>Math.max(s,a.length),0),n=Qc+Zc+Bb+jb+$s*Ub,o=Math.max(Nb,e-n),i=Math.max(Nb,r);return Math.min(Tp,o,i)}function di(e){return uD.has(e)}function Jb(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 Xb(e,t){let r=t.length>0?t[t.length-1].seq:-1,n=e.filter((o)=>o.seq>r);return[...t,...n]}function Qb(e,t){if(t.length===0)return e;let r=new Set(e.map((o)=>o.seq)),n=t.filter((o)=>!r.has(o.seq));return n.length===0?e:[...n,...e]}function Fp(e,t,r){return e&&t<=0&&r}function Zb(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=Ni(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&&di(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 pD(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 ex(e,t,r=[]){let n=sl(e,t,r);return[{name:"",count:t.length},...pD(t,n)]}function mD(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 tx(e,t,r=[]){let n=Object.keys(t.byService).map((i)=>({service:i})),o=sl(e,n,r);return[{name:"",count:t.total},...mD(o,t.byService)]}function rx(e,t){if(e<=0)return"none started";return`${e}/${t} running`}function sl(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 Np(e,t,r){let n=["",...e],o=n.indexOf(t),s=((o<0?0:o)+r+n.length)%n.length;return n[s]??""}var Xc=8,Qc=9,Zc=7,Nb=10,jb=16,Bb=2,$s=1,Mr=200,eD=2,Hb=1,tD="\u25B8",Ub=2,rD=0.4,nD,iD,uD,Yb;var vr=I(()=>{Sa();Ot();Lt();nD=/("[^"]*"|'[^']*'|\b(?:ERROR|FATAL|WARN(?:ING)?|FAIL(?:ED)?)\b|\b\d{3,5}\b)/gi;iD=/\x1b\[[0-9;?]*[ -/]*[@-~]|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|\x1b[@-Z\\-_]/g;uD=new Set(["auth","devctl","proxy"]),Yb=["devctl","mcp","auth"]});function ui(e){return e.length+hD}function nx(e,t){if(t<vD)return _e(e,3);if(t<gD)return _e(e,4);return e}function ox(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=Hp(e,o,r);if(i.start===0&&i.end===n-1)return i;let s=o>0?al:0,a=o<n-1?al:0,c=s+a,l=Hp(e,o,Math.max(e[o]??0,r-c)),d=(s>0&&l.start===0?al:0)+(a>0&&l.end===n-1?al:0);if(d===0)return l;return Hp(e,o,Math.max(e[o]??0,r-c+d))}function Hp(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),m=i+1-t;if(l&&(!d||u<=m))o-=1,s+=a;else if(d)i+=1,s+=c;else break}return{start:o,end:i}}function ix(e){if(e==="detail")return wo.findIndex((t)=>t.id==="services");return wo.findIndex((t)=>t.id===e)}function sx(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 fD;if(e==="config")return t.config??0;return 0}function ax(e){if(e.length===1&&e>="1"&&e<="9")return wo[Number(e)-1]?.id;return}var fD=9,hD=2,al=2,gD=80,vD=56,wo,Mn;var On=I(()=>{Ot();wo=[{id:"dashboard",label:"dashboard"},{id:"services",label:"services"},{id:"logs",label:"logs"},{id:"proxy",label:"proxy"}];Mn=wo.map((e)=>e.id)});function dx(e,t=!0){let r=e<cl?cx+1:cx,n=t?kD:0;return r+n+wD+n+SD+n}function mi(e){return Math.max(bD,e-xD*2)}function ux(e){let t=e.width<yD,r=e.proxyOn?`\u25CF ${_e(e.proxyAddress,t?10:18)}`:"";return[{label:t?`${e.running}/${e.total}`:rx(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 lx(e){return e.key==="/"&&e.label==="command"}function CD(e){return e.key.length+e.label.length+3}function px(e,t){let r=e.filter(lx),n=e.filter((s)=>!lx(s)),o=[],i=0;for(let s of[...r,...n]){let a=CD(s);if(i+a>t)break;o.push(s),i+=a}return o}function ll(e,t,r,n,o="center",i=!0){let s=Math.min(r,Math.max(1,e-4)),a=Math.min(dx(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 mx(e){if(e==="")return"idle";if(RD.test(e))return"error";if(e.includes("session only"))return"warning";if(PD.test(e))return"success";return"info"}function fx(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 hx(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 gx(e){let t=e.payload?.error;return typeof t==="string"&&t!==""?t:"configuration reload failed"}var pi=100,cl=90,yD=60,bD=8,xD=6,cx=1,wD=1,SD=1,kD=1,PW,PD,RD;var yr=I(()=>{Ot();vr();On();PW=dx(cl-1);PD=/^(started|stopped|restarted|refreshed|cleared|copied|exported|jumped|proxy started|proxy stopped|secrets|re-running|theme |mouse |leader |display |restored)/i,RD=/\b(fail|error|unknown|not found|no configuration|blocked|already in use|is using port)\b/i});function dl(){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 So(e){let t=e.trim().toLowerCase().replace(/^\//,""),r=t.indexOf(" ");return r===-1?t:t.slice(0,r)}function wx(e){let t=So(e);if(t==="")return dl();return dl().map((r)=>({command:r,score:BD(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 BD(e,t){if(t==="")return 1;let r=e.name.toLowerCase(),n=e.aliases.map((o)=>o.toLowerCase());return WD(r,n,t)||GD(r,n,t)||KD(r,n,t)||qD(r,n,t)||UD(e.suggest,t)||VD(e.desc.toLowerCase(),t)}function UD(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 yx;if(t.length<xx)return 0;return r.some((n)=>n.includes(t)||n.replace(/^-+/,"").startsWith(t))?bx:0}function WD(e,t,r){if(e===r)return _D;return t.includes(r)?ED:0}function GD(e,t,r){if(e.startsWith(r))return TD;if(t.some((i)=>i.startsWith(r)))return AD;let n=e.split("-");return n.length>1&&n.some((i)=>i.startsWith(r))?LD:0}function KD(e,t,r){if(r.length<xx)return 0;if(e.includes(r))return yx;return t.some((n)=>n.includes(r))?bx:0}function qD(e,t,r){if(r.length<$D)return 0;let n=vx(e,r);if(n>0)return DD+Math.min(n,jD);return t.some((o)=>vx(o,r)>0)?ID:0}function vx(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 VD(e,t){if(t.length<FD)return 0;if(e.split(/[^a-z0-9+]+/).filter((n)=>n.length>=ND).some((n)=>n.startsWith(t)))return MD;return t.length>=HD&&e.includes(t)?OD:0}function Gr(e){let t=e.trim().toLowerCase().replace(/^\//,"").split(" ")[0]??"";return dl().find((r)=>r.name===t||r.aliases.includes(t))}function jp(e){return e.trim().replace(/^\//,"").split(/\s+/).slice(1).filter((r)=>r!=="")}function Sx(e){return dl().find((r)=>r.leader===e)?.name??""}function kx(e){let t=e.includes("--cascade")||e.includes("-c");return{services:e.filter((n)=>n!=="--cascade"&&n!=="-c"),cascade:t}}function Cx(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 _D=100,ED=90,TD=80,AD=70,LD=60,yx=50,bx=45,DD=30,ID=28,MD=20,OD=10,xx=2,$D=3,FD=4,ND=4,HD=4,jD=9;function Ex(e){return e==="detail"?"services":e}function Tx(e){let t=Mn.indexOf(Ex(e));if(t<0)return"dashboard";return Mn[(t+1)%Mn.length]??"dashboard"}function Ax(e){let t=Mn.indexOf(Ex(e));if(t<0)return"dashboard";return Mn[(t-1+Mn.length)%Mn.length]??"dashboard"}function Up(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 ul(e){return e.hint?`${e.name} ${e.hint}`:e.name}function Wp(e){return e.name}function Lx(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 Dx(e){let t=e.reduce((n,o)=>Math.max(n,Wp(o).length),0),r=Math.min(Bp,Math.max(t,1));return zD+r+YD}function Ix(e){return e.hint?`${e.name} ${e.hint} `:`${e.name} `}function Mx(e,t){if(e.hint)return e.hint.split(/\s+/);return Gr(t)?.name===e.name?jp(t):[]}function Px(e,t){if(t==="")return!0;let r=t.toLowerCase(),n=e.toLowerCase();return n.startsWith(r)||n.includes(r)}function JD(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 Rx(e,t){return{...e,hint:t.token,desc:t.desc,usage:void 0,suggest:void 0}}function XD(e,t,r={}){let n=jp(t).join(" "),o=t.trim().includes(" ")||t.endsWith(" "),i=So(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(Px(d.token,l))s.push(Rx(a,d));if(!c||!o)continue;for(let d of JD(a,r))if(Px(d.token,n))s.push(Rx(a,d))}return s}function Ox(e,t={}){let r=wx(e),n=So(e)===""?Up(r).flatMap((o)=>o.items):r;return XD(n,e,t)}function Gp(e,t,r,n){let o=So(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 $x(e,t,r=en()){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[Kr,{key:"\u2191\u2193",label:"move"},{key:"enter",label:"save"},{key:"esc",label:"revert"}];if(t==="help")return[Kr,{key:"j/k",label:"scroll"},{key:"esc",label:"close"}];if(t==="log-details"||t==="scroll-text")return[Kr,{key:"j/k",label:"scroll"},{key:Li(r),label:"copy"},{key:"esc",label:"close"}];if(t==="confirm")return[{key:"enter",label:"confirm"},{key:"esc",label:"stay"}];if(t==="plan")return[Kr,{key:"esc",label:"back to dashboard"},{key:"enter",label:"done"}];if(t==="leader")return[Kr,...Kp()];if(t==="config-edit")return[{key:It("s"),label:"save"},{key:"esc",label:"discard"}];return QD(e,r)}function Kp(){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 QD(e,t){let r=[{key:Li(t),label:"copy"},Kr,{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 Fx(e,t,r){if(r<=t||e<t)return 0;return Math.min(e-t+1,r-t)}function _x(e,t,r){let n=e.slice(t,r+1),o=new Set(n.map((i)=>i.group));return n.length+o.size}function Nx(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&&_x(e,o,i+1)<=r,a=o>0&&_x(e,o-1,i)<=r;if(s)i+=1;else if(a)o-=1;else break}return e.slice(o,i+1)}function fi(e,t){if(e.length===0)return;let r=Math.min(Math.max(t,0),e.length-1);return e[r]}var Kr,zD=3,YD=2,Bp=22;var ko=I(()=>{Lr();On();Kr={key:"/",label:"command"}});function ml(e,t){let r=t??Object.keys(e?.services??{});return{running:r.filter((o)=>bo(e?.services[o])).length,total:r.length}}function Hx(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(Os(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 jx(e,t){if(!Number.isFinite(e)||!Number.isFinite(t)||t<=0)return 0;return e/t}function oI(e,t,r){if(!Number.isFinite(e))return"success";if(e>r)return"error";if(e>t)return"warning";return"success"}function iI(e,t){return oI(jx(e,t),ZD,eI)}function Yp(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 Fs[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(Fs.length-1,Math.round(i*(Fs.length-1)));return Fs[s]??Fs[0]}).join("")}function sI(e,t){let r=t>0?e/t:1;if(r<rI)return"error";if(r<tI)return"warning";return"success"}function qp(e){if(!Number.isFinite(e)||e<0)return"\u2014";return e.toFixed(2)}function aI(e){if(!Number.isFinite(e)||e<0)return"\u2014";return`${Math.round(e*100)}%`}function Bx(e,t=Date.now()){if(!e?.startTime)return"\u2014";let r=new Date(e.startTime).getTime();if(!Number.isFinite(r))return"\u2014";return Ts(t-r)}function Ux(e,t=nI){return Object.entries(e).sort((r,n)=>n[1]-r[1]||r[0].localeCompare(n[0])).slice(0,t)}function Wx(e,t=1){return Math.max(24,e-fI-t*2-hI-gI-vI)}function Gx(e){let t=e>=cI,r=e>=lI,n=e>=dI,o=e>=uI,i=e>=pI,s=e>=mI,a=Ir+$t;if(t)a+=Wr+$t;if(r)a+=Ds+$t;if(n)a+=Is+$t;if(o)a+=Ls+$t;if(i)a+=fl+$t;if(s)a+=ln;return{name:Math.max(Uc,e-a),health:t,cpu:r,mem:n,up:o,rst:i,pid:s}}function Kx(e){let t=Number.isFinite(e)&&e>0?e:0;return{ratio:Math.min(1,t),label:aI(t)}}function qx(e,t=xI){return`[${li(e.ratio,t)}] ${e.label.padStart(4)}`}function Jp(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)?wI:0,i=hi*(o>0?3:2),s=Math.min(yI,Math.max(Vp,r)),a=Math.min(bI,Math.max(zp,n)),c=t-s-a-o-i;if(c<pl&&a>zp)a=Math.max(zp,a-(pl-c)),c=t-s-a-o-i;if(c<pl&&s>Vp)s=Math.max(Vp,s-(pl-c)),c=t-s-a-o-i;return{what:s,reading:a,meaning:Math.max(0,c),meter:o}}function Vx(e){return SI.has((e?.health.type??"").toLowerCase())}function zx(e){if(e==="darwin")return"macOS";if(e==="win32")return"Windows";if(e==="linux")return"Linux";return e||"this computer"}function Yx(e,t){let r=Math.max(1,t),n=iI(e,r),o=n==="error"?"overloaded":n==="warning"?"busy":"not busy",i=n==="error"?`${r} cores are queued up \u2014 last minute ${qp(e)}`:n==="warning"?`${r} cores nearly full \u2014 last minute ${qp(e)}`:`${r} cores. Last minute ${qp(e)} (under ${r} is fine)`;return{what:"CPU work",reading:o,meaning:i,tone:n,meter:Kx(jx(e,r))}}function Jx(e,t){let r=sI(e,t),n=`${As(e)} of ${As(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:Kx(i)}}function Xx(e){let t=e?.state??"STOPPED";if(t==="FAILED")return"crashed";if(t==="STOPPING")return"stopping";if(t==="STARTING"||t==="RESTARTING")return"starting";if(Os(t))return"up";return"off"}function Qx(e){if(!e||!Os(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 Zx(e){if(e==="keychain")return"password store";if(e==="file")return"local file";return e||"unknown store"}function ew(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 ZD=0.85,eI=1.5,tI=0.2,rI=0.1,nI=5,Fs="\u2581\u2582\u2583\u2584\u2585\u2586\u2587\u2588",cI=52,lI=64,dI=72,uI=82,pI=90,mI=100,fI=2,hI=2,gI=2,vI=1,fl=8,Vp=8,yI=22,zp=6,bI=28,pl=12,hi=2,xI=10,wI=18,SI;var hl=I(()=>{Ot();Lt();SI=new Set(["http","tcp","command"])});import{useEffect as kI,useRef as CI}from"react";import{jsxDEV as fe,Fragment as RI}from"@opentui/react/jsx-dev-runtime";function PI(e,t){switch(t){case"primary":return{bg:e.primary,fg:go(e.primary,e.inverse)};case"accent":return{bg:e.accent,fg:$c(e)?Oc:go(e.accent,e.inverse)};case"success":return{bg:e.success,fg:go(e.success,e.inverse)};case"warning":return{bg:e.warning,fg:$c(e)?Oc:go(e.warning,e.inverse)};case"error":return{bg:e.error,fg:go(e.error,e.inverse)};case"info":return{bg:e.info,fg:go(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 tw(e){let{palette:t,dir:r}=e;return fe("box",{width:2,height:1,flexShrink:0,overflow:"hidden",children:fe("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=be(),s=e.ruled??!vo(i),a=n??t.element,c=fe("box",{flexShrink:0,flexDirection:"column",backgroundColor:a,overflow:"hidden",children:r},void 0,!1,void 0,this);if(!s)return c;return fe("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 gl(e){let{palette:t,items:r,active:n,width:o,onPick:i,emphasis:s="ghost"}=e,a=r.map((u)=>ui(u.label)),c=ox(a,n,Math.max(1,o)),l=c.start>0,d=c.end>=0&&c.end<r.length-1;return fe("box",{height:1,flexDirection:"row",overflow:"hidden",backgroundColor:t.element,children:[l?fe(tw,{palette:t,dir:"left"},void 0,!1,void 0,this):null,r.map((u,m)=>{if(m<c.start||m>c.end)return null;let p=m===n,f=s==="fill"&&p;return fe(Be,{palette:t,label:u.label,tone:f?"primary":"ghost",bg:f?void 0:p?t.highlight:t.element,fg:f?void 0:p?t.primary:u.color??t.muted,onMouseDown:()=>i(m)},u.id,!1,void 0,this)}),d?fe(tw,{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=PI(t,n);return fe("box",{height:1,paddingLeft:1,paddingRight:1,backgroundColor:i??a.bg,overflow:"hidden",flexShrink:0,onMouseDown:s,children:fe("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 fe("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,children:r.map((n)=>fe("box",{height:1,paddingLeft:1,paddingRight:1,flexShrink:0,overflow:"hidden",children:fe("text",{wrapMode:"none",children:[fe("span",{fg:t.primary,children:n.key},void 0,!1,void 0,this),fe("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 pt(e){let{palette:t,items:r,hints:n,ruled:o=!0}=e,i=fe("box",{height:1,flexDirection:"row",overflow:"hidden",backgroundColor:t.element,children:[r.map((s,a)=>fe(Be,{palette:t,label:s.text,tone:s.tone,onMouseDown:s.onMouseDown},`${s.text}-${a}`,!1,void 0,this)),n&&n.length>0?fe(RI,{children:[fe("box",{flexGrow:1,backgroundColor:t.element},void 0,!1,void 0,this),fe(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 fe(Ut,{palette:t,backgroundColor:t.element,children:i},void 0,!1,void 0,this)}function mt(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,m=vo(be()),p=ll(n,o,i,s,a,!m);return fe("box",{position:"absolute",left:p.left,top:p.top,width:p.width,height:p.height,border:!0,borderStyle:Xp,borderColor:c??t.borderActive,backgroundColor:t.panel,padding:m?0:1,title:r,titleColor:t.primary,bottomTitle:l,gap:m?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 gi(e,t){let r=CI(null);return kI(()=>{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=be(),r=Es(t),n=e.scroll?fe("scrollbox",{focused:!1,stickyScroll:!1,scrollX:!1,style:Ue(e.palette),children:fe("box",{flexDirection:"column",overflow:"hidden",children:e.children},void 0,!1,void 0,this)},void 0,!1,void 0,this):e.children;return fe("box",{flexGrow:1,border:!0,borderStyle:Xp,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 Co(e){let{palette:t,label:r,value:n,tone:o="text"}=e,i=be(),s=o==="success"?t.success:o==="warning"?t.warning:o==="error"?t.error:o==="muted"?t.muted:t.text;return fe("box",{height:i.rowH,flexDirection:"row",overflow:"hidden",children:[fe("box",{width:14,flexShrink:0,overflow:"hidden",children:fe("text",{fg:t.muted,children:r},void 0,!1,void 0,this)},void 0,!1,void 0,this),fe("box",{flexGrow:1,overflow:"hidden",children:fe("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 vl(e){let{palette:t,title:r,body:n,hint:o}=e;return fe("box",{paddingLeft:1,paddingRight:1,flexDirection:"column",overflow:"hidden",border:!0,borderStyle:Xp,borderColor:t.borderActive,titleColor:t.primary,children:[fe("text",{fg:t.primary,children:r},void 0,!1,void 0,this),fe("text",{fg:t.text,wrapMode:"word",children:n},void 0,!1,void 0,this),o?fe("text",{fg:t.muted,wrapMode:"word",children:o},void 0,!1,void 0,this):null]},void 0,!0,void 0,this)}var Xp="rounded";var Pe=I(()=>{Mt();yr();On();Dr();tt()});import{jsxDEV as $e,Fragment as EI}from"@opentui/react/jsx-dev-runtime";function nw(e){let{palette:t,cfg:r,snap:n,google:o,profile:i,reveal:s,width:a}=e,c=ml(n,r?Object.keys(r.services):void 0),l=n?.proxy.running===!0,d=o?.adcAvailable===!0,u=a<cl,m=_e(r?.project.name||"local",u?18:22),p=_e(i||n?.profile||"no profile",16),f=ux({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=Es(be()),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:tr()},void 0,!1,void 0,this),$e("span",{fg:t.muted,children:` ${m}`},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:f]},void 0,!0,void 0,this),u?$e("box",{height:1,flexDirection:"row",overflow:"hidden",backgroundColor:t.panel,children:f},void 0,!1,void 0,this):null]},void 0,!0,void 0,this)},void 0,!1,void 0,this)}function ow(e){let{palette:t,screen:r,width:n,onSelect:o}=e,i=n>=rw+_I,s=i?Math.max(1,n-rw):n,a=wo.map((l)=>({id:l.id,label:nx(l.label,s)})),c=ix(r);return $e(Ut,{palette:t,backgroundColor:t.element,children:$e("box",{height:1,flexDirection:"row",overflow:"hidden",backgroundColor:t.element,children:[$e(gl,{palette:t,items:a,active:c,width:s,onPick:(l)=>{let d=wo[l];if(d)o(d.id)}},void 0,!1,void 0,this),i?$e(EI,{children:[$e("box",{flexGrow:1,backgroundColor:t.element},void 0,!1,void 0,this),$e(it,{palette:t,hints:[Kr]},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 iw(e){let{palette:t,screen:r,overlay:n,status:o,paused:i,errorOnly:s,width:a,copyKey:c}=e,l=Es(be()),d=Math.max(18,Math.floor(a*0.42)),u=px($x(r,n,c),d),m=mx(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(pt,{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:m}]],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(vl,{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 yl(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 sw(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 rw,_I=8;var Jt=I(()=>{Mt();yr();ko();Ot();On();hl();Pe();Dr();rw=Kr.key.length+Kr.label.length+3});function TI(){if(process.platform==="darwin")return[["pbcopy"]];if(process.platform==="win32")return[["clip"]];return[["wl-copy"],["xclip","-selection","clipboard"],["xsel","--clipboard","--input"]]}function AI(){return TI().filter((e)=>{let t=e[0];return typeof t==="string"&&Bun.which(t)!==null})}function LI(e){return`\x1B]52;c;${Buffer.from(e,"utf8").toString("base64")}\x07`}function aw(){if(process.platform==="linux")return"install wl-clipboard (Wayland) or xclip (X11)";return"no clipboard helper found"}async function bl(e){let t=AI(),r;for(let n of t)try{await II(n,e);return}catch(o){r=o instanceof Error?o:Error(String(o))}if(process.platform!=="win32")try{await DI(e);return}catch(n){r=n instanceof Error?n:Error(String(n))}throw Te(lt,"clipboard unavailable",r?`${aw()} (${r.message})`:aw())}async function DI(e){let t=LI(e);try{await Bun.write("/dev/tty",t)}catch{if(!process.stderr.write(t))throw Error("terminal clipboard write failed")}}async function II(e,t){let r=Bun.spawn(e,{stdin:"pipe",stdout:"ignore",stderr:"ignore"});r.stdin.write(t),await r.stdin.end();let n=await r.exited;if(n!==0)throw Error(`${e[0]} exited ${n}`)}var Qp=I(()=>{te()});function Zp(e){return e==="slash"||e==="config-edit"||e==="setup-wizard"}function cw(e,t){return vn(e,t.keybinds.leader??Do("x"))}function lw(e,t){return vn(e,t.keybinds.command_list??Do("p"))}function dn(e,t){return vn(e,t.keybinds.command??"/")||e.name==="/"}function dw(e,t){return vn(e,t.keybinds.help??"?")||e.sequence==="?"}function uw(e,t){return vn(e,t.keybinds.search??"f")}function pw(e){return(e.name??"").toLowerCase()==="q"&&!e.ctrl&&!e.meta&&!e.super&&!e.alt&&!e.option}function mw(e){let t=e.name??"";return t==="R"||e.shift===!0&&t.toLowerCase()==="r"}function fw(e){return Hr(e)&&(e.name??"").toLowerCase()==="l"}function hw(e,t){return vn(e,t.keybinds.copy??en())}function gw(e,t,r=MI){return t>0&&e-t<=r}function $n(e,t,r,n){return vn(e,t.keybinds[r]??n)}function Ns(e){let t=(e.name??"").toLowerCase();return t==="pagedown"||Hr(e)&&t==="d"}function Hs(e){let t=(e.name??"").toLowerCase();return t==="pageup"||Hr(e)&&t==="u"}var MI=2000;var xl=I(()=>{Lr()});function vi(e){return`http://127.0.0.1:${e}/mcp`}function em(e){if(e==="")return{};return{Authorization:`Bearer ${e}`}}function tm(e,t){return JSON.stringify({mcpServers:{devctl:{type:"http",url:e,headers:em(t)}}},null,2)}function rm(e,t){return JSON.stringify({mcpServers:{devctl:{url:e,headers:em(t)}}},null,2)}function nm(e,t){return JSON.stringify({mcp:{devctl:{type:"remote",url:e,headers:em(t),enabled:!0}}},null,2)}function om(e,t){let r=t===""?"":`
|
|
2214
2214
|
http_headers = { Authorization = "Bearer ${t}" }`;return`[mcp_servers.devctl]
|
|
2215
2215
|
url = "${e}"${r}
|
|
2216
|
-
`}function
|
|
2217
|
-
`)}import{jsxDEV as q}from"@opentui/react/jsx-dev-runtime";function rf(){return cp.flatMap((e)=>_c.filter((t)=>t.category===e))}function vl(){return gl+rf().length}function mw(){return vl()+fw}function Os(e){if(e<gl||e>=vl())return;return rf()[e-gl]}function nf(e){let t=e-vl();return t>=0&&t<fw?t:void 0}function hw(e){let{palette:t,snap:r,port:n,portDraft:o="",selected:i,onPick:s,onToggle:a,onCopy:c,onToggleTool:l}=e,d=ye(),u=r?.mcp?.running===!0,f=r?.mcp?.port??n,p=r?.mcp?.address??mi(f),m=r?.mcp?.token??"",h=Ms(p,m),v=r?.mcp?.disabled_tools??[],b=rf(),g=b.filter((S)=>!An(S.name,v)).length,_=h[nf(i)??-1],R=_??h[0];return q(kt,{palette:t,title:"mcp settings",children:[q(ut,{palette:t,items:[{text:u?"RUNNING":"STOPPED",tone:u?"success":"idle"},{text:`127.0.0.1:${f}`,tone:"info"}]},void 0,!1,void 0,this),q("box",{flexGrow:1,overflow:"hidden",children:q("scrollbox",{focused:!1,stickyScroll:!1,scrollX:!1,style:Ue(t),children:q("box",{flexDirection:"column",overflow:"hidden",children:[q(Zp,{palette:t,text:"Server"},void 0,!1,void 0,this),q(pw,{palette:t,active:i===ef,rowH:d.rowH,label:"Listen",value:u?"[ ON ]":"[ OFF ]",valueTone:u?"success":"muted",hint:u?"space or enter stop":"space or enter start",onPick:()=>s(ef),onActivate:a},void 0,!1,void 0,this),q(pw,{palette:t,active:i===tf,rowH:d.rowH,label:"Port",value:`\u2039 ${o===""?n:o} \u203A`,valueTone:"info",hint:o===""?"\u2190 \u2192 change type digits enter apply":"typing\u2026 enter apply \u232B erase",onPick:()=>s(tf)},void 0,!1,void 0,this),q("box",{height:1,overflow:"hidden",children:q("text",{fg:t.muted,wrapMode:"none",children:u?"Listening on loopback. Agents use the URL and token in the JSON below.":"Server is stopped. Highlight Listen and press space to start it."},void 0,!1,void 0,this)},void 0,!1,void 0,this),q(Zp,{palette:t,text:"Tools",extra:g===0?"all enabled":`${g} disabled`},void 0,!1,void 0,this),q("box",{height:1,overflow:"hidden",children:q("text",{fg:t.muted,wrapMode:"none",children:"Disabled tools are hidden from agents and refused if called anyway."},void 0,!1,void 0,this)},void 0,!1,void 0,this),cp.map((S)=>{let y=b.filter((x)=>x.category===S);if(y.length===0)return null;return q("box",{flexDirection:"column",overflow:"hidden",children:[q("box",{height:1,marginTop:1,overflow:"hidden",children:q("text",{fg:t.muted,wrapMode:"none",children:PI[S]??S},void 0,!1,void 0,this)},void 0,!1,void 0,this),y.map((x)=>{let w=gl+b.indexOf(x);return q(TI,{palette:t,tool:x,enabled:An(x.name,v),active:i===w,rowH:d.rowH,onPick:()=>s(w),onToggle:()=>l(x)},x.name,!1,void 0,this)})]},S,!0,void 0,this)}),q(Zp,{palette:t,text:"Copy agent config",extra:"highlight a row, then enter or click Copy"},void 0,!1,void 0,this),h.map((S,y)=>{let x=vl()+y;return q(EI,{palette:t,snippet:S,active:i===x,rowH:d.rowH,onPick:()=>s(x),onCopy:()=>c(S)},S.kind,!1,void 0,this)}),q(AI,{palette:t,snippet:R,selected:Boolean(_),brand:R?Dc(R.kind,t):t.muted},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this),q(Ut,{palette:t,backgroundColor:t.element,edge:"top",children:q(it,{palette:t,hints:RI(i,u)},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function RI(e,t){if(e===ef)return[{key:"space",label:t?"stop server":"start server"},{key:"j/k",label:"move"}];if(e===tf)return[{key:"\u2190\u2192",label:"change port"},{key:"0-9",label:"type port"},{key:"enter",label:"apply port"},{key:"j/k",label:"move"}];if(Os(e))return[{key:"space",label:"enable / disable"},{key:"enter",label:"enable / disable"},{key:"j/k",label:"move"}];return[{key:"enter",label:"copy JSON"},{key:"space",label:"copy JSON"},{key:"j/k",label:"move"}]}function Zp(e){let t=e.extra?` \xB7 ${e.extra}`:"";return q("box",{height:1,marginTop:1,overflow:"hidden",children:q("text",{wrapMode:"none",children:[q("span",{fg:e.palette.primary,children:e.text},void 0,!1,void 0,this),q("span",{fg:e.palette.muted,children:t},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this)}function pw(e){let{palette:t,active:r,rowH:n,label:o,value:i,valueTone:s,hint:a,onPick:c,onActivate:l}=e,d=s==="success"?t.success:s==="info"?t.info:t.muted;return q("box",{height:n,flexDirection:"row",overflow:"hidden",backgroundColor:r?t.highlight:t.panel,onMouseDown:()=>{if(r&&l){l();return}c()},children:[q("box",{width:2,flexShrink:0,overflow:"hidden",children:q("text",{fg:t.primary,children:r?"\u203A":" "},void 0,!1,void 0,this)},void 0,!1,void 0,this),q("box",{width:of,flexShrink:0,overflow:"hidden",children:q("text",{fg:r?t.primary:t.text,wrapMode:"none",children:o},void 0,!1,void 0,this)},void 0,!1,void 0,this),q("box",{width:_I,flexShrink:0,overflow:"hidden",children:q("text",{fg:d,wrapMode:"none",children:i},void 0,!1,void 0,this)},void 0,!1,void 0,this),q("box",{flexGrow:1,overflow:"hidden",children:q("text",{fg:r?t.text:t.muted,wrapMode:"none",children:a},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function EI(e){let{palette:t,snippet:r,active:n,rowH:o,onPick:i,onCopy:s}=e,a=Dc(r.kind,t),c=fb(r.kind,t),l=r.language==="toml"?"Copy TOML":"Copy JSON";return q("box",{height:o,flexDirection:"row",overflow:"hidden",backgroundColor:n?t.highlight:t.panel,onMouseDown:i,children:[q("box",{width:2,flexShrink:0,overflow:"hidden",children:q("text",{fg:a,children:n?"\u203A":" "},void 0,!1,void 0,this)},void 0,!1,void 0,this),q("box",{width:of,flexShrink:0,overflow:"hidden",children:q("text",{fg:a,wrapMode:"none",children:r.title},void 0,!1,void 0,this)},void 0,!1,void 0,this),q("box",{flexGrow:1,overflow:"hidden",children:q("text",{fg:t.muted,wrapMode:"none",children:r.path},void 0,!1,void 0,this)},void 0,!1,void 0,this),q(Be,{palette:t,label:l,bg:a,fg:c,onMouseDown:()=>{i(),s()}},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function TI(e){let{palette:t,tool:r,enabled:n,active:o,rowH:i,onPick:s,onToggle:a}=e;return q("box",{height:i,flexDirection:"row",overflow:"hidden",backgroundColor:o?t.highlight:t.panel,onMouseDown:()=>{if(o){a();return}s()},children:[q("box",{width:2,flexShrink:0,overflow:"hidden",children:q("text",{fg:t.primary,children:o?"\u203A":" "},void 0,!1,void 0,this)},void 0,!1,void 0,this),q("box",{width:5,flexShrink:0,overflow:"hidden",children:q("text",{fg:n?t.success:t.muted,wrapMode:"none",children:n?" ON ":" OFF "},void 0,!1,void 0,this)},void 0,!1,void 0,this),q("box",{width:of+4,flexShrink:0,overflow:"hidden",children:q("text",{fg:o?t.primary:n?t.text:t.muted,wrapMode:"none",children:r.label},void 0,!1,void 0,this)},void 0,!1,void 0,this),q("box",{width:7,flexShrink:0,overflow:"hidden",children:q("text",{fg:r.mutates?t.warning:t.muted,wrapMode:"none",children:r.mutates?"write":"read"},void 0,!1,void 0,this)},void 0,!1,void 0,this),q("box",{flexGrow:1,overflow:"hidden",children:q("text",{fg:o?t.text:t.muted,wrapMode:"none",children:o?`space or enter ${n?"disable":"enable"} ${r.name}`:r.summary},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function AI(e){let{palette:t,snippet:r,selected:n,brand:o}=e;if(!r)return null;let i=r.hint?` \xB7 ${r.hint}`:"";return q("box",{marginTop:1,flexDirection:"column",overflow:"hidden",children:[q("text",{fg:o,wrapMode:"none",children:n?`${r.title} \xB7 paste into ${r.path}${i}`:`Preview ${r.title} \xB7 j/k to a copy row, then enter`},void 0,!1,void 0,this),q("text",{fg:t.text,children:r.text},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}var ef=0,tf=1,gl=2,fw=4,PI,of=14,_I=14;var $s=I(()=>{Mt();ke();tt();fs();PI={inspect:"Inspect",logs:"Logs",diagnostics:"Diagnostics",control:"Control",setup:"Setup"}});import{jsxDEV as Fe,Fragment as vw}from"@opentui/react/jsx-dev-runtime";function NI(e){return[{key:"\u2190\u2192",label:"filter services"},{key:"e",label:"ERROR+ only"},{key:"g",label:"jump to latest"},{key:"p",label:"pause stream"},{key:"z",label:"full-screen logs"},{key:"\\",label:"split panes"},{key:"|",label:"focus other pane"},{key:"/trace",label:"jump to request id"},{key:"f",label:"search"},{key:"t",label:"timestamps"},{key:"m",label:"metadata"},{key:"w",label:"clip / wrap selected / wrap all"},{key:"j/k",label:"move and unfold"},{key:Ei(e),label:"copy selection"},{key:"/export",label:"write ~/.devctl/exports"},{key:"/exports",label:"open export folder"}]}function $n(e){return e+II}function yw(e){let t=$n(af.length),r=$n(cf.length),n=$n(e.logCount),o=$n(lf.length),i=$n(df.length),s=uf+e.pad*2;if(e.stacked)return s+t+r+n+o+i+sf+($I-1)*e.gap;let a=Math.max(t,r),c=Math.max(o,i);return s+a+n+c+sf+(FI-1)*e.gap}function HI(e){return yw(e)-uf-e.pad*2}function jI(e){return e-uf*2<DI}function bw(e){let{palette:t,termW:r,termH:n,copyKey:o=Zr(),scrollRef:i}=e,s=ho(ye()),a=jI(r),c=s?0:1,l=s?0:1,d=NI(o),u={stacked:a,logCount:d.length,pad:c,gap:l},f=yw(u),p=HI(u);return Fe(pt,{palette:t,title:"help",bottomTitle:`${er()} \xB7 j/k scroll \xB7 esc close`,termW:r,termH:n,preferW:LI,preferH:f,gap:0,children:Fe("scrollbox",{ref:i,focused:!1,stickyScroll:!1,scrollX:!1,style:Ue(t),children:Fe("box",{height:p,flexDirection:"column",flexShrink:0,overflow:"hidden",gap:l,children:[a?Fe(vw,{children:[Fe(On,{palette:t,title:"navigation",bindings:af},void 0,!1,void 0,this),Fe(On,{palette:t,title:"services",bindings:cf},void 0,!1,void 0,this),Fe(On,{palette:t,title:"logs",bindings:d},void 0,!1,void 0,this),Fe(On,{palette:t,title:"commands",bindings:lf},void 0,!1,void 0,this),Fe(On,{palette:t,title:"display",bindings:df},void 0,!1,void 0,this)]},void 0,!0,void 0,this):Fe(vw,{children:[Fe(gw,{palette:t,left:{title:"navigation",bindings:af},right:{title:"services",bindings:cf}},void 0,!1,void 0,this),Fe(On,{palette:t,title:"logs",bindings:d},void 0,!1,void 0,this),Fe(gw,{palette:t,left:{title:"commands",bindings:lf},right:{title:"display",bindings:df}},void 0,!1,void 0,this)]},void 0,!0,void 0,this),Fe("box",{height:sf,flexShrink:0,overflow:"hidden",paddingLeft:1,children:Fe("text",{fg:t.muted,wrapMode:"none",children:"~/.devctl/tui.json \xB7 DEVCTL_TUI_CONFIG"},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this)}function gw(e){let{palette:t,left:r,right:n}=e,o=Math.max($n(r.bindings.length),$n(n.bindings.length));return Fe("box",{height:o,flexDirection:"row",flexShrink:0,overflow:"hidden",gap:OI,children:[Fe(On,{palette:t,title:r.title,bindings:r.bindings,height:o,stretch:!0},void 0,!1,void 0,this),Fe(On,{palette:t,title:n.title,bindings:n.bindings,height:o,stretch:!0},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function On(e){let{palette:t,title:r,bindings:n,stretch:o=!1}=e,i=e.height??$n(n.length),s=BI(n);return Fe("box",{height:i,flexGrow:o?1:0,flexBasis:o?0:void 0,flexShrink:0,flexDirection:"column",overflow:"hidden",border:!0,borderStyle:"rounded",borderColor:t.border,title:r,titleColor:t.accent,children:n.map((a)=>Fe("box",{height:1,flexShrink:0,overflow:"hidden",paddingLeft:1,paddingRight:1,children:Fe(UI,{palette:t,binding:a,keyWidth:s},void 0,!1,void 0,this)},`${a.key}-${a.label}`,!1,void 0,this))},void 0,!1,void 0,this)}function BI(e){return e.reduce((r,n)=>Math.max(r,n.key.length),0)+MI}function UI(e){let{palette:t,binding:r,keyWidth:n}=e;return Fe("box",{height:1,flexDirection:"row",overflow:"hidden",children:[Fe("box",{width:n,flexShrink:0,overflow:"hidden",children:Fe("text",{fg:t.primary,wrapMode:"none",children:r.key},void 0,!1,void 0,this)},void 0,!1,void 0,this),Fe("box",{flexGrow:1,overflow:"hidden",children:Fe("text",{fg:t.text,wrapMode:"none",children:r.label},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}var LI=78,DI=56,II=2,uf=2,sf=1,pf=8,MI=2,OI=2,$I=6,FI=4,af,cf,lf,df;var ff=I(()=>{Mt();ke();Er();Rr();af=[{key:"tab",label:"next tab"},{key:"1-4",label:"dashboard services logs proxy"},{key:"s/l/a/p/d/c/u",label:"letter jump"},{key:"/",label:"command overlay \xB7 other screens"},{key:"j/k",label:"move selection"},{key:"esc",label:"back / close overlay"},{key:"esc \xD72",label:"quit"},{key:"q \xD72",label:"quit"}],cf=[{key:"enter",label:"start profile or open"},{key:"space",label:"multi-select"},{key:"n",label:"start"},{key:"x",label:"stop"},{key:"r",label:"refresh"},{key:"R",label:"restart (c cascade if dependents)"}];lf=[{key:It("c"),label:"copy highlighted selection"},{key:"/",label:"command overlay"},{key:It("p"),label:"same overlay"},{key:It("x"),label:"leader chord"},{key:"/settings",label:"preferences \xB7 MCP page"},{key:"/themes",label:"preview themes"},{key:"/diff",label:"config sources"},{key:"/mcp",label:"agent MCP server"}],df=[{key:It("="),label:"larger"},{key:It("-"),label:"smaller"},{key:"?",label:"this overlay"}]});function hi(e){e.setQuery(""),e.setSlashIndex(0),e.setSlashPicker("commands"),e.setOverlay("slash")}function xw(e,t){let{overlay:r,tui:n,height:o,closeOverlay:i,confirmKind:s,onQuit:a,confirmAction:c,planBusy:l,logDetailsScrollRef:d,scrollTextScrollRef:u,routeDetailsScrollRef:f,planScrollRef:p,helpScrollRef:m,revertThemePreview:h,setPaletteIndex:v,setThemeName:b,paletteIndex:g,applyTheme:_,leaderTimer:R,setOverlay:S,runCommand:y,setConfigEditError:x,saveConfigBuffer:w,setSlashIndex:k,filtered:D,setQuery:O,slashIndex:F,submitSlash:Z,advanceWizard:W}=e,C=(t.name??"").toLowerCase();if(r==="none")return!1;if(r==="setup-wizard"){if(C==="escape")return i(),!0;if(C==="return")return W?.(),!0;return Vp(r)}if(r==="confirm"){if(C==="escape")return i(),!0;if(s==="quit"&&C==="d")return a(!0),!0;if(s==="quit"&&C==="k")return e.onDown(!0),!0;if(s==="restart-cascade"&&C==="c")return c("cascade"),!0;if(C==="return")c();return!0}if(r==="log-details"||r==="scroll-text"){if(cn(t,n))return hi(e),!0;if(C==="escape")return i(),!0;if(r==="log-details"&&C==="return"){let B=e.logDetail?.request_id?.trim()??"";if(B!=="")e.setLogSearch(B),e.setScreen("logs"),e.closeOverlay(),e.setStatus(`tracing ${B}`);return!0}let j=r==="log-details"?d.current:u.current;if(C==="down"||C==="j")return rt(j,n.scroll_speed),!0;if(C==="up"||C==="k")return rt(j,-n.scroll_speed),!0;if(Ds(t))return rt(j,di(o)),!0;if(Is(t))return rt(j,-di(o)),!0;return!0}if(r==="route-details"){if(cn(t,n))return hi(e),!0;if(C==="escape"||C==="return")return i(),!0;if(C==="down"||C==="j")return rt(f.current,n.scroll_speed),!0;if(C==="up"||C==="k")return rt(f.current,-n.scroll_speed),!0;return!0}if(r==="plan"){if(cn(t,n))return hi(e),!0;if(C==="escape"||C==="return"&&!l)return i(),!0;if(C==="down"||C==="j")return rt(p.current,n.scroll_speed),!0;if(C==="up"||C==="k")return rt(p.current,-n.scroll_speed),!0;return!0}if(r==="help"){if(cn(t,n))return hi(e),!0;if(C==="escape")return i(),!0;if(C==="down"||C==="j")return rt(m.current,1),!0;if(C==="up"||C==="k")return rt(m.current,-1),!0;if(C==="pagedown"||Ds(t))return rt(m.current,pf),!0;if(C==="pageup"||Is(t))return rt(m.current,-pf),!0;return!0}if(r==="themes"){if(cn(t,n))return h(),hi(e),!0;if(C==="escape")return h(),i(),!0;if(C==="down"||C==="j")return v((j)=>{let B=Math.min(j+1,St.length-1),z=St[B];if(z)b(z);return B}),!0;if(C==="up"||C==="k")return v((j)=>{let B=Math.max(0,j-1),z=St[B];if(z)b(z);return B}),!0;if(C==="return"){let j=St[g%St.length];if(j)_(j)}return!0}if(r==="leader"){if(cn(t,n)){if(R.current)clearTimeout(R.current);return hi(e),!0}if(R.current)clearTimeout(R.current);S("none");let j=gx(C),B=j?Wr(j):void 0;if(B)y(B,[]);return!0}if(r==="config-edit"){if(C==="escape")return x(""),i(),!0;if(Or(t)&&C==="s")return w(),!0;return!0}if(Vp(r)){if(C==="escape")return i(),!0;if(r==="slash"&&C==="down")return k((j)=>Math.min(Math.max(D.length-1,0),j+1)),!0;if(r==="slash"&&C==="up")return k((j)=>Math.max(0,j-1)),!0;if(r==="slash"&&C==="tab"){if(t.shift)return k((B)=>Math.max(0,B-1)),!0;let j=ui(D,F);if(j)O(Rx(j));return!0}if(r==="slash"&&C==="return")return Z(),!0;return!0}return!0}var ww=I(()=>{vr();wo();hl();Rr();ke();ff();tt()});var yl=I(()=>{Zu()});function Sw(e,t){if(t.length===0)return[];let r=new Set(t);return co(e,t).filter((n)=>!r.has(n))}function kw(e,t,r){let n=r!==""&&e.profiles[r]?r:"";if(t.length>0)return{services:t,profile:n};if(n!=="")return{services:[...e.profiles[r]?.services??[]],profile:n};return{services:Object.keys(e.services),profile:""}}function bl(e){return e.waves.flat().join(" \u2192 ")}function Cw(e){let t=bl(e);if(t==="")return"Started selected services";return`Started ${t}`}function Pw(e){let t=bl(e);if(t==="")return"Stopped running services";return`Stopped ${t}`}function _w(e,t,r,n="start"){let o=r?.services[e],i=tr(o);if(o?.last_error&&n!=="stop")return o.last_error;if(n==="stop"){if(i==="STOPPED")return"stopped";if(i==="STOPPING")return"stopping now";if(i==="FAILED")return"already failed";return"queued"}if(i==="HEALTHY")return"ready";if(i==="STARTING")return"starting now";if(i==="RUNNING")return"up, waiting for health";if(i==="UNHEALTHY")return"unhealthy \u2014 retrying health check";if(i==="STOPPING")return"stopping first";if(i==="FAILED")return"failed \u2014 later waves will not start";let a=(t.steps.find((c)=>c.name===e)?.dependencies??[]).filter((c)=>{let l=tr(r?.services[zt(c)]);return vn(c)==="service_healthy"?l!=="HEALTHY":l==="STOPPED"||l==="FAILED"}).map(zt);if(a.length>0)return`waits for ${a.join(", ")}`;return"queued"}function Fs(e,t,r="start"){if(e.length===0)return"completed";let n=e.map((i)=>t?.services[i]);if(n.some((i)=>i?.state==="FAILED"))return"failed";if(r==="stop"){if(n.every((i)=>!i||i.state==="STOPPED"||i.state==="UNKNOWN"))return"completed";if(n.some((i)=>i?.state==="STOPPING"))return"active";return"queued"}let o=(i)=>Boolean(i&&(i.state==="HEALTHY"||i.health==="HEALTHY"||i.state==="RUNNING"&&i.health!=="UNHEALTHY"));if(n.every(o))return"completed";if(n.some((i)=>i?.state==="UNHEALTHY"||i?.health==="UNHEALTHY"))return"unhealthy";if(n.some((i)=>i?.state==="STARTING"||i?.state==="RUNNING"||i?.state==="RESTARTING"))return"active";return"queued"}function Rw(e,t,r="start"){let o=e.waves.flat().length,i=0,s=0,a=0,c=0;for(let m=0;m<e.waves.length;m++){let h=e.waves[m]??[],v=Fs(h,t,r);if(v==="active"||v==="unhealthy"||v==="failed")c=m;else if(v==="completed"&&c===m&&m<e.waves.length-1)c=m+1;for(let b of h){let g=t?.services[b];if(r==="stop"?!g||g.state==="STOPPED"||g.state==="UNKNOWN":Boolean(g&&(g.state==="HEALTHY"||g.health==="HEALTHY"||g.state==="RUNNING"&&g.health!=="UNHEALTHY")))i++;else if(g?.state==="FAILED")s++;else if(g?.state==="STARTING"||g?.state==="RUNNING"||g?.state==="UNHEALTHY"||g?.health==="UNHEALTHY"||g?.state==="STOPPING"||g?.state==="RESTARTING")a++}}let l=o>0?Math.round(i/o*100):100,d=16,u=Math.round(l/100*d),f="\u2588".repeat(u)+"\u2591".repeat(Math.max(0,d-u)),p=i===o&&o>0;return{total:o,ready:i,active:a,failed:s,percent:l,progressBar:f,currentWaveIndex:c,totalWaves:e.waves.length,isComplete:p}}function Ew(e,t,r){let n=r==="completed"?"\u2713 Completed":r==="failed"?"\u2717 Failed":r==="unhealthy"?"\u26A0 Unhealthy":r==="active"?"\u23F3 In Progress":"\u25CB Queued";return`${e==="stop"?t===0?"Wave 1 (Stop First)":`Wave ${t+1}`:t===0?"Wave 1 (Start First)":`Wave ${t+1}`} \xB7 ${n}`}function Tw(e,t,r,n){let o=n?` \xB7 Profile ${n}`:"";if(r)return`${e==="stop"?"Shutdown":"Startup"} Failed (${r})`;if(t)return`${e==="stop"?"Stopping Services":e==="restart"?"Restarting Pipeline":"Starting Pipeline"}${o}`;return`${e==="stop"?"Shutdown Complete":"Startup Complete"}${o}`}function Aw(e,t){if(e)return{primary:"Working\u2026 esc hide this panel (keeps running in background)",secondary:"Services are transitioning. You can dismiss anytime without stopping them."};if(t)return{primary:"enter or esc back to dashboard",secondary:"Execution stopped. Check logs or open Doctor to resolve errors."};return{primary:"enter or esc back to dashboard",secondary:"All waves finished. Click here or press enter to continue."}}var xl=I(()=>{ht();At();Lt()});function Lw(e,t){let r=(t.name??"").toLowerCase();if(e.screen==="mcp"&&e.listCursor===1&&r.length===1&&r>="0"&&r<="9")return e.setMcpPortDraft((n)=>Ty(n,r)),!0;return!1}function Dw(e,t){let r=(t.name??"").toLowerCase(),{screen:n,overlay:o,tui:i,cfg:s,controller:a,profile:c,listCursor:l,names:d,listCount:u,height:f,checked:p,detailName:m,bootErrorMissing:h,bootError:v,logSearchFocused:b,logSources:g,logService:_,logShowTimestamps:R,logShowMeta:S,logWrap:y,showSystemLogs:x,logPinned:w,dashboardLogCursor:k,applyLogCursor:D,applyDashboardLogCursor:O,jumpToLatestLogs:F,toggleSystemLogs:Z,clearLogs:W,setLogService:C,setLogShowTimestamps:j,setLogShowMeta:B,setPaused:z,setErrorOnly:de,setLogWrap:Ee,beginStart:Ne,beginStop:ne,beginRestart:H,applyMcpPortDraft:ae,toggleMcp:Q,toggleMcpTool:nt,copyFocusedMcpSnippet:ft,persistMcpPort:ie,restartMcpOnPort:Le,setMcpPortDraft:Me,settingRows:Ke,persistPrefs:gt,cycleSetting:Xe,toggleMouse:Ct,setDoctorTick:ge,refreshAuth:vt,configScrollRef:at,detailScrollRef:yt,handleEnter:rr,setScreen:Qe,setChecked:Xt,setStatus:qe,setSelected:mt,setProfile:br,refresh:Kt,toggleChecked:xr,createStarterConfig:nr,startWizard:qt,setConfirmKind:or,setConfirmDetail:ir,setOverlay:sr,openConfigBuffer:Vt,runCommand:ce}=e,K=(n==="logs"||n==="dashboard")&&!b;if(K&&(r==="left"||r==="["||n==="logs"&&r==="h")){C(Dp(g,_,-1));return}if(K&&(r==="right"||r==="]"||n==="logs"&&r==="l")){C(Dp(g,_,1));return}if((n==="dashboard"||n==="setup")&&r==="o"){Qe("profiles");return}if((n==="dashboard"||n==="services")&&(r==="*"||t.shift&&r==="8")){Xt([...d]),qe(`Selected ${d.length} services`);return}if((n==="dashboard"||n==="services")&&(r==="-"||r==="_"||r==="minus")){Xt([]),qe("Selection cleared");return}if((n==="dashboard"||n==="services")&&r==="n"){Ne(Bc(p,d[l]??""),c);return}if((n==="dashboard"||n==="services")&&r==="x"){ne(Bc(p,d[l]??""));return}if((n==="dashboard"||n==="services"||n==="detail")&&aw(t)){let A=Bc(p,d[l]??m);if(s&&Sw(s,A).length>0){or("restart-cascade"),ir({services:A}),sr("confirm");return}H(A,c);return}if((n==="dashboard"||n==="services")&&r==="r"){Kt(),qe("Refreshed");return}if(n==="auth"&&r==="r"){vt();return}if(n==="logs"&&r==="t"){let A=!R;j(A),gt({log_timestamps:A},A?"timestamps on":"timestamps off");return}if(n==="logs"&&r==="m"){let A=!S;B(A),gt({log_metadata:A},A?"metadata on":"metadata off");return}if(r==="down"||r==="j"){if(n==="mcp"&&l===1)ae();if(n==="config"){if(u>0)mt((A)=>Math.min(Math.max(u-1,0),A+1));rt(at.current,i.scroll_speed);return}if(n==="detail"){rt(yt.current,i.scroll_speed);return}if(n==="logs"){D(l+1);return}if(n==="dashboard"&&k>=0){O(k+1);return}mt((A)=>Math.min(Math.max(u-1,0),A+1));return}if(r==="up"||r==="k"){if(n==="mcp"&&l===1)ae();if(n==="config"){if(u>0)mt((A)=>Math.max(0,Math.min(A,Math.max(u-1,0))-1));rt(at.current,-i.scroll_speed);return}if(n==="detail"){rt(yt.current,-i.scroll_speed);return}if(n==="logs"){D(l-1);return}if(n==="dashboard"&&k>=0){O(k-1);return}mt((A)=>Math.max(0,Math.min(A,Math.max(u-1,0))-1));return}if(Ds(t)){let A=di(f);if(n==="config"){rt(at.current,A);return}if(n==="detail"){rt(yt.current,A);return}if(n==="logs"){D(l+A);return}if(n==="dashboard"&&(w||k>=0)){O(Math.max(0,k)+A);return}mt((ve)=>Math.min(Math.max(u-1,0),ve+A));return}if(Is(t)){let A=di(f);if(n==="config"){rt(at.current,-A);return}if(n==="detail"){rt(yt.current,-A);return}if(n==="logs"){D(l-A);return}if(n==="dashboard"&&(w||k>=0)){O(Math.max(0,k)-A);return}mt((ve)=>Math.max(0,ve-A));return}if(n==="proxy"&&r==="n"){a?.proxyStart().then(async()=>{qe("Proxy started"),await Kt()});return}if(n==="proxy"&&r==="x"){a?.proxyStop().then(async()=>{qe("Proxy stopped"),await Kt()});return}if(n==="detail"&&m!==""){if(r==="n"){Ne([m],c);return}if(r==="x"){ne([m]);return}if(r==="o"){Qe("config");return}if(r==="l"){C(m),Qe("logs");return}}if(n==="mcp"&&l===1&&(r==="left"||r==="h")){let A=Rn(ae()-1);ie(A),Le(A);return}if(n==="mcp"&&l===1&&(r==="right"||r==="l")){let A=Rn(ae()+1);ie(A),Le(A);return}if(n==="mcp"&&l===1&&(r==="backspace"||r==="delete")){Me((A)=>Ay(A));return}if(n==="settings"&&(r==="left"||r==="h")){Xe(-1,l);return}if(n==="settings"&&(r==="right"||r==="l")){Xe(1,l);return}if(r==="space"){if(n==="profiles"){let J=Object.keys(s?.profiles??{}).sort()[l];if(J)br(J),qe(`Profile ${J}`);return}if(n==="mcp"){if(l===0){Q();return}let ve=Os(l);if(ve){nt(ve);return}ft(l);return}if(n==="logs")return;if(n==="settings"){if(Ln(Ke,l)?.id==="mouse")Ct();return}let A=d[l];if(A)xr(A);return}if(r==="return"){if(n==="setup"&&!a){if(!h){qe(v||"Existing configuration is invalid \u2014 fix it and restart devctl.");return}qt();return}if(n==="setup"&&a){Ne([],c);return}rr();return}if(Mn(t,i,"services","s")&&o==="none"&&n!=="logs"){Qe("services");return}if(Mn(t,i,"logs","l")&&o==="none"&&n!=="detail"&&n!=="logs"&&n!=="settings"){Qe("logs");return}if(Mn(t,i,"auth","a")&&o==="none"){Qe("auth");return}if(Mn(t,i,"proxy","p")&&o==="none"&&n!=="logs"){Qe("proxy");return}if(Mn(t,i,"doctor","d")&&o==="none"&&n!=="doctor"){Qe("doctor");return}if(Mn(t,i,"config","c")&&o==="none"){Qe("config");return}if(Mn(t,i,"setup","u")&&o==="none"){Qe("setup");return}if(n==="config"&&r==="e"){ce(Wr("/edit")??{name:"edit",aliases:[],desc:"",leader:"",group:"ui"},[]);return}if(n==="config"&&r==="v"){Vt();return}if(n==="doctor"&&r==="r"){ge((A)=>A+1),qe("Re-running doctor");return}if(n==="logs"&&r==="p"){z((A)=>!A);return}if((n==="logs"||n==="dashboard")&&r==="g"){F();return}if((n==="logs"||n==="dashboard")&&r==="w"){let A=Qc(y);Ee(A),qe(`Log ${el(A)}`);return}if((n==="logs"||n==="dashboard")&&r==="e"){de((A)=>!A);return}if((n==="logs"||n==="dashboard")&&r==="i"){Z(),qe(x?"Hiding internal auth/mcp/devctl/proxy logs":"Showing internal logs");return}if((n==="logs"||n==="dashboard")&&cw(t))W()}var Iw=I(()=>{yl();vr();gr();xl();Lt();hl();ke();$s();Er()});import{useKeyboard as WI}from"@opentui/react";import{useCallback as mf}from"react";function Mw({tui:e,controller:t,cfg:r,snap:n,ui:o,logView:i,lifecycleActions:s,mcp:a,preferences:c,diagnostics:l,refs:d}){let{screen:u,onQuit:f,closeOverlay:p,confirmKind:m,confirmDetail:h,portTarget:v,profile:b,listCursor:g,names:_,runCommand:R,copySelection:S,setOverlay:y,setConfirmKind:x,setConfirmDetail:w,setPortTarget:k,setLogDetail:D,setProfile:O,setStatus:F,setSlashIndex:Z,setQuery:W,setSlashPicker:C,setScreen:j,freePort:B,openDetail:z}=o,{logSlice:de,logSliceB:Ee,logSearchFocused:Ne,logsFullscreen:ne,setLogSearchFocused:H,setLogsFullscreen:ae,splitLogs:Q,splitFocus:nt,toggleSplitLogs:ft,cycleSplitFocus:ie,setActiveLogService:Le,logService:Me,logServiceB:Ke}=i,{beginStart:gt,beginRestart:Xe,planBusy:Ct}=s,{applyMcpPortDraft:ge,toggleMcp:vt,toggleMcpTool:at,copyFocusedMcpSnippet:yt}=a,{settingRows:rr,activateSetting:Qe,applyFont:Xt,applyReset:qe,fontSize:mt,revertThemePreview:br,setThemeName:Kt,leaderMs:xr}=c,{doctor:nr}=l,{interruptArmedAt:qt,leaderTimer:or}=d,ir=mf(()=>{if(r?.shutdown.stop_services_on_exit===void 0){x("quit"),y("confirm");return}f(r.shutdown.stop_services_on_exit===!1)},[r,f,x,y]),sr=mf((ce)=>{if(m==="quit"){f(!1);return}if(m==="reload"){p();let K=n?.restart_required??[];if(K.length>0)Xe(K,b);return}if(m==="restart-cascade"){let K=h?.services??[];p(),Xe(K,b,ce==="cascade");return}if(m==="reset-prefs"){p(),qe();return}if(m==="free-port"){let K=v;if(p(),!K)return;B(K).then(()=>{F(`Stopped ${K.command} (pid ${K.pid}) on port ${K.port}`),l.setDoctorTick((A)=>A+1)}).catch((A)=>{F(L(A))});return}p(),gt([],b)},[qe,Xe,gt,p,h,m,l,B,f,v,b,F,n]),Vt=mf(()=>{if(u==="logs"){let K=(Q&&nt===1?Ee:de)[g];if(K)D(K),y("log-details");return}if(u==="profiles"){let A=Object.keys(r?.profiles??{}).sort()[g];if(A)O(A),x("start-profile"),y("confirm");return}if(u==="doctor"){let K=nr?.checks[g];if(K?.action?.kind==="free-port"){let A=K.action.holder;k(A),w({port:A.port,pid:A.pid,process:A.command}),x("free-port"),y("confirm")}return}if(u==="mcp"){if(g===0){vt();return}if(g===1){ge();return}let K=Os(g);if(K){at(K);return}yt(g);return}if(u==="settings"){let K=Ln(rr,g);if(K)Qe(K);return}if(u==="config"){let K=Object.keys(r?.tasks??{}).sort()[g],A=Wr("run");if(K&&A)R(A,[K]);return}if(u==="dashboard"&&jc(n)){gt([],b);return}let ce=_[g];if(ce&&(u==="dashboard"||u==="services"))z(ce)},[Qe,ge,gt,r,yt,nr,g,de,Ee,_,z,b,R,u,w,x,D,y,k,O,rr,n,nt,Q,vt,at]);WI((ce)=>{let K=(ce.name??"").toLowerCase();if(lw(ce,e)){S();return}if(Or(ce)&&(K==="="||K==="+"||K==="plus")){Xt(xs(mt,1));return}if(Or(ce)&&(K==="-"||K==="_"||K==="minus")){Xt(xs(mt,-1));return}if(Or(ce)&&K==="0"){Xt(Mc().font_size);return}if(u==="logs"&&!Ne&&(ce.sequence==="\\"||K==="\\")){ft(),F(Q?"Single log pane":"Split log panes");return}if(u==="logs"&&!Ne&&(ce.sequence==="|"||K==="|")){ie();return}if(xw({...o,tui:e,confirmAction:sr,planBusy:Ct,leaderTimer:or,logDetailsScrollRef:d.logDetailsScrollRef,scrollTextScrollRef:d.scrollTextScrollRef,routeDetailsScrollRef:d.routeDetailsScrollRef,planScrollRef:d.planScrollRef,helpScrollRef:d.helpScrollRef,revertThemePreview:br,setThemeName:Kt},ce))return;if(Ne){if(K==="escape")H(!1);return}if(rw(ce,e)){y("leader"),or.current=setTimeout(()=>y("none"),xr);return}if(nw(ce,e)||cn(ce,e)){W(""),Z(0),C("commands"),y("slash");return}if(ow(ce,e)){y("help");return}if(iw(ce,e)||u==="logs"&&K==="f"&&!ce.ctrl&&!ce.meta){j("logs"),H(!0);return}if(K==="escape"||sw(ce)){if(K==="escape"){if(ne){ae(!1);return}if(u==="detail"){j("services");return}if(u==="setup"&&t){j("dashboard");return}}let J=Date.now();if(dw(J,qt.current)){qt.current=0,ir();return}qt.current=J,F(K==="q"?"q again to quit":"Esc again to quit");return}if(K==="z"&&(u==="logs"||u==="dashboard")&&!ce.ctrl&&!ce.meta){j("logs"),ae((J)=>u==="logs"?!J:!0);return}if(K==="tab"){j(ce.shift?Cx(u):kx(u));return}let A={...o,tui:e,controller:t,cfg:r,snap:n,...i,...s,...a,...c,logService:Q&&nt===1?Ke:Me,setLogService:Le,setDoctorTick:l.setDoctorTick,refreshAuth:l.refreshAuth,configScrollRef:d.configScrollRef,detailScrollRef:d.detailScrollRef,handleEnter:Vt};if(Lw(A,ce))return;let ve=tx(K);if(ve){j(ve);return}Dw(A,ce)})}var Ow=I(()=>{te();wo();In();Lt();hl();$s();Er();Rr();ww();Iw()});import{parse as GI,stringify as KI}from"yaml";function wl(e,t="imported"){let r=GI(e);if(!r||typeof r!=="object"||Array.isArray(r))throw Error("compose file is not a mapping");let n=r,o=gi(n.services)?n.services:{},i=[];if(n.networks!==void 0)i.push({service:"(root)",field:"networks",reason:$w.networks??"not modeled"});let s={},a=[];for(let[l,d]of Object.entries(o)){if(!gi(d))continue;let u=qI(l,d,i);if(s[l]=u.body,!u.container)a.push(l)}return{yaml:KI({version:1,project:{name:t},services:s,profiles:{default:{services:a}}}),dropped:i,services:Object.keys(s),profileServices:a}}function Sl(e){let t=[e.yaml.trimEnd(),"","Dropped fields:"];if(e.dropped.length===0)t.push("(none)");else{t.push("service\tfield\treason");for(let r of e.dropped)t.push(`${r.service} ${r.field} ${r.reason}`)}if(e.profileServices.length<e.services.length)t.push(""),t.push("Container services were omitted from profile default so first start does not require Docker.");return`${t.join(`
|
|
2216
|
+
`}function OI(e){return`codex mcp add --url ${e}`}function js(e,t){return[{kind:"claude",title:"Claude",path:".mcp.json",language:"json",text:tm(e,t),hint:"claude mcp add-json"},{kind:"cursor",title:"Cursor",path:".cursor/mcp.json",language:"json",text:rm(e,t)},{kind:"kilo",title:"Kilo Code",path:"kilo.jsonc",language:"json",text:nm(e,t)},{kind:"codex",title:"Codex",path:"~/.codex/config.toml",language:"toml",text:om(e,t),hint:OI(e)}]}function vw(e,t){let r=[`URL ${e}`,""];for(let n of js(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 cm(){return fp.flatMap((e)=>Lc.filter((t)=>t.category===e))}function Sl(){return wl+cm().length}function xw(){return Sl()+bw}function Bs(e){if(e<wl||e>=Sl())return;return cm()[e-wl]}function lm(e){let t=e-Sl();return t>=0&&t<bw?t:void 0}function ww(e){let{palette:t,snap:r,port:n,portDraft:o="",selected:i,onPick:s,onToggle:a,onCopy:c,onToggleTool:l}=e,d=be(),u=r?.mcp?.running===!0,m=r?.mcp?.port??n,p=r?.mcp?.address??vi(m),f=r?.mcp?.token??"",h=js(p,f),v=r?.mcp?.disabled_tools??[],b=cm(),g=b.filter((S)=>!Dn(S.name,v)).length,C=h[lm(i)??-1],E=C??h[0];return q(kt,{palette:t,title:"mcp settings",children:[q(pt,{palette:t,items:[{text:u?"RUNNING":"STOPPED",tone:u?"success":"idle"},{text:`127.0.0.1:${m}`,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(im,{palette:t,text:"Server"},void 0,!1,void 0,this),q(yw,{palette:t,active:i===sm,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(sm),onActivate:a},void 0,!1,void 0,this),q(yw,{palette:t,active:i===am,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(am)},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(im,{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),fp.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:$I[S]??S},void 0,!1,void 0,this)},void 0,!1,void 0,this),y.map((x)=>{let w=wl+b.indexOf(x);return q(jI,{palette:t,tool:x,enabled:Dn(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(im,{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=Sl()+y;return q(HI,{palette:t,snippet:S,active:i===x,rowH:d.rowH,onPick:()=>s(x),onCopy:()=>c(S)},S.kind,!1,void 0,this)}),q(BI,{palette:t,snippet:E,selected:Boolean(C),brand:E?Fc(E.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:NI(i,u)},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function NI(e,t){if(e===sm)return[{key:"space",label:t?"stop server":"start server"},{key:"j/k",label:"move"}];if(e===am)return[{key:"\u2190\u2192",label:"change port"},{key:"0-9",label:"type port"},{key:"enter",label:"apply port"},{key:"j/k",label:"move"}];if(Bs(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 im(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 yw(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:dm,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:FI,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 HI(e){let{palette:t,snippet:r,active:n,rowH:o,onPick:i,onCopy:s}=e,a=Fc(r.kind,t),c=bb(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:dm,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 jI(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:dm+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 BI(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 sm=0,am=1,wl=2,bw=4,$I,dm=14,FI=14;var Us=I(()=>{Mt();Pe();tt();bs();$I={inspect:"Inspect",logs:"Logs",diagnostics:"Diagnostics",control:"Control",setup:"Setup"}});import{jsxDEV as Fe,Fragment as kw}from"@opentui/react/jsx-dev-runtime";function YI(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:Li(e),label:"copy selection"},{key:"/export",label:"write ~/.devctl/exports"},{key:"/exports",label:"open export folder"}]}function Nn(e){return e+GI}function Cw(e){let t=Nn(pm.length),r=Nn(mm.length),n=Nn(e.logCount),o=Nn(fm.length),i=Nn(hm.length),s=gm+e.pad*2;if(e.stacked)return s+t+r+n+o+i+um+(VI-1)*e.gap;let a=Math.max(t,r),c=Math.max(o,i);return s+a+n+c+um+(zI-1)*e.gap}function JI(e){return Cw(e)-gm-e.pad*2}function XI(e){return e-gm*2<WI}function Pw(e){let{palette:t,termW:r,termH:n,copyKey:o=en(),scrollRef:i}=e,s=vo(be()),a=XI(r),c=s?0:1,l=s?0:1,d=YI(o),u={stacked:a,logCount:d.length,pad:c,gap:l},m=Cw(u),p=JI(u);return Fe(mt,{palette:t,title:"help",bottomTitle:`${tr()} \xB7 j/k scroll \xB7 esc close`,termW:r,termH:n,preferW:UI,preferH:m,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(kw,{children:[Fe(Fn,{palette:t,title:"navigation",bindings:pm},void 0,!1,void 0,this),Fe(Fn,{palette:t,title:"services",bindings:mm},void 0,!1,void 0,this),Fe(Fn,{palette:t,title:"logs",bindings:d},void 0,!1,void 0,this),Fe(Fn,{palette:t,title:"commands",bindings:fm},void 0,!1,void 0,this),Fe(Fn,{palette:t,title:"display",bindings:hm},void 0,!1,void 0,this)]},void 0,!0,void 0,this):Fe(kw,{children:[Fe(Sw,{palette:t,left:{title:"navigation",bindings:pm},right:{title:"services",bindings:mm}},void 0,!1,void 0,this),Fe(Fn,{palette:t,title:"logs",bindings:d},void 0,!1,void 0,this),Fe(Sw,{palette:t,left:{title:"commands",bindings:fm},right:{title:"display",bindings:hm}},void 0,!1,void 0,this)]},void 0,!0,void 0,this),Fe("box",{height:um,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 Sw(e){let{palette:t,left:r,right:n}=e,o=Math.max(Nn(r.bindings.length),Nn(n.bindings.length));return Fe("box",{height:o,flexDirection:"row",flexShrink:0,overflow:"hidden",gap:qI,children:[Fe(Fn,{palette:t,title:r.title,bindings:r.bindings,height:o,stretch:!0},void 0,!1,void 0,this),Fe(Fn,{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 Fn(e){let{palette:t,title:r,bindings:n,stretch:o=!1}=e,i=e.height??Nn(n.length),s=QI(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(ZI,{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 QI(e){return e.reduce((r,n)=>Math.max(r,n.key.length),0)+KI}function ZI(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 UI=78,WI=56,GI=2,gm=2,um=1,vm=8,KI=2,qI=2,VI=6,zI=4,pm,mm,fm,hm;var ym=I(()=>{Mt();Pe();Dr();Lr();pm=[{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"}],mm=[{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)"}];fm=[{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"}],hm=[{key:It("="),label:"larger"},{key:It("-"),label:"smaller"},{key:"?",label:"this overlay"}]});function yi(e){e.setQuery(""),e.setSlashIndex(0),e.setSlashPicker("commands"),e.setOverlay("slash")}function Rw(e,t){let{overlay:r,tui:n,height:o,closeOverlay:i,confirmKind:s,onQuit:a,confirmAction:c,planBusy:l,logDetailsScrollRef:d,scrollTextScrollRef:u,routeDetailsScrollRef:m,planScrollRef:p,helpScrollRef:f,revertThemePreview:h,setPaletteIndex:v,setThemeName:b,paletteIndex:g,applyTheme:C,leaderTimer:E,setOverlay:S,runCommand:y,setConfigEditError:x,saveConfigBuffer:w,setSlashIndex:k,filtered:D,setQuery:O,slashIndex:F,submitSlash:Z,advanceWizard:G}=e,R=(t.name??"").toLowerCase();if(r==="none")return!1;if(r==="setup-wizard"){if(R==="escape")return i(),!0;if(R==="return")return G?.(),!0;return Zp(r)}if(r==="confirm"){if(R==="escape")return i(),!0;if(s==="quit"&&R==="d")return a(!0),!0;if(s==="quit"&&R==="k")return e.onDown(!0),!0;if(s==="restart-cascade"&&R==="c")return c("cascade"),!0;if(R==="return")c();return!0}if(r==="log-details"||r==="scroll-text"){if(dn(t,n))return yi(e),!0;if(R==="escape")return i(),!0;if(r==="log-details"&&R==="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(R==="down"||R==="j")return rt(j,n.scroll_speed),!0;if(R==="up"||R==="k")return rt(j,-n.scroll_speed),!0;if(Ns(t))return rt(j,mi(o)),!0;if(Hs(t))return rt(j,-mi(o)),!0;return!0}if(r==="route-details"){if(dn(t,n))return yi(e),!0;if(R==="escape"||R==="return")return i(),!0;if(R==="down"||R==="j")return rt(m.current,n.scroll_speed),!0;if(R==="up"||R==="k")return rt(m.current,-n.scroll_speed),!0;return!0}if(r==="plan"){if(dn(t,n))return yi(e),!0;if(R==="escape"||R==="return"&&!l)return i(),!0;if(R==="down"||R==="j")return rt(p.current,n.scroll_speed),!0;if(R==="up"||R==="k")return rt(p.current,-n.scroll_speed),!0;return!0}if(r==="help"){if(dn(t,n))return yi(e),!0;if(R==="escape")return i(),!0;if(R==="down"||R==="j")return rt(f.current,1),!0;if(R==="up"||R==="k")return rt(f.current,-1),!0;if(R==="pagedown"||Ns(t))return rt(f.current,vm),!0;if(R==="pageup"||Hs(t))return rt(f.current,-vm),!0;return!0}if(r==="themes"){if(dn(t,n))return h(),yi(e),!0;if(R==="escape")return h(),i(),!0;if(R==="down"||R==="j")return v((j)=>{let B=Math.min(j+1,St.length-1),z=St[B];if(z)b(z);return B}),!0;if(R==="up"||R==="k")return v((j)=>{let B=Math.max(0,j-1),z=St[B];if(z)b(z);return B}),!0;if(R==="return"){let j=St[g%St.length];if(j)C(j)}return!0}if(r==="leader"){if(dn(t,n)){if(E.current)clearTimeout(E.current);return yi(e),!0}if(E.current)clearTimeout(E.current);S("none");let j=Sx(R),B=j?Gr(j):void 0;if(B)y(B,[]);return!0}if(r==="config-edit"){if(R==="escape")return x(""),i(),!0;if(Hr(t)&&R==="s")return w(),!0;return!0}if(Zp(r)){if(R==="escape")return i(),!0;if(r==="slash"&&R==="down")return k((j)=>Math.min(Math.max(D.length-1,0),j+1)),!0;if(r==="slash"&&R==="up")return k((j)=>Math.max(0,j-1)),!0;if(r==="slash"&&R==="tab"){if(t.shift)return k((B)=>Math.max(0,B-1)),!0;let j=fi(D,F);if(j)O(Ix(j));return!0}if(r==="slash"&&R==="return")return Z(),!0;return!0}return!0}var _w=I(()=>{yr();ko();xl();Lr();Pe();ym();tt()});var kl=I(()=>{np()});function Ew(e,t){if(t.length===0)return[];let r=new Set(t);return lo(e,t).filter((n)=>!r.has(n))}function Tw(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 Cl(e){return e.waves.flat().join(" \u2192 ")}function Aw(e){let t=Cl(e);if(t==="")return"Started selected services";return`Started ${t}`}function Lw(e){let t=Cl(e);if(t==="")return"Stopped running services";return`Stopped ${t}`}function Dw(e,t,r,n="start"){let o=r?.services[e],i=rr(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=rr(r?.services[Yt(c)]);return bn(c)==="service_healthy"?l!=="HEALTHY":l==="STOPPED"||l==="FAILED"}).map(Yt);if(a.length>0)return`waits for ${a.join(", ")}`;return"queued"}function Ws(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 Iw(e,t,r="start"){let o=e.waves.flat().length,i=0,s=0,a=0,c=0;for(let f=0;f<e.waves.length;f++){let h=e.waves[f]??[],v=Ws(h,t,r);if(v==="active"||v==="unhealthy"||v==="failed")c=f;else if(v==="completed"&&c===f&&f<e.waves.length-1)c=f+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),m="\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:m,currentWaveIndex:c,totalWaves:e.waves.length,isComplete:p}}function Mw(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 Ow(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 $w(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 Pl=I(()=>{dt();At();Lt()});function Fw(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)=>My(n,r)),!0;return!1}function Nw(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:m,checked:p,detailName:f,bootErrorMissing:h,bootError:v,logSearchFocused:b,logSources:g,logService:C,logShowTimestamps:E,logShowMeta:S,logWrap:y,showSystemLogs:x,logPinned:w,dashboardLogCursor:k,applyLogCursor:D,applyDashboardLogCursor:O,jumpToLatestLogs:F,toggleSystemLogs:Z,clearLogs:G,setLogService:R,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:nr,setScreen:Qe,setChecked:Qt,setStatus:qe,setSelected:ht,setProfile:xr,refresh:Kt,toggleChecked:wr,createStarterConfig:or,startWizard:qt,setConfirmKind:ir,setConfirmDetail:sr,setOverlay:ar,openConfigBuffer:Vt,runCommand:ce}=e,K=(n==="logs"||n==="dashboard")&&!b;if(K&&(r==="left"||r==="["||n==="logs"&&r==="h")){R(Np(g,C,-1));return}if(K&&(r==="right"||r==="]"||n==="logs"&&r==="l")){R(Np(g,C,1));return}if((n==="dashboard"||n==="setup")&&r==="o"){Qe("profiles");return}if((n==="dashboard"||n==="services")&&(r==="*"||t.shift&&r==="8")){Qt([...d]),qe(`Selected ${d.length} services`);return}if((n==="dashboard"||n==="services")&&(r==="-"||r==="_"||r==="minus")){Qt([]),qe("Selection cleared");return}if((n==="dashboard"||n==="services")&&r==="n"){Ne(qc(p,d[l]??""),c);return}if((n==="dashboard"||n==="services")&&r==="x"){ne(qc(p,d[l]??""));return}if((n==="dashboard"||n==="services"||n==="detail")&&mw(t)){let A=qc(p,d[l]??f);if(s&&Ew(s,A).length>0){ir("restart-cascade"),sr({services:A}),ar("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=!E;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)ht((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}ht((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)ht((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}ht((A)=>Math.max(0,Math.min(A,Math.max(u-1,0))-1));return}if(Ns(t)){let A=mi(m);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}ht((ve)=>Math.min(Math.max(u-1,0),ve+A));return}if(Hs(t)){let A=mi(m);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}ht((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"&&f!==""){if(r==="n"){Ne([f],c);return}if(r==="x"){ne([f]);return}if(r==="o"){Qe("config");return}if(r==="l"){R(f),Qe("logs");return}}if(n==="mcp"&&l===1&&(r==="left"||r==="h")){let A=Tn(ae()-1);ie(A),Le(A);return}if(n==="mcp"&&l===1&&(r==="right"||r==="l")){let A=Tn(ae()+1);ie(A),Le(A);return}if(n==="mcp"&&l===1&&(r==="backspace"||r==="delete")){Me((A)=>Oy(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)xr(J),qe(`Profile ${J}`);return}if(n==="mcp"){if(l===0){Q();return}let ve=Bs(l);if(ve){nt(ve);return}ft(l);return}if(n==="logs")return;if(n==="settings"){if(In(Ke,l)?.id==="mouse")Ct();return}let A=d[l];if(A)wr(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}nr();return}if($n(t,i,"services","s")&&o==="none"&&n!=="logs"){Qe("services");return}if($n(t,i,"logs","l")&&o==="none"&&n!=="detail"&&n!=="logs"&&n!=="settings"){Qe("logs");return}if($n(t,i,"auth","a")&&o==="none"){Qe("auth");return}if($n(t,i,"proxy","p")&&o==="none"&&n!=="logs"){Qe("proxy");return}if($n(t,i,"doctor","d")&&o==="none"&&n!=="doctor"){Qe("doctor");return}if($n(t,i,"config","c")&&o==="none"){Qe("config");return}if($n(t,i,"setup","u")&&o==="none"){Qe("setup");return}if(n==="config"&&r==="e"){ce(Gr("/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=nl(y);Ee(A),qe(`Log ${il(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")&&fw(t))G()}var Hw=I(()=>{kl();yr();vr();Pl();Lt();xl();Pe();Us();Dr()});import{useKeyboard as eM}from"@opentui/react";import{useCallback as bm}from"react";function jw({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:m,closeOverlay:p,confirmKind:f,confirmDetail:h,portTarget:v,profile:b,listCursor:g,names:C,runCommand:E,copySelection:S,setOverlay:y,setConfirmKind:x,setConfirmDetail:w,setPortTarget:k,setLogDetail:D,setProfile:O,setStatus:F,setSlashIndex:Z,setQuery:G,setSlashPicker:R,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:nr,activateSetting:Qe,applyFont:Qt,applyReset:qe,fontSize:ht,revertThemePreview:xr,setThemeName:Kt,leaderMs:wr}=c,{doctor:or}=l,{interruptArmedAt:qt,leaderTimer:ir}=d,sr=bm(()=>{if(r?.shutdown.stop_services_on_exit===void 0){x("quit"),y("confirm");return}m(r.shutdown.stop_services_on_exit===!1)},[r,m,x,y]),ar=bm((ce)=>{if(f==="quit"){m(!1);return}if(f==="reload"){p();let K=n?.restart_required??[];if(K.length>0)Xe(K,b);return}if(f==="restart-cascade"){let K=h?.services??[];p(),Xe(K,b,ce==="cascade");return}if(f==="reset-prefs"){p(),qe();return}if(f==="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,f,l,B,m,v,b,F,n]),Vt=bm(()=>{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=or?.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=Bs(g);if(K){at(K);return}yt(g);return}if(u==="settings"){let K=In(nr,g);if(K)Qe(K);return}if(u==="config"){let K=Object.keys(r?.tasks??{}).sort()[g],A=Gr("run");if(K&&A)E(A,[K]);return}if(u==="dashboard"&&Kc(n)){gt([],b);return}let ce=C[g];if(ce&&(u==="dashboard"||u==="services"))z(ce)},[Qe,ge,gt,r,yt,or,g,de,Ee,C,z,b,E,u,w,x,D,y,k,O,nr,n,nt,Q,vt,at]);eM((ce)=>{let K=(ce.name??"").toLowerCase();if(hw(ce,e)){S();return}if(Hr(ce)&&(K==="="||K==="+"||K==="plus")){Qt(Rs(ht,1));return}if(Hr(ce)&&(K==="-"||K==="_"||K==="minus")){Qt(Rs(ht,-1));return}if(Hr(ce)&&K==="0"){Qt(Hc().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(Rw({...o,tui:e,confirmAction:ar,planBusy:Ct,leaderTimer:ir,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(cw(ce,e)){y("leader"),ir.current=setTimeout(()=>y("none"),wr);return}if(lw(ce,e)||dn(ce,e)){G(""),Z(0),R("commands"),y("slash");return}if(dw(ce,e)){y("help");return}if(uw(ce,e)||u==="logs"&&K==="f"&&!ce.ctrl&&!ce.meta){j("logs"),H(!0);return}if(K==="escape"||pw(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(gw(J,qt.current)){qt.current=0,sr();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?Ax(u):Tx(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(Fw(A,ce))return;let ve=ax(K);if(ve){j(ve);return}Nw(A,ce)})}var Bw=I(()=>{te();ko();On();Lt();xl();Us();Dr();Lr();_w();Hw()});import{parse as tM,stringify as rM}from"yaml";function Rl(e,t="imported"){let r=tM(e);if(!r||typeof r!=="object"||Array.isArray(r))throw Error("compose file is not a mapping");let n=r,o=bi(n.services)?n.services:{},i=[];if(n.networks!==void 0)i.push({service:"(root)",field:"networks",reason:Uw.networks??"not modeled"});let s={},a=[];for(let[l,d]of Object.entries(o)){if(!bi(d))continue;let u=nM(l,d,i);if(s[l]=u.body,!u.container)a.push(l)}return{yaml:rM({version:1,project:{name:t},services:s,profiles:{default:{services:a}}}),dropped:i,services:Object.keys(s),profileServices:a}}function _l(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(`
|
|
2218
2218
|
`)}
|
|
2219
|
-
`}function
|
|
2219
|
+
`}function nM(e,t,r){for(let m of Object.keys(t)){let p=Uw[m];if(p)r.push({service:e,field:m,reason:p})}let n={},o=Ww(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=oM(t.depends_on);if(s.length>0)n.dependencies=s;let a=iM(t.environment);if(Object.keys(a).length>0)n.environment={defaults:a};let c=sM(t.ports);if(c.host.length>0)n.ports=c.host;let l=cM(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 Ww(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 oM(e){if(Array.isArray(e))return e.filter((t)=>typeof t==="string");if(bi(e))return Object.keys(e);return[]}function iM(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(bi(e))for(let[r,n]of Object.entries(e))t[r]=n===void 0||n===null?"":String(n);return t}function sM(e){let t=[],r={};if(!Array.isArray(e))return{host:t,container:r};let n=0;for(let o of e){let i=aM(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 aM(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(bi(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 cM(e){if(!bi(e)||e.disable===!0)return;let t=Ww(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 bi(e){return Boolean(e)&&typeof e==="object"&&!Array.isArray(e)}var Uw;var xm=I(()=>{Uw={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 Gw(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 Kw(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 qw(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 Vw(e){let t=lM(e);return[{label:"exit",value:t,tone:t==="ask on quit"?"warning":"text"},{label:"grace",value:`${e.shutdown.grace_seconds}s`},{label:"refresh",value:`${Ph(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 zw(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 Yw(e){return[{label:"listen",value:Sn(e.proxy.listen),tone:e.proxy.enabled?"text":"muted"},{label:"token",value:pM(e),tone:e.proxy.token_endpoint.enabled?"text":"muted"}]}function Jw(e){return Object.keys(e.services).sort().map((t)=>{let r=e.services[t]??xn();return{name:t,extends:r.extends||"\u2014",command:Vc(r),ports:Ms(r),identity:Yc(r),health:zc(r),depends:r.dependencies.map(Ia).join(", ")||"\u2014",restart:Jc(r)}})}function Xw(e){return e.proxy.routes.map((t)=>({name:t.name||t.match.host||"(unnamed)",match:mM(t),upstream:t.upstream.url||"\u2014",auth:t.auth.type||"none",identity:fM(t)}))}function Qw(e){return Object.keys(e.profiles).sort().map((t)=>{let r=e.profiles[t],n=Object.keys(r?.environment??{}).sort();return{name:t,services:r?.services.join(", ")||"\u2014",env:n.length>0?n.join(", "):"\u2014"}})}function Zw(e){return Object.keys(e.templates).sort().map((t)=>({name:t,summary:hM(e.templates[t])}))}function eS(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 tS(e,t){let r=Rn(e);if(r.length===0)return"No provenance recorded for this configuration.";let n=new hr(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?"":`
|
|
2220
2220
|
shadowed ${o.shadowed.map((c)=>`${c.layer} \xB7 ${c.source}`).join("; ")}`;return`${o.path}
|
|
2221
2221
|
${o.layer} \xB7 ${o.source}
|
|
2222
2222
|
${s??""}${a}`}).join(`
|
|
2223
2223
|
|
|
2224
|
-
`)}function
|
|
2224
|
+
`)}function rS(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 lM(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 nS(e){let t=e.reduce((r,n)=>Math.max(r,n.name.length),0);return Math.min(uM,Math.max(dM,t))}function pM(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 mM(e){let t=e.match.host||"*",r=e.match.path;if(r==="")return t;return`${t}${r.startsWith("/")?r:`/${r}`}`}function fM(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 hM(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 dM=8,uM=20,oS=80,xi=10;var wm=I(()=>{dt();si();Lt()});async function El(e,t){e.suspend();try{return await t()}finally{e.resume(),e.intermediateRender()}}import{useCallback as gM,useRef as vM}from"react";function iS({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:m,cfg:p,setScrollText:f,reveal:h,controller:v,refresh:b,profile:g,setReveal:C,resolveEnvironment:E,openDetail:S,copySelection:y,lastExportPath:x,openConfigBuffer:w,onDown:k,workspace:D,logView:O,diagnostics:F,lifecycleActions:Z}){let{loginGoogle:G,detectGoogle:R,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:nr,errorOnly:Qe,logLevel:Qt,logSearch:qe,logRegex:ht,logSource:xr,filteredLogs:Kt,setLogRegex:wr,setLogSince:or,setLogUntil:qt,setLogs:ir,setLogSearch:sr,toggleSplitLogs:ar}=O,{refreshAuth:Vt,setGoogle:ce}=F,{beginStart:K,beginStop:A,beginRestart:ve}=Z,J=vM(!1);return{runCommand:gM(async(ct,Re)=>{if(J.current)return;if(J.current=!0,ct.name!=="start"&&ct.name!=="stop"&&ct.name!=="restart")e("none");t("");let _o=Ip(Re,n);try{switch(ct.name){case"exit":o("quit"),e("confirm");return;case"down":{let _=Re.includes("--keep-services")||Re.includes("-k");k(_);return}case"help":e("help");return;case"version":i(tr()),ie().then((_)=>i(Me(_))).catch((_)=>i(L(_)));return;case"update":i("checking for update\u2026"),ie().then(async(_)=>{if(!_.newer||!_.command){i(Me(_));return}i(`installing ${_.current} \u2192 ${_.latest} via ${_.kind}\u2026`);let ue=await Le(_.command);if(ue.code!==0){i(`update failed (${ue.code}): ${(ue.stderr||ue.stdout).trim()||_.hint}`);return}i(`updated to ${_.latest}; ${bc}`)}).catch((_)=>i(L(_)));return;case"themes":if(Re[0]){s(Re[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(Re[0])Ke(Re[0]);l("logs");return;case"fullscreen":l("logs"),gt((_)=>u==="logs"?!_:!0);return;case"split":l("logs"),ar(),i("Split log panes");return;case"trace":{let _=Re.join(" ").trim();if(_===""){i("usage: /trace <request-or-trace-id>");return}sr(_),l("logs"),i(`tracing ${_}`);return}case"auth":{let _=(Re[0]??"").toLowerCase();if(_==="refresh"){l("auth"),await Vt();return}if(_==="login"){l("auth"),i("starting gcloud ADC login\u2026");try{await El(m,()=>G()),ce(await R(p?.google.project_id??"")),await Vt(),i("ADC login complete")}catch(ue){i(L(ue))}return}if(_==="logout"){l("auth"),i("revoking ADC\u2026");try{await j(),ce(await R(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 _=Re[0]??"",ue=Re.includes("--write"),Jr=Re.find((se)=>se!=="compose"&&se!=="--write")??"compose.yaml";if(_!=="compose"){i("usage: /import compose [path] [--write]");return}if(!z(Jr)){i(`compose file not found: ${Jr}`);return}let Xr=p?.repoRoot||process.cwd(),Sr=p?.configPath||`${Xr}/.devctl/config.yaml`;try{let se=Rl(de(Jr),Xr.split("/").pop()??"imported"),Wn=Ne(Xr,Sr,se.yaml),Gn=`${_l(se)}${Wn.length>0?`
|
|
2225
2225
|
validation:
|
|
2226
|
-
${
|
|
2226
|
+
${Wn.map(($r)=>` ${$r}`).join(`
|
|
2227
2227
|
`)}
|
|
2228
|
-
`:""}`;if(ue){if(
|
|
2229
|
-
Enter is preview only. /import compose ${
|
|
2230
|
-
`)),n("Config buffer not saved");return}if(i(e.configPath,v),p(""),r("none"),!s){n(`Wrote ${e.configPath}`);return}s.reload().then((g)=>{if(n(g.restart_required.length===0?"Configuration saved and reloaded":`Saved; restart required: ${g.restart_required.join(", ")}`),g.restart_required.length>0)a("reload"),r("confirm");c()})},[e,d,s,c]);return{configEditRef:l,configEditText:d,configEditError:f,setConfigEditError:p,openConfigBuffer:m,saveConfigBuffer:h}}var oS=I(()=>{te()});import{useEffect as lM}from"react";function iS({controller:e,cfg:t,paused:r,logSince:n,refresh:o,setLogs:i,setCfg:s,setConfigReloadError:a,setStatus:c}){return lM(()=>{if(!e)return;let l,d=!1,u=[],f=t&&t.logs.max_memory_events>0?t.logs.max_memory_events:50000,p=()=>{if(l=void 0,u.length>0){let h=u.splice(0,u.length);i((v)=>Gb(v,h,n,f))}if(d)d=!1,o()},m=e.onEvent((h)=>{if(h.type===Io&&h.payload&&typeof h.payload==="object"&&"event"in h.payload){let v=h.payload.event;if(!r)u.push(v);if(!l)l=setTimeout(p,30);return}if(h.type===Ti){a(dx(h));return}if(h.type===da)a(void 0),e.configSnapshot().then(s).catch((v)=>c(L(v)));if(d=!0,!l)l=setTimeout(p,30)});return()=>{if(m(),l)clearTimeout(l)}},[e,n,r,o]),{}}var sS=I(()=>{te();vr();gr()});import{useCallback as dM,useEffect as aS,useRef as cS,useState as yi}from"react";function lS({controller:e,workspace:t,cfg:r,snap:n,screen:o,configReloadError:i,setSnap:s,setStatus:a}){let{detectGoogle:c,runDoctor:l,loadPath:d}=t,[u,f]=yi(),[p,m]=yi(),[h,v]=yi(!1),[b,g]=yi(""),[_,R]=yi(0),[S,y]=yi({active:"Preparing diagnostics",checks:[]}),x=cS(void 0),w=cS(0),k=dM(async()=>{if(!e){a("Supervisor is not running");return}a("Probing configured identities\u2026");try{let D=await e.refreshAuth();s((W)=>W?{...W,identity:D}:W);let O=Object.values(D.service_account_status),F=O.filter((W)=>W==="available").length,Z=O.filter((W)=>W==="unavailable").length;a(O.length===0?"Identity refreshed \u2014 no service accounts configured":`Identity refreshed \u2014 ${F} available${Z>0?`, ${Z} unavailable`:""}`)}catch(D){a(L(D))}},[e]);return aS(()=>{c(r?.google.project_id??"").then(f).catch((D)=>{a(L(D))})},[r?.google.project_id,n?.identity.project]),aS(()=>{if(o!=="doctor"||!r)return;let D=x.current,O=n?.session_id??"";if(D?.cfg===r&&D.tick===_&&D.sessionID===O&&D.reloadError===i)return;x.current={cfg:r,tick:_,sessionID:O,reloadError:i};let F=++w.current;v(!0),g(""),y({active:"Preparing diagnostics",checks:[]});let Z;if(r.configPath!==""){Z="";try{d(r.repoRoot,r.configPath)}catch(W){Z=L(W)}}l(r,(W)=>{if(F===w.current)y(W)},{services:n?.services,proxyRunning:n?.proxy.running,repositoryConfigError:Z}).then((W)=>{if(F!==w.current)return;m(W)}).catch((W)=>{if(F!==w.current)return;g(L(W))}).finally(()=>{if(F===w.current)v(!1)})},[o,r,_,n?.session_id,i]),{google:u,setGoogle:f,doctor:p,doctorLoading:h,doctorError:b,doctorProgress:S,setDoctorTick:R,refreshAuth:k}}var dS=I(()=>{te()});import{useCallback as vf,useState as Cl}from"react";function uS({controller:e,workspace:t,cfg:r,snap:n,refresh:o,setStatus:i,setOverlay:s}){let{resolveStartRequest:a,startupPlan:c,shutdownPlan:l,shutdownPlanExact:d}=t,[u,f]=Cl(),[p,m]=Cl([]),[h,v]=Cl(!1),[b,g]=Cl("start"),_=vf(async(y,x)=>{if(!e||!r){i("no configuration loaded");return}try{let w=y.length>0,k=a(r,{services:y,profile:w?void 0:x}),D=c(r,k.services,k.profile);g("start"),m(D.waves.flat().filter((Z)=>vo(n?.services[Z]))),f(D),s("plan"),v(!0);let O=k.services.filter((Z)=>!vo(n?.services[Z])),F=await e.start({services:O.length>0?O:k.services,profile:w?void 0:k.profile});await o(),v(!1),i(Cw(F))}catch(w){v(!1),await o(),i(L(w))}},[r,e,o,n]),R=vf(async(y)=>{if(!e||!r){i("no configuration loaded");return}let x=y.length>0?y:Object.entries(n?.services??{}).filter(([,w])=>w.state!=="STOPPED"&&w.state!=="UNKNOWN").map(([w])=>w);if(x.length===0){i("nothing to stop");return}try{let w=l(r,x);g("stop"),m([]),f(w),s("plan"),v(!0),await e.stop(y),await o(),v(!1),i(Pw(w))}catch(w){v(!1),await o(),i(L(w))}},[r,e,o,n]),S=vf(async(y,x,w=!1)=>{if(!e||!r){i("no configuration loaded");return}try{let k=kw(r,y,x),D=w?l(r,k.services):d(r,k.services);g("restart"),m([]),f(D),s("plan"),v(!0),await e.restart(k.services,w),await o(),v(!1);let O=bl(D);i(O===""?"Restarted selected services":`Restarted ${O}`)}catch(k){v(!1),await o(),i(L(k))}},[r,e,o]);return{plan:u,planInitiallyRunning:p,planBusy:h,lifecycle:b,beginStart:_,beginStop:R,beginRestart:S}}var pS=I(()=>{te();xl();Lt()});import{useCallback as Lr,useEffect as Kr,useMemo as Fn,useRef as uM,useState as be}from"react";function mS(e,t,r,n,o){let i=Ap(e,t.sliceLen,t.windowStart,t.windowNewer);if(i.startDelta!==0){r(Math.max(0,t.windowStart+i.startDelta)),n(!0),o(i.selected);return}let s=Math.max(t.sliceLen-1,0),a=t.sliceLen>0&&(i.selected<s||t.windowNewer>0);if(a&&!t.pinned)r(Zc(t.filteredLen));n(a),o(i.selected)}function hS({controller:e,tui:t,names:r,screen:n,refresh:o,setStatus:i}){let[s,a]=be([]),[c,l]=be(!1),[d,u]=be(!1),[f,p]=be(!0),[m,h]=be(""),[v,b]=be(!1),[g,_]=be(0),[R,S]=be(""),[y,x]=be(""),[w,k]=be(""),[D,O]=be(""),[F,Z]=be(!1),[W,C]=be(0),[j,B]=be(Ar-1),[z,de]=be(!1),[Ee,Ne]=be(0),ne=fS,[H,ae]=be(t.log_timestamps!==!1),[Q,nt]=be(t.log_metadata!==!1),[ft,ie]=be(()=>[...Wb]),[Le]=be(""),[Me]=be(""),[Ke,gt]=be(!1),[Xe,Ct]=be("focus"),[ge,vt]=be(!1),[at,yt]=be(Ar-1),[rr,Qe]=be(!1),[Xt,qe]=be(-1),[mt,br]=be(),[Kt,xr]=be(""),[nr,qt]=be(!1),[or,ir]=be(!1),sr=uM([]),[Vt,ce]=be(0),K=Fn(()=>tl(r,s,ft),[r,s,ft]);Kr(()=>{ie((le)=>{let bt=new Set(le),Pt=!1;for(let Xr of s)if(Xr.service!==""&&!r.includes(Xr.service)&&!bt.has(Xr.service))bt.add(Xr.service),Pt=!0;return Pt?[...bt].sort():le})},[s,r]);let A=Fn(()=>yo(s,{service:w,services:ne,errorOnly:d,search:m,regex:Ke,source:Me,since:R,until:y,systemLogs:f}),[d,Ke,m,w,ne,R,Me,y,s,f]),ve=Fn(()=>Tp(A,ge,Vt),[A,ge,Vt]),J=ve.items,Dt=Fn(()=>yo(s,{service:D,services:ne,errorOnly:d,search:m,regex:Ke,source:Me,since:R,until:y,systemLogs:f}),[d,Ke,m,D,ne,R,Me,y,s,f]),ct=Fn(()=>Tp(Dt,z,Ee),[Dt,z,Ee]),Ce=ct.items;Kr(()=>{qe(-1),vt(!1),yt(Math.max(0,Math.min(Ar,A.length)-1))},[d,m,w,f]),Kr(()=>{de(!1),B(Math.max(0,Math.min(Ar,Dt.length)-1))},[d,m,D,f]),Kr(()=>{if(n!=="logs")Qe(!1)},[n]),Kr(()=>{if(n!=="logs"||ge)return;yt(Math.max(0,Math.min(Ar,A.length)-1))},[A.length,ge,n]),Kr(()=>{if(n!=="logs"||z)return;B(Math.max(0,Math.min(Ar,Dt.length)-1))},[Dt.length,z,n]);let Po=Lr(()=>{if(!ge)ce(Zc(A.length)),vt(!0)},[A.length,ge]),P=Lr(()=>{if(!z)Ne(Zc(Dt.length)),de(!0)},[Dt.length,z]),ue=Fn(()=>({sliceLen:J.length,windowStart:ve.start,windowNewer:ve.newer,filteredLen:A.length,pinned:ge}),[A.length,ge,J.length,ve.newer,ve.start]),Yr=Fn(()=>({sliceLen:Ce.length,windowStart:ct.start,windowNewer:ct.newer,filteredLen:Dt.length,pinned:z}),[Dt.length,z,Ce.length,ct.newer,ct.start]),Jr=Lr((le)=>mS(le,ue,ce,vt,yt),[ue]),wr=Lr((le)=>mS(le,Yr,Ne,de,B),[Yr]),se=Lr((le)=>{if(F&&W===1){wr(le);return}Jr(le)},[Jr,wr,W,F]),Bn=Lr((le)=>{let bt=J.length,Pt=Ap(le,bt,ve.start,ve.newer);if(Pt.startDelta!==0)ce(Math.max(0,ve.start+Pt.startDelta)),vt(!0);qe(Pt.selected)},[J.length,ve.newer,ve.start]),Un=Lr(()=>{if(vt(!1),de(!1),qe(-1),n==="logs")yt(Math.max(0,Math.min(Ar,A.length)-1)),B(Math.max(0,Math.min(Ar,Dt.length)-1));_((bt)=>bt+1);let le=Math.max(ve.newer,ct.newer);i(le>0?`Jumped to latest (+${le} new)`:"Jumped to latest logs")},[A.length,Dt.length,ve.newer,ct.newer,n]),Dr=Fn(()=>({services:F?fS:w!==""?[w]:ne,level:d?"ERROR":Le,search:m,regex:Ke,source:Me,since:R,until:y}),[d,Le,Ke,m,w,ne,R,Me,y,F]),un=Lr(async()=>{if(!e)return;try{br(await e.logsStats(Dr))}catch(le){i(L(le))}},[e,Dr]),Ws=Lr(async()=>{if(!e||c)return;try{let le=await e.logsPage(Dr);a((bt)=>Kb(bt,le.events)),xr(le.prevCursor),qt(le.hasPrev),await un()}catch(le){i(L(le))}},[e,Dr,c,un]),Ci=Lr(()=>{a([]),S(new Date().toISOString()),vt(!1),de(!1),i("Cleared on-screen log buffer")},[]),Pi=Lr(()=>{p((le)=>!le)},[]);return Kr(()=>{o().then(()=>Ws())},[o,Ws]),Kr(()=>{sr.current=s},[s]),Kr(()=>{let le=Lp(ge,ve.start,nr),bt=F&&Lp(z,ct.start,nr);if(!e||or||!le&&!bt)return;ir(!0),e.logsPage({...Dr,cursor:Kt,direction:"backward"}).then((Pt)=>{if(Pt.sessionChanged){i("Daemon session changed \u2014 older log history is no longer available"),qt(!1);return}let Xr=sr.current,Gs=qb(Xr,Pt.events);a(Gs);let _o=Gs.length-Xr.length;if(_o>0){if(ge)ce((Sr)=>Sr+_o);if(z)Ne((Sr)=>Sr+_o)}xr(Pt.prevCursor),qt(Pt.hasPrev)}).catch((Pt)=>i(L(Pt))).finally(()=>ir(!1))},[e,Dr,or,nr,ge,z,Kt,ve.start,ct.start,F]),Kr(()=>{if(!e||c||n!=="logs")return;let le=setInterval(()=>{un()},pM);return()=>clearInterval(le)},[e,c,un,n]),{logs:s,setLogs:a,paused:c,setPaused:l,errorOnly:d,setErrorOnly:u,showSystemLogs:f,logSearch:m,setLogSearch:h,logSearchFocused:v,setLogSearchFocused:b,logFollow:g,logSince:R,setLogSince:S,setLogUntil:x,logService:w,setLogService:k,logServiceB:D,setLogServiceB:O,splitLogs:F,splitFocus:W,setSplitFocus:C,toggleSplitLogs:()=>{if(F){C(0),Z(!1);return}if(D===""){let le=r.find((bt)=>bt!==w);if(le!==void 0)O(le)}Z(!0)},cycleSplitFocus:()=>C((le)=>le===0?1:0),setActiveLogService:(le)=>{let bt=(Pt)=>typeof le==="function"?le(Pt):le;if(F&&W===1){O(bt);return}k(bt)},logSliceB:Ce,logWindowB:ct,logSelectedB:j,logPinnedB:z,logServices:ne,logShowTimestamps:H,setLogShowTimestamps:ae,logShowMeta:Q,setLogShowMeta:nt,logLevel:Le,logSource:Me,logRegex:Ke,setLogRegex:gt,logWrap:Xe,setLogWrap:Ct,logPinned:ge,logSelected:at,logsFullscreen:rr,setLogsFullscreen:Qe,dashboardLogCursor:Xt,setDashboardLogCursor:qe,logFacets:mt,logSources:K,filteredLogs:A,filteredLogsB:Dt,logWindow:ve,logSlice:J,pinLogView:Po,pinLogViewB:P,applyLogCursor:se,applyLogCursorA:Jr,applyLogCursorB:wr,applyDashboardLogCursor:Bn,jumpToLatestLogs:Un,clearLogs:Ci,toggleSystemLogs:Pi}}var fS,pM=2000;var gS=I(()=>{te();gr();fS=[]});import{useCallback as ko,useState as vS}from"react";function yS({tui:e,controller:t,cfg:r,persistPrefs:n,snap:o,refresh:i,setStatus:s}){let[a,c]=vS(""),[l,d]=vS(()=>e.mcp_port??fo(t?.cfg.repoRoot??process.cwd())),u=ko((g)=>{let _=Rn(g);d(_);let R=r?.repoRoot??process.cwd();if(Dy(R,_)){n({mcp_port:null},`MCP port ${_} (default)`);return}n({mcp_port:_},`MCP port ${_}`)},[r,n]),f=ko(()=>{if(a==="")return l;let g=Ly(a,l);return c(""),u(g),g},[l,a,u]),p=ko(async(g)=>{if(!t||o?.mcp?.running!==!0)return;await t.mcpStop(),await t.mcpStart({port:g}),await i()},[t,i,o?.mcp?.running]),m=ko(async(g)=>{if(!t){s("Supervisor is not running");return}let _=o?.mcp?.disabled_tools??[],R=An(g.name,_),S=R?[..._,g.name]:_.filter((y)=>y!==g.name);try{let y=await t.mcpSetTools(S);n({mcp_disabled_tools:y},`${g.label} ${R?"disabled":"enabled"}`),await i()}catch(y){s(L(y))}},[t,n,i,o]),h=ko(async()=>{if(!t){s("Supervisor is not running");return}try{if(o?.mcp?.running)await t.mcpStop(),n({mcp_enabled:!1},"MCP off"),s("MCP stopped");else await t.mcpStart({port:l}),n({mcp_enabled:!0},"MCP on"),s("MCP started");await i()}catch(g){s(L(g))}},[t,l,n,i,o?.mcp?.running]),v=ko((g)=>{ml(g.text).then(()=>{s(`Copied ${g.title} snippet`)}).catch((_)=>{s(L(_))})},[]),b=ko((g)=>{let _=nf(g);if(_===void 0)return!1;let R=o?.mcp?.address??mi(o?.mcp?.port??l),S=Ms(R,o?.mcp?.token??"")[_];if(!S)return!1;return v(S),!0},[v,l,o?.mcp?.address,o?.mcp?.port,o?.mcp?.token]);return{mcpPortDraft:a,setMcpPortDraft:c,mcpPort:l,persistMcpPort:u,applyMcpPortDraft:f,restartMcpOnPort:p,toggleMcpTool:m,toggleMcp:h,copyMcpSnippet:v,copyFocusedMcpSnippet:b}}var bS=I(()=>{te();yl();fs();qp();$s()});import{RGBA as fM}from"@opentui/core";import{useCallback as ln,useEffect as mM,useMemo as xS,useRef as Pl,useState as _l}from"react";function wS({tui:e,controller:t,resolveTuiOverridePath:r,terminalBackground:n,userTuiConfigPath:o,snap:i,setStatus:s,saveTuiPreferences:a,setPaletteIndex:c,setOverlay:l,setConfirmKind:d,setScreen:u,setSelected:f,screen:p}){let[m,h]=_l(e.theme||t?.cfg.ui.theme||"devctl"),v=Pl(m),[b,g]=_l(e.mouse),_=Pl(e.mouse),[R,S]=_l(e.leader_timeout),y=Pl(e.leader_timeout),[x,w]=_l(()=>bs(e.font_size)),k=Pl(x),D=vb(r()),O=xS(()=>{let H=pb(m);if(Tc(m)!=="terminal"||!fp(n))return H;let ae=n??H.background;return{...H,background:ae,panel:ae,element:ae,inverse:ae}},[n,m]),F=Tc(m)==="terminal"&&fp(n)?fM.defaultBackground(n??void 0):O.background,Z=xS(()=>wb({themeName:m,fontSize:x,mouse:b,leaderMs:R,locked:D,configPath:D?e.path||yp(r(),o()):yp(r(),o()),mcpRunning:i?.mcp?.running===!0}),[x,R,b,D,i?.mcp?.running,m,e.path]),W=ln((H,ae)=>{if(D){s(`${ae} session only`);return}let Q=a(H);s(`${ae} saved ${Q}`)},[D]),C=ln((H)=>{h(H),v.current=H,W({theme:H},`theme ${H}`)},[W]),j=ln(()=>{let H=!b;g(H),_.current=H,W({mouse:H},`mouse ${H?"on":"off"} restart TUI to apply clicks`)},[b,W]),B=ln((H)=>{S(H),y.current=H,W({leader_timeout:H},`leader ${H}ms`)},[W]),z=ln((H)=>{let ae=bs(H);w(ae),k.current=ae,W({font_size:ae},`display ${Ic(ae)}`)},[W]),de=ln(()=>{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=ln((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=ln((H,ae)=>{let Q=Ln(Z,ae);if(!Q)return;if(Q.id==="theme"){C(yb(m,H));return}if(Q.id==="leader"){B(bb(R,H));return}if(Q.id==="font"){z(xs(x,H));return}if(Q.id==="mouse")j()},[z,B,x,R,C,Z,m,j]),ne=ln(()=>{h(v.current)},[]);return mM(()=>{if(p==="settings")return;h(v.current),w(k.current),S(y.current),g(_.current)},[p]),{themeName:m,setThemeName:h,leaderMs:R,fontSize:x,prefsLocked:D,palette:O,rootBackground:F,settingRows:Z,persistPrefs:W,persistTheme:C,toggleMouse:j,applyFont:z,applyReset:de,activateSetting:Ee,cycleSetting:Ne,revertThemePreview:ne}}var SS=I(()=>{Er();tt()});import{jsxDEV as Ns}from"@opentui/react/jsx-dev-runtime";function kS(e){let{palette:t,termW:r,termH:n,step:o,answers:i,repo:s,draft:a,authStatus:c,onDraft:l,onSubmit:d}=e,u=wt[o]??wt[0],f=wt.length,p=hM(u.id,i,s);return Ns(pt,{palette:t,title:`setup ${o+1}/${f} ${u.title}`,bottomTitle:"enter next \xB7 esc cancel",termW:r,termH:n,preferW:64,preferH:12,borderColor:t.primary,gap:1,children:[Ns("text",{fg:t.muted,wrapMode:"word",children:p},void 0,!1,void 0,this),u.id==="auth"||u.id==="write"||u.id==="repo"?Ns("text",{fg:t.text,wrapMode:"word",children:u.id==="auth"?c:u.id==="repo"?s:"Write .devctl/config.yaml and attach the daemon."},void 0,!1,void 0,this):Ns("input",{focused:!0,value:a,placeholder:u.prompt,onInput:l,onSubmit:d,backgroundColor:t.element,focusedBackgroundColor:t.element,textColor:t.text,cursorColor:t.primary},void 0,!1,void 0,this),Ns(it,{palette:t,hints:[{key:"enter",label:u.id==="write"?"write and attach":"next"},{key:"esc",label:"cancel"}]},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function yf(e,t,r){if(e==="name")return t.name;if(e==="project")return t.project;if(e==="sa")return t.sa;if(e==="audience")return t.audience;if(e==="port")return String(t.proxyPort);if(e==="profile")return t.profile;if(e==="repo")return r;return""}function CS(e,t,r,n){let o={...t};if(e==="name")o.name=r.trim()||Sn(n).name;else if(e==="project")o.project=r.trim();else if(e==="sa")o.sa=r.trim();else if(e==="audience")o.audience=r.trim();else if(e==="port")o.proxyPort=Aa(r,t.proxyPort);else if(e==="profile")o.profile=r.trim();return o}function hM(e,t,r){if(e==="repo")return`Using ${r}. The TUI always writes under this checkout.`;if(e==="auth")return"ADC login needs a real TTY. Enter continues; if ADC is missing, login runs gcloud on the terminal.";if(e==="write")return`Project ${t.name||"(unnamed)"}. Enter writes the starter file and attaches the daemon.`;return wt.find((n)=>n.id===e)?.prompt??""}var bf=I(()=>{La();ke()});import{useCallback as xf,useState as Rl}from"react";function PS({workspace:e,renderer:t,setOverlay:r,setStatus:n,setScreen:o,onAttached:i,setController:s,setCfg:a,setBootError:c}){let l=process.cwd(),[d,u]=Rl(0),[f,p]=Rl(()=>Sn(l)),[m,h]=Rl(""),[v,b]=Rl("Checking Application Default Credentials\u2026"),g=xf(()=>{let S=Sn(l);u(0),p(S),h(yf("repo",S,l)),r("setup-wizard"),e.detectGoogle("").then((y)=>{p((x)=>({...x,project:y.projectID||x.project})),b(y.adcAvailable?`ADC available${y.userEmail?` (${y.userEmail})`:""}. Enter continues.`:"ADC is not available. Enter runs gcloud ADC login on this TTY, then continues.")})},[l,r,e]),_=xf(async(S)=>{try{Ia(l,S);let y=await e.openTui(l,"");i?.(y),s(y),a(y.cfg),c(void 0),r("none"),o("dashboard"),n("Wrote .devctl/config.yaml and attached the daemon")}catch(y){n(L(y))}},[i,l,c,a,s,r,o,n,e]),R=xf(async()=>{let S=wt[d];if(!S)return;let y=CS(S.id,f,m,l);if(S.id==="auth"){if(!(await e.detectGoogle(y.project)).adcAvailable)try{await kl(t,()=>e.loginGoogle())}catch(D){n(L(D))}}if(p(y),S.id==="write"){await _(y);return}let x=Math.min(d+1,wt.length-1),w=wt[x];u(x),h(w?yf(w.id,y,l):"")},[f,m,_,t,l,d,e,n]);return{step:d,answers:f,draft:m,authStatus:v,repo:l,setDraft:h,startWizard:g,advanceWizard:R}}var _S=I(()=>{La();Ma();te();bf()});import{useCallback as gM,useEffect as RS,useState as wf}from"react";function ES({controller:e,cfg:t,envService:r}){let[n,o]=wf(),[i,s]=wf(!1),[a,c]=wf(""),l=n?.name===r,d=l?n?.env:void 0,u=r===""?"config":i&&!l?"loading":l?"resolved":a!==""?"error":"config",f=l||r===""?"":a;RS(()=>{o(void 0),c("")},[t]),RS(()=>{if(!e||r===""||!t?.services[r])return;if(n?.name===r)return;let m=!1;return s(!0),c(""),e.execService(r,[],!0).then((h)=>{if(m)return;o({name:r,env:h.environment??{}}),c("")}).catch((h)=>{if(m)return;c(L(h))}).finally(()=>{s(!1)}),()=>{m=!0}},[t,e,r,n?.name]);let p=gM(async(m)=>{if(!e)return;try{let h=await e.execService(m,[],!0);o({name:m,env:h.environment??{}}),c("")}catch(h){throw c(L(h)),h}},[e]);return{inspectorEnv:d,inspectorEnvStatus:u,inspectorEnvError:f,resolveEnvironment:p}}var TS=I(()=>{te()});import{jsxDEV as Nn}from"@opentui/react/jsx-dev-runtime";function AS(e){let{palette:t,path:r,initialValue:n,error:o,termW:i,termH:s,textareaRef:a}=e;return Nn(pt,{palette:t,title:"config buffer",bottomTitle:`${It("s")} save \xB7 esc discard \xB7 e still opens $EDITOR`,termW:i,termH:s,preferW:88,preferH:28,gap:1,children:[Nn("box",{height:1,overflow:"hidden",children:Nn("text",{fg:t.muted,wrapMode:"none",children:r},void 0,!1,void 0,this)},void 0,!1,void 0,this),Nn("textarea",{ref:a,focused:!0,initialValue:n,backgroundColor:t.panel,focusedBackgroundColor:t.panel,textColor:t.text,focusedTextColor:t.text,cursorColor:t.primary},void 0,!1,void 0,this),o!==""?Nn("box",{height:2,overflow:"hidden",children:Nn("text",{fg:t.error,wrapMode:"word",children:o},void 0,!1,void 0,this)},void 0,!1,void 0,this):Nn("box",{height:1,overflow:"hidden",children:Nn("text",{fg:t.muted,children:"validate, then write. invalid YAML is not saved."},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}var LS=I(()=>{ke();Rr()});import{jsxDEV as Sf}from"@opentui/react/jsx-dev-runtime";function DS(e){let{palette:t,title:r,body:n,kind:o,termW:i,termH:s}=e,a=lx(o),c=a.map((l)=>`${l.key} ${l.label}`).join(" \xB7 ");return Sf(pt,{palette:t,title:r,bottomTitle:c,termW:i,termH:s,preferW:52,preferH:8,borderColor:t.warning,gap:1,children:[Sf("text",{fg:t.text,wrapMode:"word",children:n},void 0,!1,void 0,this),Sf(it,{palette:t,hints:a},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}var IS=I(()=>{vr();ke()});import{jsxDEV as MS}from"@opentui/react/jsx-dev-runtime";function OS(e){let{palette:t,termW:r,termH:n}=e,o=Hp();return MS(pt,{palette:t,title:"leader",termW:r,termH:n,preferW:68,preferH:5,anchor:"bottom",children:MS(it,{palette:t,hints:o},void 0,!1,void 0,this)},void 0,!1,void 0,this)}var $S=I(()=>{wo();ke()});import{jsxDEV as Jt,Fragment as vM}from"@opentui/react/jsx-dev-runtime";function FS(e){let{palette:t,event:r,termW:n,termH:o,scrollRef:i}=e;if(!r)return null;let s=r.raw?Fb(r.raw):void 0,a=r.message.length>120||r.message.includes(`
|
|
2231
|
-
`)||s!==void 0;return Jt(pt,{palette:t,title:"log details",bottomTitle:"j/k scroll \xB7 esc close",termW:n,termH:o,preferW:a?84:72,preferH:a?22:14,gap:1,children:Jt("scrollbox",{ref:i,focused:!1,stickyScroll:!1,scrollX:!1,style:Ue(t),children:Jt("box",{flexDirection:"column",overflow:"hidden",children:[Jt("text",{fg:t.text,wrapMode:"word",children:Jc(r.message)},void 0,!1,void 0,this),Jt("text",{fg:t.muted,children:`time ${r.timestamp}`},void 0,!1,void 0,this),Jt("text",{fg:Ze(r.service,t),children:`service ${r.service}`},void 0,!1,void 0,this),Jt("text",{fg:t.muted,children:`source ${r.source}${r.stream?` / ${r.stream}`:""}`},void 0,!1,void 0,this),Jt("text",{fg:t.muted,children:`level ${Yc(r.level)}`},void 0,!1,void 0,this),Jt("text",{fg:t.muted,children:`pid ${r.pid||"\u2014"}`},void 0,!1,void 0,this),Jt("text",{fg:t.muted,children:`request ${r.request_id||"\u2014"}`},void 0,!1,void 0,this),Jt("text",{fg:t.muted,children:`identity ${r.identity||"\u2014"}`},void 0,!1,void 0,this),s===void 0?null:Jt(vM,{children:[Jt("text",{fg:t.muted,children:"raw json"},void 0,!1,void 0,this),Jt("text",{fg:t.text,wrapMode:"word",children:s},void 0,!1,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this)}var NS=I(()=>{gr();ke();tt()});import{jsxDEV as U}from"@opentui/react/jsx-dev-runtime";function kf(e,t){switch(e){case"completed":return{icon:"\u2713",label:"Completed",tone:"success",color:t.success};case"failed":return{icon:"\u2717",label:"Failed",tone:"error",color:t.error};case"unhealthy":return{icon:"\u26A0",label:"Unhealthy",tone:"warning",color:t.warning};case"active":return{icon:"\u25CF",label:"In progress",tone:"primary",color:t.primary};default:return{icon:"\u25CB",label:"Queued",tone:"muted",color:t.muted}}}function yM(e,t,r,n){if(t!=="")return{icon:"\u2717",label:"Failed",tone:"error",color:n.error};if(!e)return{icon:"\u2713",label:"Finished",tone:"success",color:n.success};if(r==="unhealthy")return{icon:"\u26A0",label:"Waiting on health",tone:"warning",color:n.warning};return{icon:"\u25CF",label:"Running",tone:"primary",color:n.primary}}function HS(e){return U("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,children:U("text",{wrapMode:"none",children:[U("span",{fg:e.palette.primary,children:e.label.toUpperCase()},void 0,!1,void 0,this),e.detail?U("span",{fg:e.palette.muted,children:` ${e.detail}`},void 0,!1,void 0,this):null]},void 0,!0,void 0,this)},void 0,!1,void 0,this)}function jS(e){let{palette:t,plan:r,snap:n,busy:o,failed:i,kind:s,termW:a=80,termH:c=24,scrollRef:l,initiallyRunning:d=[],onDismiss:u}=e,f=new Set(d),p=r.waves.map((C,j)=>({wave:C.filter((B)=>!f.has(B)),waveIdx:j})).filter(({wave:C})=>C.length>0),m=Rw(r,n,s),h=r.waves[m.currentWaveIndex]??[],v=Fs(h,n,s),b=kf(v,t),g=yM(o,i,v,t),_=Tw(s,o,i,r.profile),R=Aw(o,i),S=i?t.inverse:t.text,y=i?t.error:t.element,x=Math.min(92,Math.max(40,a-8)),w=x>=82?22:x>=66?17:13,k=12,D=9,O=x>=56,F=x>=72,Z=x<72||r.waves.length>3,W=r.profile?`PROFILE ${r.profile}`:s.toUpperCase();return U(pt,{palette:t,title:_,bottomTitle:"enter close \xB7 esc hide (operation continues)",termW:a,termH:c,preferW:98,preferH:28,borderColor:g.color,gap:0,children:[U("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,backgroundColor:t.element,children:[U(Be,{palette:t,label:`${g.icon} ${g.label.toUpperCase()}`,tone:g.tone},void 0,!1,void 0,this),U("box",{paddingLeft:1,flexGrow:1,overflow:"hidden",children:U("text",{fg:t.text,wrapMode:"none",children:W},void 0,!1,void 0,this)},void 0,!1,void 0,this),U(Be,{palette:t,label:`${m.ready}/${m.total} READY`,tone:m.isComplete?"success":"info"},void 0,!1,void 0,this),U(Be,{palette:t,label:`WAVE ${Math.min(m.currentWaveIndex+1,Math.max(1,m.totalWaves))}/${Math.max(1,m.totalWaves)}`,tone:b.tone},void 0,!1,void 0,this)]},void 0,!0,void 0,this),U("box",{height:2,flexDirection:"column",overflow:"hidden",flexShrink:0,paddingLeft:1,paddingRight:1,children:[U("box",{height:1,flexDirection:"row",overflow:"hidden",children:[U("text",{wrapMode:"none",children:[U("span",{fg:t.muted,children:"PROGRESS "},void 0,!1,void 0,this),U("span",{fg:g.color,children:`[${m.progressBar}]`},void 0,!1,void 0,this),U("span",{fg:t.text,children:` ${m.percent}%`},void 0,!1,void 0,this)]},void 0,!0,void 0,this),U("box",{flexGrow:1},void 0,!1,void 0,this),U("text",{wrapMode:"none",children:U("span",{fg:b.color,children:`${b.icon} ${b.label}`},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this),U("box",{height:1,overflow:"hidden",children:U("text",{fg:t.muted,wrapMode:"none",children:o?s==="stop"?"Dependents stop first; each wave waits for the previous wave.":"Each wave waits until its services are healthy before the next wave starts.":R.primary},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this),U(HS,{palette:t,label:"Pipeline",detail:`${r.waves.length} execution wave${r.waves.length===1?"":"s"}`},void 0,!1,void 0,this),U("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,backgroundColor:t.element,children:r.waves.map((C,j)=>{let B=Fs(C,n,s),z=kf(B,t),de=Z?`W${j+1} ${z.icon} ${C.length}`:`W${j+1} ${z.icon} ${z.label} \xB7 ${C.length}`;return U("box",{flexDirection:"row",overflow:"hidden",flexShrink:0,children:[U(Be,{palette:t,label:de,tone:z.tone},void 0,!1,void 0,this),j<r.waves.length-1?U("box",{width:3,flexShrink:0,overflow:"hidden",children:U("text",{fg:t.muted,children:" \u2500\u25BA"},void 0,!1,void 0,this)},void 0,!1,void 0,this):null]},`step-${j}`,!0,void 0,this)})},void 0,!1,void 0,this),(r.blockers??[]).length>0?U("box",{minHeight:1,flexDirection:"row",overflow:"hidden",flexShrink:0,backgroundColor:t.element,paddingLeft:1,children:U("text",{fg:t.error,wrapMode:"word",children:`BLOCKED ${r.blockers?.map((C)=>`${C.name}: ${C.message}`).join(" \xB7 ")}`},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,d.length>0?U("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,paddingLeft:1,children:U("text",{wrapMode:"none",children:[U("span",{fg:t.success,children:"\u2713 ALREADY READY"},void 0,!1,void 0,this),U("span",{fg:t.muted,children:` ${d.join(", ")}`},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this):null,U(HS,{palette:t,label:"Wave details",detail:"live process and health state"},void 0,!1,void 0,this),U("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,backgroundColor:t.element,paddingLeft:1,children:[U("box",{width:2,flexShrink:0},void 0,!1,void 0,this),U("box",{width:w,flexShrink:0,overflow:"hidden",children:U("text",{fg:t.muted,children:oe("SERVICE",w)},void 0,!1,void 0,this)},void 0,!1,void 0,this),U("box",{width:12,flexShrink:0,overflow:"hidden",children:U("text",{fg:t.muted,children:oe("STATE",12)},void 0,!1,void 0,this)},void 0,!1,void 0,this),O?U("box",{width:9,flexShrink:0,overflow:"hidden",children:U("text",{fg:t.muted,children:oe("PORT",9)},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,F?U("box",{flexGrow:1,overflow:"hidden",children:U("text",{fg:t.muted,children:"DETAIL"},void 0,!1,void 0,this)},void 0,!1,void 0,this):null]},void 0,!0,void 0,this),U("scrollbox",{ref:l,focused:!1,stickyScroll:!1,scrollX:!1,style:Ue(t),children:U("box",{flexDirection:"column",overflow:"hidden",gap:1,children:p.map(({wave:C,waveIdx:j})=>{let B=Fs(C,n,s),z=kf(B,t),de=`${Ew(s,j,B)} \xB7 ${C.length} service${C.length===1?"":"s"}`;return U("box",{border:!0,borderStyle:"rounded",borderColor:z.color,title:de,titleColor:z.color,flexDirection:"column",paddingLeft:1,paddingRight:1,flexShrink:0,overflow:"hidden",children:C.map((Ee)=>{let Ne=n?.services[Ee],ne=tr(Ne),H=_w(Ee,r,n,s),ae=Ee===i||ne==="FAILED",Q=ne==="UNHEALTHY",nt=ne==="HEALTHY"||s==="stop"&&ne==="STOPPED",ft=go(Ne),ie=ae?"\u2717":nt?"\u2713":Q?"\u26A0":ne==="STARTING"||ne==="RUNNING"||ne==="STOPPING"||ne==="RESTARTING"?"\u25CF":"\u25CB",Le=ae?t.error:et(t,ne),Me=ae?t.error:Q?t.warning:nt?t.success:t.muted;return U("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,children:[U("box",{width:2,flexShrink:0,overflow:"hidden",children:U("text",{fg:Le,children:ie},void 0,!1,void 0,this)},void 0,!1,void 0,this),U("box",{width:w,flexShrink:0,overflow:"hidden",children:U("text",{fg:Ze(Ee,t),wrapMode:"none",children:oe(Ee,w)},void 0,!1,void 0,this)},void 0,!1,void 0,this),U("box",{width:12,flexShrink:0,overflow:"hidden",children:U("text",{fg:et(t,ne),wrapMode:"none",children:oe(ne,12)},void 0,!1,void 0,this)},void 0,!1,void 0,this),O?U("box",{width:9,flexShrink:0,overflow:"hidden",children:U("text",{fg:ft?t.info:t.muted,wrapMode:"none",children:oe(ft?`:${ft}`:"\u2014",9)},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,F?U("box",{flexGrow:1,overflow:"hidden",children:U("text",{fg:Me,wrapMode:"none",children:H},void 0,!1,void 0,this)},void 0,!1,void 0,this):null]},Ee,!0,void 0,this)})},`wave-card-${j}`,!1,void 0,this)})},void 0,!1,void 0,this)},void 0,!1,void 0,this),U("box",{height:3,flexShrink:0,flexDirection:"column",backgroundColor:y,border:["top"],borderStyle:"single",borderColor:g.color,paddingLeft:1,paddingRight:1,overflow:"hidden",onMouseDown:u,children:[U("text",{fg:S,wrapMode:"none",children:R.primary},void 0,!1,void 0,this),U("text",{fg:i?t.inverse:t.muted,wrapMode:"none",children:R.secondary},void 0,!1,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)}var BS=I(()=>{Ot();xl();Lt();ke();tt()});import{jsxDEV as yr}from"@opentui/react/jsx-dev-runtime";function US(e){let{palette:t,route:r,termW:n,termH:o,scrollRef:i}=e;if(!r)return null;return yr(pt,{palette:t,title:r.name,bottomTitle:"j/k scroll \xB7 esc close",termW:n,termH:o,preferW:78,preferH:18,gap:1,children:yr("scrollbox",{ref:i,focused:!1,stickyScroll:!1,scrollX:!1,style:Ue(t),children:yr("box",{flexDirection:"column",overflow:"hidden",children:[yr("text",{fg:t.muted,children:`auth ${r.authType||"none"}`},void 0,!1,void 0,this),yr("text",{fg:t.muted,children:`identity ${r.identityType||"\u2014"}`},void 0,!1,void 0,this),yr("text",{fg:t.text,wrapMode:"word",children:`account ${r.serviceAccount||"\u2014"}`},void 0,!1,void 0,this),yr("text",{fg:t.text,wrapMode:"word",children:`audience ${r.audience||"\u2014"}`},void 0,!1,void 0,this),yr("text",{fg:t.muted,wrapMode:"word",children:`client id ${r.clientId||"ADC default"}`},void 0,!1,void 0,this),yr("text",{fg:t.muted,wrapMode:"word",children:`client secret ${r.clientSecret||"\u2014"}`},void 0,!1,void 0,this),yr("text",{fg:t.muted,children:`match host ${r.matchHost||"*"}`},void 0,!1,void 0,this),yr("text",{fg:t.muted,children:`match path ${r.matchPath||"*"}`},void 0,!1,void 0,this),yr("text",{fg:t.text,wrapMode:"word",children:`upstream ${r.upstream}`},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this)}var WS=I(()=>{ke()});import{jsxDEV as El}from"@opentui/react/jsx-dev-runtime";function GS(e){let{palette:t,title:r,body:n,termW:o,termH:i,scrollRef:s}=e,a=n.length>400||n.split(`
|
|
2232
|
-
`).length>16;return El(pt,{palette:t,title:r,bottomTitle:"j/k scroll \xB7 esc close",termW:o,termH:i,preferW:a?88:76,preferH:a?24:16,gap:1,children:El("scrollbox",{ref:s,focused:!1,stickyScroll:!1,scrollX:!1,style:Ue(t),children:El("box",{flexDirection:"column",overflow:"hidden",children:El("text",{fg:n===""?t.muted:t.text,wrapMode:"word",children:n===""?"(empty)":n},void 0,!1,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this)}var KS=I(()=>{ke()});import{jsxDEV as Wt}from"@opentui/react/jsx-dev-runtime";function qS(e){let{palette:t,items:r,query:n,selected:o,onQuery:i,onSubmit:s,title:a="commands"}=e,c=xo(n)!=="",l=Lx(o,Cf,r.length),d=c?r.slice(l,l+Cf):Dx(r,o,Cf),u=c?[{group:"",items:d}]:$p(d),f=Math.max(d.length+(c?0:u.length),1),p=ui(r,o),m=_x(r);return Wt("box",{height:f+bM,flexShrink:0,border:!0,borderStyle:"rounded",borderColor:t.borderActive,titleColor:t.primary,backgroundColor:t.panel,title:a,flexDirection:"column",overflow:"hidden",children:[d.length===0?Wt("box",{height:1,paddingLeft:1,overflow:"hidden",children:Wt("text",{fg:t.muted,children:"no matching command"},void 0,!1,void 0,this)},void 0,!1,void 0,this):u.flatMap((h)=>[...h.group===""?[]:[Wt("box",{height:1,paddingLeft:1,overflow:"hidden",backgroundColor:t.panel,children:Wt("text",{fg:t.muted,wrapMode:"none",children:h.group},void 0,!1,void 0,this)},`g-${h.group}`,!1,void 0,this)],...h.items.map((b)=>{let g=sl(b)===(p?sl(p):"");return Wt("box",{height:1,flexDirection:"row",overflow:"hidden",paddingLeft:1,paddingRight:1,backgroundColor:g?t.highlight:t.panel,children:[Wt("box",{width:m,flexShrink:0,overflow:"hidden",children:Wt("text",{fg:g?t.primary:t.text,wrapMode:"none",children:`${g?"\u203A":" "} /${Fp(b).slice(0,Op)}`},void 0,!1,void 0,this)},void 0,!1,void 0,this),Wt("box",{flexGrow:1,overflow:"hidden",children:Wt("text",{fg:g?t.accent:t.muted,wrapMode:"none",children:Px(b)},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},sl(b),!0,void 0,this)})]),Wt("box",{height:1,flexDirection:"row",overflow:"hidden",backgroundColor:t.highlight,paddingLeft:1,children:[Wt("box",{width:2,flexShrink:0,children:Wt("text",{fg:t.primary,children:"/"},void 0,!1,void 0,this)},void 0,!1,void 0,this),Wt("box",{flexGrow:1,overflow:"hidden",children:Wt("input",{focused:!0,value:n,placeholder:"start logs settings",onInput:i,onSubmit:()=>s(),backgroundColor:t.highlight,focusedBackgroundColor:t.highlight,textColor:t.text,cursorColor:t.primary},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)}var Cf=10,bM=3;var VS=I(()=>{wo()});import{jsxDEV as JS}from"@opentui/react/jsx-dev-runtime";function XS(e){let{palette:t,themeName:r,selected:n,termW:o,termH:i,onIndex:s,onPreview:a,onPick:c}=e,l=St.map((u)=>({name:u===r?`${u} (active)`:u,description:u===r?`${pp[u]} \xB7 current`:pp[u],value:u})),d=ol(o,i,zS,YS,"center",!ho(ye()));return JS(pt,{palette:t,title:"/themes",bottomTitle:"enter save \xB7 esc revert",termW:o,termH:i,preferW:zS,preferH:YS,children:JS("select",{height:Math.max(4,d.height-3),options:l,selectedIndex:n,showDescription:!0,showSelectionIndicator:!0,wrapSelection:!0,backgroundColor:t.panel,focusedBackgroundColor:t.panel,textColor:t.text,focusedTextColor:t.text,selectedBackgroundColor:t.highlight,selectedTextColor:t.primary,descriptionColor:t.muted,selectedDescriptionColor:t.accent,onChange:(u)=>{s(u);let f=St[u];if(f)a(f)},onSelect:(u,f)=>{let p=f?.value;if(typeof p==="string"&&p!=="")c(p)}},void 0,!1,void 0,this)},void 0,!1,void 0,this)}var zS=52,YS=22;var QS=I(()=>{Mt();vr();ke();Er();tt()});function Tl(e,t,r){let n=e?.google?.project_id??"";if(n!=="")return{project:n,source:"configuration"};let o=t?.project||r?.projectID||"",i=t?.project_source||r?.projectSource||"";return{project:o,source:i}}import{jsxDEV as st}from"@opentui/react/jsx-dev-runtime";function Al(e){let{palette:t,title:r,tone:n="muted",children:o}=e,i=n==="success"?t.success:n==="warning"?t.warning:n==="error"?t.error:t.border;return st("box",{border:!0,borderStyle:"rounded",borderColor:i,title:r,titleColor:i,flexDirection:"column",flexShrink:0,paddingLeft:1,paddingRight:1,marginBottom:1,overflow:"hidden",children:o},void 0,!1,void 0,this)}function ZS(e){let{palette:t,cfg:r,google:n,identity:o}=e;if(!n&&!o)return st(fl,{palette:t,label:"Checking Application Default Credentials\u2026"},void 0,!1,void 0,this);let i=xM(r,o),s=o?.user||n?.userEmail||"",{project:a,source:c}=Tl(r,o,n),l=o?.adc??n?.adcAvailable===!0,d=o?.iap??(r?.proxy.routes??[]).some((h)=>h.auth.type.toLowerCase()==="iap"),u=i.filter((h)=>h.inactive!==!0),f=u.some((h)=>h.ok===!0),p=u.some((h)=>h.ok===!1),m=f?"AVAILABLE":p?"UNAVAILABLE":u.length>0?"NOT PROBED":i.length>0?"not required":"not configured";return st(kt,{palette:t,title:"identity",scroll:!0,children:[st(Al,{palette:t,title:"Google identity",tone:l?"success":"warning",children:[st(So,{palette:t,label:"user",value:s||"(unknown)"},void 0,!1,void 0,this),st(So,{palette:t,label:"project",value:a||"(unset)"},void 0,!1,void 0,this),st(So,{palette:t,label:"source",value:c||"\u2014"},void 0,!1,void 0,this),st(So,{palette:t,label:"ADC",value:l?"\u2713 available":"\u2717 missing",tone:l?"success":"error"},void 0,!1,void 0,this),st(So,{palette:t,label:"gcloud",value:n?.gcloudInstalled?"\u2713 installed":"\u2717 not installed",tone:n?.gcloudInstalled?"success":"error"},void 0,!1,void 0,this)]},void 0,!0,void 0,this),st(Al,{palette:t,title:`Service accounts (${i.length})`,tone:u.length===0?"muted":f?"success":"warning",children:[i.length===0?st("text",{fg:t.muted,wrapMode:"word",children:"none configured \u2014 identities come from config, never hard-coded"},void 0,!1,void 0,this):i.map((h)=>st("text",{fg:h.inactive?t.muted:h.ok===!0?t.success:h.ok===!1?t.error:t.text,wrapMode:"none",children:`${h.inactive?"\u2013":h.ok===!0?"\u2713":h.ok===!1?"\u2717":"\u25CB"} ${h.email}${h.inactive?" \u2014 inactive (route auth is none)":""}`},h.email,!1,void 0,this)),st("box",{height:1,flexShrink:0},void 0,!1,void 0,this),st(So,{palette:t,label:"impersonation",value:m,tone:f?"success":u.length>0?"warning":"muted"},void 0,!1,void 0,this)]},void 0,!0,void 0,this),st(Al,{palette:t,title:"IAP",tone:d?"success":"muted",children:[st("box",{flexDirection:"row",overflow:"hidden",flexShrink:0,children:st(Be,{palette:t,label:d?"routes present":"no IAP routes",tone:d?"success":"idle"},void 0,!1,void 0,this)},void 0,!1,void 0,this),d?st("text",{fg:t.muted,wrapMode:"word",children:"each route needs its own audience"},void 0,!1,void 0,this):null]},void 0,!0,void 0,this),!l?st(Al,{palette:t,title:"ADC unavailable",tone:"warning",children:[st("text",{fg:t.text,wrapMode:"word",children:"Local services still start. Cloud identity, impersonation, and IAP need ADC."},void 0,!1,void 0,this),st("text",{fg:t.muted,wrapMode:"word",children:"/auth login suspends this screen and runs gcloud ADC login, then restores the TUI"},void 0,!1,void 0,this)]},void 0,!0,void 0,this):null]},void 0,!0,void 0,this)}function xM(e,t){let r=new Map((e?iu(e):[]).map((i)=>[i.email,i.active])),n=t?.service_account_status??{};return[...new Set([...r.keys(),...Object.keys(n)])].sort((i,s)=>i.localeCompare(s)).map((i)=>{let s=n[i]??"unknown",a=r.get(i)===!1;return{email:i,inactive:a,ok:a||s==="unknown"?void 0:s==="available"}})}var ek=I(()=>{jr();Yt();ke()});import{jsxDEV as M}from"@opentui/react/jsx-dev-runtime";function ok(e){let{palette:t,cfg:r,width:n,selectedTask:o=0}=e,i=ye();if(!r)return M(ze,{palette:t,title:"No configuration",body:"Create .devctl/config.yaml or run setup."},void 0,!1,void 0,this);let s=Math.max(vi+8,n-6-i.pad*2),a=s>=Qw,c=a?Math.floor((s-rk)/2):s,l=Gw(r),d=Kw(r),u=qw(r),f=Vw(r),p=zw(r);return M(kt,{palette:t,title:`config ${r.project.name||"unnamed"}`,children:[M(ut,{palette:t,items:Nw(r)},void 0,!1,void 0,this),M("box",{flexGrow:1,overflow:"hidden",children:M("scrollbox",{ref:e.scrollRef,focused:!1,stickyScroll:!1,scrollX:!1,style:Ue(t),children:M("box",{flexDirection:"column",overflow:"hidden",gap:nk,children:[M(Pf,{wide:a,left:M(qr,{palette:t,title:"project",children:M(bi,{palette:t,facts:Hw(r,jt),width:c},void 0,!1,void 0,this)},void 0,!1,void 0,this),right:M(qr,{palette:t,title:"google",children:M(bi,{palette:t,facts:jw(r),width:c},void 0,!1,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this),M(Pf,{wide:a,left:M(qr,{palette:t,title:"runtime",children:M(bi,{palette:t,facts:Bw(r),width:c},void 0,!1,void 0,this)},void 0,!1,void 0,this),right:M(qr,{palette:t,title:"logs",children:M(bi,{palette:t,facts:Uw(r),width:c},void 0,!1,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this),M(qr,{palette:t,title:`proxy ${r.proxy.enabled?"enabled":"disabled"}`,children:[M(bi,{palette:t,facts:Ww(r),width:s},void 0,!1,void 0,this),M(SM,{palette:t,routes:d,width:s},void 0,!1,void 0,this)]},void 0,!0,void 0,this),M(qr,{palette:t,title:`services ${l.length}`,children:M(wM,{palette:t,rows:l,width:s},void 0,!1,void 0,this)},void 0,!1,void 0,this),M(qr,{palette:t,title:`tasks ${p.length}`,children:M(tk,{palette:t,rows:p,empty:"no tasks",width:s,selected:o},void 0,!1,void 0,this)},void 0,!1,void 0,this),M(Pf,{wide:a,left:M(qr,{palette:t,title:`profiles ${u.length}`,children:M(kM,{palette:t,rows:u,width:c},void 0,!1,void 0,this)},void 0,!1,void 0,this),right:M(qr,{palette:t,title:`templates ${f.length}`,children:M(tk,{palette:t,rows:f,empty:"no templates",width:c},void 0,!1,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this),M(qr,{palette:t,title:"extras",children:M(bi,{palette:t,facts:Jw(r),width:s},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this),M(Ut,{palette:t,backgroundColor:t.element,edge:"top",children:M(it,{palette:t,hints:[{key:"v",label:"validate/save buffer"},{key:"e",label:"open in $EDITOR"},{key:"/diff",label:"config sources"},{key:"/reload",label:"re-read .devctl after editing"},{key:"enter",label:"run task"},{key:"j/k",label:"scroll"}]},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function Pf(e){let{wide:t,left:r,right:n}=e;if(!t)return M("box",{flexDirection:"column",overflow:"hidden",gap:nk,flexShrink:0,children:[r,n]},void 0,!0,void 0,this);return M("box",{flexDirection:"row",overflow:"hidden",gap:rk,flexShrink:0,children:[M("box",{flexGrow:1,flexBasis:0,overflow:"hidden",children:r},void 0,!1,void 0,this),M("box",{flexGrow:1,flexBasis:0,overflow:"hidden",children:n},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function qr(e){let{palette:t,title:r,children:n}=e;return M("box",{border:!0,borderStyle:"rounded",borderColor:t.border,title:r,titleColor:t.accent,paddingLeft:1,paddingRight:1,flexDirection:"column",overflow:"hidden",flexShrink:0,children:n},void 0,!1,void 0,this)}function bi(e){let{palette:t,facts:r,width:n}=e,o=Math.max(4,n-vi-4);return M("box",{flexDirection:"column",overflow:"hidden",flexShrink:0,children:r.map((i)=>M("box",{height:1,flexDirection:"row",overflow:"hidden",children:[M("box",{width:vi,flexShrink:0,overflow:"hidden",children:M("text",{fg:t.muted,wrapMode:"none",children:oe(i.label,vi)},void 0,!1,void 0,this)},void 0,!1,void 0,this),M("box",{width:o,flexShrink:0,overflow:"hidden",children:M("text",{fg:_M(t,i.tone),wrapMode:"none",children:oe(i.value,o)},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},i.label,!0,void 0,this))},void 0,!1,void 0,this)}function wM(e){let{palette:t,rows:r,width:n}=e;if(r.length===0)return M(Ll,{palette:t,text:"no services"},void 0,!1,void 0,this);let o=Xw(r),i=Math.max(8,n-o-6);return M("box",{flexDirection:"column",overflow:"hidden",flexShrink:0,children:r.map((s)=>M("box",{height:1,flexDirection:"row",overflow:"hidden",children:[M("box",{width:o,flexShrink:0,overflow:"hidden",children:M("text",{fg:Ze(s.name,t),wrapMode:"none",children:oe(s.name,o)},void 0,!1,void 0,this)},void 0,!1,void 0,this),M("box",{width:i,flexShrink:0,overflow:"hidden",children:M("text",{fg:t.text,wrapMode:"none",children:Re(CM(s),i)},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},s.name,!0,void 0,this))},void 0,!1,void 0,this)}function SM(e){let{palette:t,routes:r,width:n}=e;if(r.length===0)return M(Ll,{palette:t,text:"no routes"},void 0,!1,void 0,this);let o=Math.min(vi+4,Math.max(8,...r.map((s)=>s.name.length))),i=Math.max(8,n-o-6);return M("box",{flexDirection:"column",overflow:"hidden",flexShrink:0,children:r.map((s)=>M("box",{height:1,flexDirection:"row",overflow:"hidden",children:[M("box",{width:o,flexShrink:0,overflow:"hidden",children:M("text",{fg:t.primary,wrapMode:"none",children:oe(s.name,o)},void 0,!1,void 0,this)},void 0,!1,void 0,this),M("box",{width:i,flexShrink:0,overflow:"hidden",children:M("text",{fg:t.text,wrapMode:"none",children:Re(PM(s),i)},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},`${s.name}-${s.match}`,!0,void 0,this))},void 0,!1,void 0,this)}function kM(e){let{palette:t,rows:r,width:n}=e;if(r.length===0)return M(Ll,{palette:t,text:"no profiles"},void 0,!1,void 0,this);let o=Math.min(12,Math.max(8,...r.map((s)=>s.name.length))),i=Math.max(8,n-o-6);return M("box",{flexDirection:"column",overflow:"hidden",flexShrink:0,children:r.map((s)=>M("box",{height:1,flexDirection:"row",overflow:"hidden",children:[M("box",{width:o,flexShrink:0,overflow:"hidden",children:M("text",{fg:t.primary,wrapMode:"none",children:oe(s.name,o)},void 0,!1,void 0,this)},void 0,!1,void 0,this),M("box",{width:i,flexShrink:0,overflow:"hidden",children:M("text",{fg:t.text,wrapMode:"none",children:Re(s.env==="\u2014"?s.services:`${s.services} env ${s.env}`,i)},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},s.name,!0,void 0,this))},void 0,!1,void 0,this)}function tk(e){let{palette:t,rows:r,empty:n,width:o,selected:i}=e;if(r.length===0)return M(Ll,{palette:t,text:n},void 0,!1,void 0,this);let s=Math.min(14,Math.max(8,...r.map((c)=>c.name.length))),a=Math.max(8,o-s-6);return M("box",{flexDirection:"column",overflow:"hidden",flexShrink:0,children:r.map((c,l)=>{let d=i===l;return M("box",{height:1,flexDirection:"row",overflow:"hidden",backgroundColor:d?t.highlight:void 0,children:[M("box",{width:s,flexShrink:0,overflow:"hidden",children:M("text",{fg:d?t.primary:t.text,wrapMode:"none",children:oe(c.name,s)},void 0,!1,void 0,this)},void 0,!1,void 0,this),M("box",{width:a,flexShrink:0,overflow:"hidden",children:M("text",{fg:d?t.accent:t.muted,wrapMode:"none",children:Re(c.summary,a)},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},c.name,!0,void 0,this)})},void 0,!1,void 0,this)}function Ll(e){return M("box",{height:1,overflow:"hidden",children:M("text",{fg:e.palette.muted,wrapMode:"none",children:e.text},void 0,!1,void 0,this)},void 0,!1,void 0,this)}function CM(e){let t=e.extends==="\u2014"?"":` ${e.extends}`;return` ${e.ports} ${e.identity} ${e.health} ${e.restart} \u2190 ${e.depends}${t}`}function PM(e){return` ${e.auth} ${e.identity} ${e.match} \u2192 ${e.upstream}`}function _M(e,t){if(t==="success")return e.success;if(t==="warning")return e.warning;if(t==="error")return e.error;if(t==="muted")return e.muted;return e.text}var rk=1,nk=1;var ik=I(()=>{Yt();gf();Mt();Ot();ke();tt()});import{jsxDEV as Ft}from"@opentui/react/jsx-dev-runtime";function sk(e){let{palette:t,credentials:r}=e;if(!r)return Ft(fl,{palette:t,label:"Reading credential store\u2026"},void 0,!1,void 0,this);let n=r.entries.filter((i)=>i.valid).length,o=r.entries.length-n;return Ft(kt,{palette:t,title:"credentials",scroll:!0,children:[Ft("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,children:[Ft(Be,{palette:t,label:`backend: ${r.backend}`,tone:"info"},void 0,!1,void 0,this),r.entries.length>0?Ft(Be,{palette:t,label:`${n} valid`,tone:"success"},void 0,!1,void 0,this):null,o>0?Ft(Be,{palette:t,label:`${o} expired`,tone:"warning"},void 0,!1,void 0,this):null]},void 0,!0,void 0,this),Ft("text",{fg:t.muted,wrapMode:"word",children:"Tokens stay in the OS keychain or ~/.devctl/credentials (0600). They are never shown here."},void 0,!1,void 0,this),Ft("box",{height:1,flexShrink:0},void 0,!1,void 0,this),r.entries.length===0?Ft("text",{fg:t.muted,children:"no cached credentials"},void 0,!1,void 0,this):Ft("box",{flexDirection:"column",overflow:"hidden",gap:1,children:r.entries.map((i)=>Ft("box",{border:!0,borderStyle:"rounded",borderColor:i.valid?t.success:t.warning,title:i.identity,titleColor:i.valid?t.success:t.warning,flexDirection:"column",flexShrink:0,paddingLeft:1,paddingRight:1,overflow:"hidden",children:[Ft("text",{wrapMode:"none",children:[Ft("span",{fg:i.valid?t.success:t.warning,children:i.valid?"\u2713 valid":"\u2717 expired"},void 0,!1,void 0,this),Ft("span",{fg:t.muted,children:` \xB7 expires ${i.expires_at}`},void 0,!1,void 0,this)]},void 0,!0,void 0,this),Ft("text",{fg:t.muted,wrapMode:"none",children:`audience: ${i.audience||"(none)"}`},void 0,!1,void 0,this)]},`${i.identity}|${i.audience}`,!0,void 0,this))},void 0,!1,void 0,this),Ft(pl,{palette:t,title:"Actions",body:"Use /refresh to mint again or auth refresh on the CLI. Invalidate clears the store without printing tokens.",hint:"/refresh /auth"},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}var ak=I(()=>{Yt();ke()});import{jsxDEV as Dl}from"@opentui/react/jsx-dev-runtime";function xi(e){let t=e.newer>0?`g jump to latest \xB7 ${e.newer} new`:"g jump to latest",r=Math.min(e.width,t.length+4);return Dl("box",{position:"absolute",left:Math.max(0,Math.floor((e.width-r)/2)),bottom:e.bottom??2,width:r,height:3,border:!0,borderStyle:"rounded",borderColor:e.palette.warning,backgroundColor:e.palette.panel,alignItems:"center",justifyContent:"center",onMouseDown:e.onJump,children:Dl("text",{wrapMode:"none",children:[Dl("span",{fg:e.palette.primary,children:"g"},void 0,!1,void 0,this),Dl("span",{fg:e.palette.text,children:t.slice(1)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this)}var ck=()=>{};import{jsxDEV as Hs}from"@opentui/react/jsx-dev-runtime";function wi(e){let{palette:t,logs:r,names:n,service:o,errorOnly:i,width:s,onService:a,onToggleErrors:c,facets:l}=e,d=l?Yb(n,l):zb(n,r),u=s<80,f=d.map((v)=>{let b=v.name===""?"all":v.name;return{id:b,label:u?b:`${b} \xB7 ${v.count}`,color:Ze(v.name,t)}}),p=Math.max(0,d.findIndex((v)=>v.name===o)),m=i?"ERROR+":u?"lvls":"all levels",h=Math.max(ci(f[p]?.label??"all"),s-ci(m));return Hs(Ut,{palette:t,backgroundColor:t.element,children:Hs("box",{height:1,flexDirection:"row",overflow:"hidden",backgroundColor:t.element,children:[Hs("box",{flexGrow:1,overflow:"hidden",children:Hs(ul,{palette:t,items:f,active:p,width:h,emphasis:"fill",onPick:(v)=>{let b=d[v];if(b)a(b.name)}},void 0,!1,void 0,this)},void 0,!1,void 0,this),Hs(Be,{palette:t,label:m,tone:i?"error":"muted",onMouseDown:c},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this)}var lk=I(()=>{gr();In();ke();tt()});import{jsxDEV as RM}from"@opentui/react/jsx-dev-runtime";function js(e){let t=Math.min(e.total,e.start+e.count),r=Math.max(0,e.start),n=Math.max(0,e.total-t);return RM(ut,{palette:e.palette,items:[{text:`view ${e.start+1}\u2013${t} of ${e.total}`,tone:"primary"},{text:r>0?`\u2191 ${r} older`:"start of history",tone:r>0?"info":"idle"},{text:n>0?`\u2193 ${n} newer`:"at latest",tone:n>0?"warning":"success"}],hints:[{key:"pgup/pgdn",label:"move history window"},{key:"g",label:"latest"}]},void 0,!1,void 0,this)}var dk=I(()=>{ke()});import{useEffect as _f,useRef as uk}from"react";import{jsxDEV as Ye}from"@opentui/react/jsx-dev-runtime";function Si(e){let{palette:t,logs:r,limit:n,width:o=80,followTick:i=0,focused:s=!1,wrapMode:a="clip",selected:c=-1,follow:l=!0,showTimestamps:d=!0,showMeta:u=!0,onPick:f,onLeaveLatest:p,viewStart:m=0,search:h="",regex:v=!1}=e,b=uk(null),g=uk(""),_=n===void 0?r:r.slice(-n),R=[...new Set(r.map((k)=>k.service))],S=Ub(o,R),y=Bb({width:o,serviceWidth:S,showTimestamps:d,showMeta:u}),x=_[_.length-1],w=x===void 0?"empty":String(x.seq);return _f(()=>{let k=b.current;if(!k)return;if(k.stickyScroll=l,l)k.stickyStart="bottom",k.scrollTo({x:k.scrollLeft,y:Math.max(0,k.scrollHeight)})},[l,i,w]),_f(()=>{let k=`${m}:${c}:${a}`;if(g.current===k)return;if(g.current=k,l&&(c<0||c>=_.length-1))return;let D=b.current;if(!D||c<0)return;let O=`log-row-${c}`,F=requestAnimationFrame(()=>{D.stickyScroll=!1,D.scrollChildIntoView(O)});return()=>cancelAnimationFrame(F)},[l,c,_.length,m,a]),_f(()=>{if(!l||!p)return;let k=!1,D=0,O=setInterval(()=>{let F=b.current;if(!F)return;let Z=F.viewport.height;if(Z<=0)return;let W=F.scrollHeight;if(F.scrollTop+Z>=W-TM){k=!0,D=W;return}if(W>D){F.stickyScroll=!0,F.stickyStart="bottom",F.scrollTo({x:F.scrollLeft,y:Math.max(0,W)}),D=W;return}if(k)p()},EM);return()=>{clearInterval(O)}},[l,i,p]),Ye("box",{flexGrow:1,height:"100%",overflow:"hidden",children:Ye("scrollbox",{ref:b,focused:s,stickyScroll:l,stickyStart:"bottom",scrollX:!1,style:{rootOptions:{flexGrow:1,height:"100%",overflow:"hidden",backgroundColor:t.panel},viewportOptions:{backgroundColor:t.panel},contentOptions:{backgroundColor:t.panel},scrollbarOptions:{trackOptions:{foregroundColor:t.primary,backgroundColor:t.element}}},children:Ye("box",{flexDirection:"column",overflow:"hidden",children:_.map((k,D)=>Ye(AM,{id:`log-row-${D}`,palette:t,event:k,serviceWidth:S,msgWidth:y,expanded:Hb(a,D===c),active:D===c,showTimestamps:d,showMeta:u,onPick:f?()=>f(D):void 0,search:h,regex:v},`${k.timestamp}-${k.service}-${D}`,!1,void 0,this))},void 0,!1,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this)}function AM(e){let{id:t,palette:r,event:n,serviceWidth:o,msgWidth:i,expanded:s,active:a,showTimestamps:c=!0,showMeta:l=!0,onPick:d,search:u="",regex:f=!1}=e,p=Nb(Xc(n.message,i),i,s);return Ye("box",{id:t,flexShrink:0,flexDirection:"column",overflow:"hidden",backgroundColor:a?r.highlight:void 0,onMouseDown:d,children:p.visible.map((m,h)=>Ye("box",{height:1,flexDirection:"row",overflow:"hidden",children:[c?Ye("box",{width:Vc,flexShrink:0,overflow:"hidden",children:Ye("text",{fg:r.muted,children:h===0?n.timestamp.slice(11,19):""},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,Ye("box",{width:o,flexShrink:0,overflow:"hidden",children:Ye("text",{fg:h===0?Ze(n.service,r):r.muted,wrapMode:"none",children:h===0?oe(n.service,o):""},void 0,!1,void 0,this)},void 0,!1,void 0,this),Ye("box",{width:As,flexShrink:0,overflow:"hidden",children:Ye("text",{children:" "},void 0,!1,void 0,this)},void 0,!1,void 0,this),Ye("box",{width:zc,flexShrink:0,overflow:"hidden",children:Ye("text",{fg:h===0?et(r,n.level):r.muted,children:h===0?Yc(String(n.level)):""},void 0,!1,void 0,this)},void 0,!1,void 0,this),l?Ye("box",{width:qc,flexShrink:0,overflow:"hidden",children:Ye("text",{fg:r.muted,children:h===0&&n.source?oe(n.source,qc):""},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,Ye("box",{width:As,flexShrink:0,overflow:"hidden",children:Ye("text",{fg:r.muted,children:h===0?" ":"\u2502"},void 0,!1,void 0,this)},void 0,!1,void 0,this),Ye("box",{flexGrow:1,overflow:"hidden",children:Ye(LM,{palette:r,level:n.level,text:m,mark:h===0?p.mark:"",search:u,regex:f},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},`${n.timestamp}-${h}`,!0,void 0,this))},void 0,!1,void 0,this)}function LM(e){let{palette:t,level:r,text:n,mark:o,search:i="",regex:s=!1}=e;return Ye("text",{wrapMode:"none",children:[jb(n,i,s).map((a,c)=>Ye("span",{fg:mb(t,r,a.kind),children:a.text},`${a.kind}-${c}`,!1,void 0,this)),o===""?null:Ye("span",{fg:t.muted,children:o},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}var EM=200,TM=2;var pk=I(()=>{Ot();gr();tt()});var Rf=I(()=>{ck();lk();dk();pk()});import{useRef as DM}from"react";import{jsxDEV as Ie}from"@opentui/react/jsx-dev-runtime";function Il(e){let{palette:t,names:r,snap:n,selected:o,checked:i,width:s,onOpen:a,onSelectIndex:c,onToggle:l}=e,d=ye(),u=DM(!1),f=new Set(i),p=_p(s),m=Pp(s),h=Cp(s),v=Eb(s);return Ie("box",{flexDirection:"column",flexGrow:1,overflow:"hidden",children:[Ie("box",{height:1,flexDirection:"row",overflow:"hidden",children:[Ie(Gt,{width:2,fg:t.muted,text:""},void 0,!1,void 0,this),Ie(Gt,{width:4,fg:t.accent,text:"sel"},void 0,!1,void 0,this),Ie(Gt,{width:2,fg:t.muted,text:""},void 0,!1,void 0,this),Ie(Gt,{width:v,fg:t.muted,text:"name"},void 0,!1,void 0,this),Ie(Gt,{width:$t,fg:t.muted,text:""},void 0,!1,void 0,this),Ie(Gt,{width:Tr,fg:t.muted,text:"state"},void 0,!1,void 0,this),h?Ie(Gt,{width:Ur,fg:t.muted,text:"health"},void 0,!1,void 0,this):null,m?Ie(Gt,{width:$c,fg:t.muted,text:"port"},void 0,!1,void 0,this):null,p?Ie(Gt,{width:an,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:an,fg:t.text,text:_?.pid?String(_.pid):""},void 0,!1,void 0,this):null]},b,!0,void 0,this)})]},void 0,!0,void 0,this)}function Ml(e){let{palette:t,checked:r}=e,n=r.length;if(n>0)return Ie(ut,{palette:t,items:[{text:`${n} selected`,tone:"primary"}],hints:[{key:"space",label:"unmark"},{key:"n",label:"start these"},{key:"x",label:"stop these"}]},void 0,!1,void 0,this);return null}function IM(e,t,r){if(t)return e.success;if(r)return e.primary;return e.muted}function Gt(e){return Ie("box",{width:e.width,flexShrink:0,overflow:"hidden",children:Ie("text",{fg:e.fg,wrapMode:"none",children:e.text},void 0,!1,void 0,this)},void 0,!1,void 0,this)}var Ef=I(()=>{Mt();Ot();Lt();ke();tt()});import{jsxDEV as fe}from"@opentui/react/jsx-dev-runtime";function mk(e){let{palette:t,cfg:r,snap:n,logs:o,names:i,selected:s,selectedLog:a=-1,checked:c,width:l,followTick:d,logService:u,logSources:f,errorOnly:p,showSystemLogs:m,onOpen:h,onSelectIndex:v,onToggle:b,onFilterService:g,onToggleErrors:_,onShowErrors:R,wrapMode:S="clip",view:y,follow:x=!0,onLeaveLatest:w,onPickLog:k,viewStart:D=0,viewTotal:O,newer:F=0,onJumpLatest:Z,facets:W,leftover:C,search:j="",regex:B=!1}=e,z=ye();if(!r||i.length===0)return fe(ze,{palette:t,title:"No services configured",body:"Add services to .devctl/config.yaml or finish setup.",hint:"open the setup screen with /setup"},void 0,!1,void 0,this);let de=l<li,Ee=jc(n),Ne=cl(n,i),ne=i.filter((ge)=>n?.services[ge]?.state==="FAILED").length,H=Vb(o),ae=Ee?Lb(C,n?.session_id):void 0,Q=Hc(l,i,de),nt=Math.max(24,de?l-4:l-Q-4),ft=m?o:o.filter((ge)=>!ai(ge.source)),ie=yo(o,{service:u,errorOnly:p,systemLogs:m,search:j,regex:B}),Le=y??ie,Me=O??ie.length,Ke=Math.min(Me,D+Le.length),gt=Me===0?"empty":`${D+1}\u2013${Ke} of ${Me}`,Xe=u===""?"all":u,Ct=[...Ne.running>0?[{text:`${Ne.running}/${i.length}`,tone:"success"}]:[],...ne>0?[{text:`${ne} failed`,tone:"error"}]:[],...c.length>0?[{text:`${c.length} selected`,tone:"primary"}]:[],...H>0?[{text:`${H} errors`,tone:"error",onMouseDown:R}]:[]];return fe("box",{flexGrow:1,flexDirection:de?"column":"row",overflow:"hidden",children:[fe("box",{flexGrow:de?1:0,flexShrink:0,minWidth:de?void 0:Q,width:de?void 0:Q,border:!0,borderStyle:"rounded",borderColor:Ee?t.border:t.borderActive,backgroundColor:t.panel,title:"services",titleColor:t.primary,flexDirection:"column",overflow:"hidden",children:[Ct.length>0?fe(ut,{palette:t,items:Ct},void 0,!1,void 0,this):null,fe(Ml,{palette:t,checked:c},void 0,!1,void 0,this),fe("box",{flexGrow:1,paddingLeft:z.pad,paddingRight:z.pad,overflow:"hidden",children:fe(Il,{palette:t,names:i,snap:n,selected:s,checked:c,width:Nc(Q,z.pad),onOpen:h,onSelectIndex:v,onToggle:b},void 0,!1,void 0,this)},void 0,!1,void 0,this),fe(FM,{palette:t,names:i,snap:n,width:Q,onOpen:h},void 0,!1,void 0,this),ae?fe(HM,{palette:t,leftover:ae,width:Q},void 0,!1,void 0,this):null]},void 0,!0,void 0,this),fe("box",{position:"relative",flexGrow:2,minWidth:de?void 0:32,minHeight:de?8:void 0,border:!0,borderStyle:"rounded",borderColor:t.border,backgroundColor:t.panel,title:`logs \xB7 ${Xe} \xB7 ${gt}`,titleColor:t.primary,overflow:"hidden",flexDirection:"column",children:[fe(wi,{palette:t,logs:ft,names:f??i,service:u,errorOnly:p,width:nt,onService:g,onToggleErrors:_,facets:W},void 0,!1,void 0,this),Me>Le.length?fe(js,{palette:t,start:D,count:Le.length,total:Me},void 0,!1,void 0,this):null,ie.length===0?fe(ze,{palette:t,title:Ee?"Waiting for logs":o.length===0?"No events yet":"No events in this filter",body:Ee?"Start services to stream output here. Later starts keep earlier logs.":o.length===0?"New lines appear as services write output.":"Click All or another service chip, or turn off ERROR+.",hint:"\u2190 \u2192 cycle service e errors g latest"},void 0,!1,void 0,this):fe(Si,{palette:t,logs:Le,width:nt,followTick:d,focused:!1,wrapMode:S,selected:a,follow:x,onLeaveLatest:w,onPick:k,viewStart:D,search:j,regex:B},d,!1,void 0,this),!x?fe(xi,{palette:t,width:nt,newer:F,bottom:0,onJump:Z},void 0,!1,void 0,this):null]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)}function OM(e){if(!e)return!1;return e.state===dt||e.health===Oe||e.state===fr||e.restarts>0||e.last_error!==""}function fk(e){if(e.state===dt)return 0;if(e.health===Oe)return 1;if(e.state===fr)return 2;return 3}function $M(e){if(e.last_error!=="")return e.last_error;if(e.restarts>0)return`restarted ${e.restarts}x, no error recorded`;return e.health===Oe?"failing health check":e.state.toLowerCase()}function FM(e){let{palette:t,names:r,snap:n,width:o,onOpen:i}=e,s=r.map((d)=>({name:d,rt:n?.services[d]})).filter((d)=>OM(d.rt)).sort((d,u)=>fk(d.rt)-fk(u.rt));if(s.length===0)return null;let a=s.slice(0,MM),c=s.length-a.length,l=Math.max(8,o-Tf-4);return fe("box",{flexDirection:"column",flexShrink:0,paddingLeft:1,paddingRight:1,overflow:"hidden",children:[fe("box",{height:1,overflow:"hidden",children:fe("text",{fg:t.error,wrapMode:"none",children:`issues (${s.length})`},void 0,!1,void 0,this)},void 0,!1,void 0,this),a.map(({name:d,rt:u})=>{let f=tr(u);return fe("box",{height:1,flexDirection:"row",overflow:"hidden",onMouseDown:()=>i(d),children:[fe("box",{width:2,flexShrink:0,children:fe("text",{fg:et(t,f),children:hr(f)},void 0,!1,void 0,this)},void 0,!1,void 0,this),fe("box",{width:Tf,flexShrink:0,overflow:"hidden",children:fe("text",{fg:Ze(d,t),wrapMode:"none",children:oe(d,Tf)},void 0,!1,void 0,this)},void 0,!1,void 0,this),fe("box",{width:l,flexShrink:0,overflow:"hidden",children:fe("text",{fg:t.muted,wrapMode:"none",children:Re($M(u),l)},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},d,!0,void 0,this)}),c>0?fe("text",{fg:t.muted,wrapMode:"none",children:`+${c} more \u2014 open a service to see details`},void 0,!1,void 0,this):null]},void 0,!0,void 0,this)}function HM(e){let{palette:t,leftover:r,width:n}=e,o=r.processes.slice(0,NM),i=r.processes.length-o.length,s=Math.max(8,n-Af-4);return fe("box",{flexDirection:"column",flexShrink:0,paddingLeft:1,paddingRight:1,overflow:"hidden",children:[fe("box",{height:1,overflow:"hidden",children:fe("text",{fg:t.warning,wrapMode:"none",children:"last session"},void 0,!1,void 0,this)},void 0,!1,void 0,this),fe("text",{fg:t.muted,wrapMode:"none",children:Re(`${r.session_id} ${r.profile||"(none)"}`,Math.max(8,n-4))},void 0,!1,void 0,this),o.map((a)=>fe("box",{height:1,flexDirection:"row",overflow:"hidden",children:[fe("box",{width:Af,flexShrink:0,overflow:"hidden",children:fe("text",{fg:t.text,wrapMode:"none",children:oe(a.name,Af)},void 0,!1,void 0,this)},void 0,!1,void 0,this),fe("box",{width:s,flexShrink:0,overflow:"hidden",children:fe("text",{fg:t.muted,wrapMode:"none",children:Re(`pid ${a.pid} leftover`,s)},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},`${a.name}-${a.pid}`,!0,void 0,this)),i>0?fe("text",{fg:t.muted,wrapMode:"none",children:`+${i} more leftover pids`},void 0,!1,void 0,this):null]},void 0,!0,void 0,this)}var MM=4,Tf=14,NM=4,Af=14;var hk=I(()=>{At();Yt();Mt();vr();Ot();gr();Lt();dl();ke();tt();Rf();Ef()});import jM from"cli-spinners";import{useEffect as BM,useState as UM}from"react";import{jsxDEV as re}from"@opentui/react/jsx-dev-runtime";function gk(e){let{palette:t,report:r,loading:n,progress:o,error:i,selected:s,onPick:a,onReload:c}=e,l=fi(s,Df);if(n)return re(WM,{palette:t,progress:o},void 0,!1,void 0,this);if(i)return re(ew,{palette:t,title:"Doctor failed",body:i},void 0,!1,void 0,this);if(!r)return re(ze,{palette:t,title:"Doctor",body:"Diagnostics have not run yet.",hint:"opening this screen runs doctor"},void 0,!1,void 0,this);let d=r.checks.filter((v)=>v.severity==="ok").length,u=r.checks.filter((v)=>v.severity==="warn").length,f=r.checks.filter((v)=>v.severity==="error").length,p=r.checks.length,m=si(p>0?d/p:0),h=f>0?t.error:u>0?t.warning:t.success;return re(kt,{palette:t,title:`doctor ${r.issues} issue(s)`,children:[re("box",{height:4,flexDirection:"column",flexShrink:0,alignItems:"center",justifyContent:"center",overflow:"hidden",border:!0,borderStyle:"rounded",borderColor:t.primary,backgroundColor:t.element,onMouseDown:c,children:[re("text",{wrapMode:"none",children:[re("span",{fg:t.inverse,bg:t.primary,children:" r "},void 0,!1,void 0,this),re("span",{fg:t.primary,children:" RUN DOCTOR AGAIN"},void 0,!1,void 0,this)]},void 0,!0,void 0,this),re("text",{fg:t.muted,wrapMode:"none",children:"results stay cached until you refresh"},void 0,!1,void 0,this)]},void 0,!0,void 0,this),re("box",{height:1,flexShrink:0},void 0,!1,void 0,this),re("box",{height:1,flexShrink:0,overflow:"hidden",children:re("text",{wrapMode:"none",children:[re("span",{fg:h,children:`[${m}]`},void 0,!1,void 0,this),re("span",{fg:t.success,children:` ${d} ok`},void 0,!1,void 0,this),u>0?re("span",{fg:t.warning,children:` \xB7 ${u} warn`},void 0,!1,void 0,this):null,f>0?re("span",{fg:t.error,children:` \xB7 ${f} error`},void 0,!1,void 0,this):null,u===0&&f===0?re("span",{fg:t.muted,children:" \xB7 all clear"},void 0,!1,void 0,this):null]},void 0,!0,void 0,this)},void 0,!1,void 0,this),re("box",{height:1,flexShrink:0},void 0,!1,void 0,this),re("scrollbox",{ref:l,focused:!1,stickyScroll:!1,scrollX:!1,style:Ue(t),children:re("box",{flexDirection:"column",overflow:"hidden",gap:1,children:r.checks.map((v,b)=>re(GM,{palette:t,check:v,index:b,active:b===s,onPick:a},`${v.name}-${b}`,!1,void 0,this))},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function WM({palette:e,progress:t}){let[r,n]=UM(0);return BM(()=>{let o=setInterval(()=>n((i)=>(i+1)%Lf.frames.length),Lf.interval);return()=>clearInterval(o)},[]),re("box",{flexGrow:1,alignItems:"center",justifyContent:"center",overflow:"hidden",children:re("box",{width:"80%",maxWidth:52,height:10,flexShrink:1,border:!0,borderStyle:"rounded",borderColor:e.borderActive,backgroundColor:e.panel,flexDirection:"column",alignItems:"center",justifyContent:"center",overflow:"hidden",children:[re("text",{wrapMode:"none",children:[re("span",{fg:e.primary,children:`${Lf.frames[r]} `},void 0,!1,void 0,this),re("span",{fg:e.text,children:"devctl doctor"},void 0,!1,void 0,this)]},void 0,!0,void 0,this),re("box",{height:1,flexShrink:0},void 0,!1,void 0,this),re("text",{fg:e.text,wrapMode:"none",children:`Checking ${t.active}`},void 0,!1,void 0,this),re("box",{height:1,flexShrink:0},void 0,!1,void 0,this),t.checks.slice(-4).map((o,i)=>re("text",{wrapMode:"none",children:[re("span",{fg:et(e,o.severity==="error"?"ERROR":o.severity==="warn"?"WARN":"OK"),children:`${hr(o.severity==="error"?"ERROR":o.severity==="warn"?"WARN":"OK")} `},void 0,!1,void 0,this),re("span",{fg:e.muted,children:o.name},void 0,!1,void 0,this)]},`${o.name}-${i}`,!0,void 0,this)),t.checks.length===0?re("text",{fg:e.muted,children:"Waiting for the first result\u2026"},void 0,!1,void 0,this):null]},void 0,!0,void 0,this)},void 0,!1,void 0,this)}function GM(e){let{palette:t,check:r,index:n,active:o,onPick:i}=e,s=r.severity==="error"?"ERROR":r.severity==="warn"?"WARN":"OK";if(r.severity==="ok")return re("box",{id:`${Df}-${n}`,flexDirection:"row",flexShrink:0,paddingLeft:1,overflow:"hidden",backgroundColor:o?t.highlight:void 0,onMouseDown:()=>i(n),children:re("text",{wrapMode:"none",children:[re("span",{fg:t.primary,children:o?"\u203A ":" "},void 0,!1,void 0,this),re("span",{fg:et(t,s),children:hr(s)},void 0,!1,void 0,this),re("span",{fg:o?t.primary:t.text,children:` ${r.name}`},void 0,!1,void 0,this),re("span",{fg:t.muted,children:r.message?` \xB7 ${r.message}`:""},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this);let c=r.severity==="error"?t.error:t.warning;return re("box",{id:`${Df}-${n}`,border:!0,borderStyle:"rounded",borderColor:o?t.borderActive:c,title:r.name,titleColor:c,flexDirection:"column",flexShrink:0,paddingLeft:1,paddingRight:1,overflow:"hidden",backgroundColor:o?t.highlight:void 0,onMouseDown:()=>i(n),children:[re("text",{wrapMode:"word",children:[re("span",{fg:et(t,s),children:`${hr(s)} `},void 0,!1,void 0,this),re("span",{fg:t.text,children:r.message},void 0,!1,void 0,this)]},void 0,!0,void 0,this),r.hint?re("text",{fg:t.info,wrapMode:"word",children:`\u2192 ${r.hint}`},void 0,!1,void 0,this):null,o&&r.action?.kind==="free-port"?re("text",{fg:t.primary,wrapMode:"none",children:`enter stop ${r.action.holder.command} (pid ${r.action.holder.pid})`},void 0,!1,void 0,this):null]},void 0,!0,void 0,this)}var Df="doctor-row",Lf;var vk=I(()=>{Yt();Ot();ke();tt();Lf=jM.bouncingBall});import{jsxDEV as We,Fragment as KM}from"@opentui/react/jsx-dev-runtime";function bk(e){let{palette:t,logs:r,names:n,logSources:o,service:i,errorOnly:s,showSystemLogs:a,search:c,searchFocused:l,followTick:d,width:u,onSearch:f,onService:p,onToggleErrors:m,onSelect:h,wrapMode:v="focus",selected:b=-1,follow:g=!0,newer:_=0,viewStart:R=0,viewTotal:S,onLeaveLatest:y,onJumpLatest:x,view:w,fullscreen:k=!1,facets:D,split:O=!1,splitFocus:F=0,serviceB:Z="",viewB:W,selectedB:C=-1,followB:j=!0,newerB:B=0,viewStartB:z=0,viewTotalB:de,onServiceB:Ee,onSelectB:Ne,onLeaveLatestB:ne,onFocusPane:H}=e,ae=ye(),Q=Ep(u,ae.pad,k),nt=a?r:r.filter((ge)=>!ai(ge.source)),ft=yo(r,{service:i,services:e.services,errorOnly:s,search:c,regex:e.regex,source:e.source,systemLogs:a}),ie=w??ft,Le=S??ft.length,Me=Math.min(Le,R+ie.length),Ke=Le===0?"empty":`${R+1}\u2013${Me} of ${Le}`,gt=e.services??[],Xe=gt.length>0?gt.join(","):i===""?"all services":i,Ct=[...g?[]:[{text:_>0?`${O?"left ":""}pinned \xB7 +${_} new`:`${O?"left ":""}pinned`,tone:"warning"}],...O&&!j?[{text:B>0?`right pinned \xB7 +${B} new`:"right pinned",tone:"warning"}]:[],...e.source?[{text:`src ${e.source}`,tone:"info"}]:[],...e.regex?[{text:"regex",tone:"accent"}]:[],...c===""?[]:[{text:`search ${c}`,tone:"accent"}],...O?[{text:F===0?"left pane":"right pane",tone:"accent"}]:[]];if(O){let ge=Math.max(20,Math.floor(u/2)),vt=Math.max(20,u-ge),at=W??[],yt=de??at.length;return We("box",{flexGrow:1,flexDirection:"column",overflow:"hidden",children:[l?We("box",{height:1,paddingLeft:1,backgroundColor:t.highlight,overflow:"hidden",children:We("input",{focused:!0,value:c,placeholder:"search messages or service names",onInput:f,backgroundColor:t.highlight,focusedBackgroundColor:t.highlight,textColor:t.text,cursorColor:t.primary},void 0,!1,void 0,this)},void 0,!1,void 0,this):We(ut,{palette:t,items:Ct},void 0,!1,void 0,this),We("box",{flexGrow:1,flexDirection:"row",overflow:"hidden",children:[We(yk,{palette:t,names:o??n,logs:r,service:i,errorOnly:s,showSystemLogs:a,search:c,regex:e.regex,source:e.source,width:ge,shown:ie,shownTotal:Le,selected:b,follow:g,newer:_,viewStart:R,wrapMode:v,showTimestamps:e.showTimestamps!==!1,showMeta:e.showMeta!==!1,followTick:d,facets:D,focused:F===0,onService:p,onToggleErrors:m,onSelect:h,onLeaveLatest:y,onFocus:()=>H?.(0)},void 0,!1,void 0,this),We(yk,{palette:t,names:o??n,logs:r,service:Z,errorOnly:s,showSystemLogs:a,search:c,regex:e.regex,source:e.source,width:vt,shown:at,shownTotal:yt,selected:C,follow:j,newer:B,viewStart:z,wrapMode:v,showTimestamps:e.showTimestamps!==!1,showMeta:e.showMeta!==!1,followTick:d,facets:D,focused:F===1,onService:Ee??p,onToggleErrors:m,onSelect:Ne,onLeaveLatest:ne,onFocus:()=>H?.(1)},void 0,!1,void 0,this)]},void 0,!0,void 0,this),!g||!j?We(xi,{palette:t,width:u,newer:Math.max(_,B),onJump:x},void 0,!1,void 0,this):null]},void 0,!0,void 0,this)}return We("box",{flexGrow:1,flexDirection:"column",overflow:"hidden",children:[k?null:We(KM,{children:[Ct.length>0?We(ut,{palette:t,items:Ct},void 0,!1,void 0,this):null,We(wi,{palette:t,logs:nt,names:o??n,service:i,errorOnly:s,width:u,onService:p,onToggleErrors:m,facets:D},void 0,!1,void 0,this)]},void 0,!0,void 0,this),l?We("box",{height:1,paddingLeft:1,backgroundColor:t.highlight,overflow:"hidden",children:We("input",{focused:!0,value:c,placeholder:"search messages or service names",onInput:f,backgroundColor:t.highlight,focusedBackgroundColor:t.highlight,textColor:t.text,cursorColor:t.primary},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,w&&Le>ie.length?We(js,{palette:t,start:R,count:ie.length,total:Le},void 0,!1,void 0,this):null,We("box",{flexGrow:1,border:!k,borderStyle:"rounded",borderColor:t.border,title:k?void 0:`logs ${Xe} \xB7 ${Ke}`,titleColor:t.primary,padding:k?0:ae.pad,overflow:"hidden",children:ie.length===0?We(ze,{palette:t,title:r.length===0?"No log events":"No events in this filter",body:r.length===0?"Start services to stream logs.":"Pick All, another service, or clear search / ERROR+.",hint:`\u2190 \u2192 cycle filters e errors i internal ${It("l")} clear`},void 0,!1,void 0,this):We(Si,{palette:t,logs:ie,width:Q,followTick:d,focused:!1,wrapMode:v,selected:b,follow:g,showTimestamps:e.showTimestamps!==!1,showMeta:e.showMeta!==!1,onPick:h,onLeaveLatest:y,viewStart:R,search:c,regex:e.regex===!0},d,!1,void 0,this)},void 0,!1,void 0,this),!g?We(xi,{palette:t,width:u,newer:_,onJump:x},void 0,!1,void 0,this):null]},void 0,!0,void 0,this)}function yk(e){let{palette:t,width:r,service:n,shown:o,shownTotal:i,viewStart:s,focused:a}=e,c=e.showSystemLogs?e.logs:e.logs.filter((u)=>!ai(u.source)),l=n===""?"all services":n,d=i===0?"empty":`${s+1}\u2013${Math.min(i,s+o.length)} of ${i}`;return We("box",{width:r,flexGrow:0,flexShrink:0,flexDirection:"column",overflow:"hidden",onMouseDown:e.onFocus,children:[We(wi,{palette:t,logs:c,names:e.names,service:n,errorOnly:e.errorOnly,width:r,onService:(u)=>{e.onFocus(),e.onService(u)},onToggleErrors:()=>{e.onFocus(),e.onToggleErrors()},facets:e.facets},void 0,!1,void 0,this),We("box",{flexGrow:1,border:!0,borderStyle:"rounded",borderColor:a?t.borderActive:t.border,title:`logs ${l} \xB7 ${d}`,titleColor:a?t.primary:t.muted,overflow:"hidden",children:o.length===0?We(ze,{palette:t,title:"No events in this pane",body:"Click the pane, then \u2190 \u2192 to pick a service.",hint:"\\\\ split | focus"},void 0,!1,void 0,this):We(Si,{palette:t,logs:o,width:Ep(r,0,!1),followTick:e.followTick,focused:!1,wrapMode:e.wrapMode,selected:e.selected,follow:e.follow,showTimestamps:e.showTimestamps,showMeta:e.showMeta,onPick:(u)=>{e.onFocus(),e.onSelect?.(u)},onLeaveLatest:e.onLeaveLatest,viewStart:s,search:e.search,regex:e.regex===!0},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}var xk=I(()=>{Rf();Yt();Mt();gr();ke();Rr()});import{jsxDEV as Ge}from"@opentui/react/jsx-dev-runtime";function Sk(e){let{palette:t,cfg:r,snap:n,profile:o,selected:i,onPick:s}=e,a=ye(),c=fi(i,wk),l=Object.keys(r?.profiles??{}).sort();if(l.length===0)return Ge(ze,{palette:t,title:"No profiles",body:"Add profiles.<name>.services in config.yaml."},void 0,!1,void 0,this);return Ge(kt,{palette:t,title:"profiles",children:[Ge("box",{height:1,flexShrink:0,overflow:"hidden",children:Ge("text",{fg:t.muted,wrapMode:"none",children:[`${l.length} profile${l.length===1?"":"s"} \xB7 current: `,Ge("span",{fg:t.success,children:o||"(none)"},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this),Ge("box",{height:1,flexShrink:0},void 0,!1,void 0,this),Ge("scrollbox",{ref:c,focused:!1,stickyScroll:!1,scrollX:!1,style:Ue(t),children:Ge("box",{flexDirection:"column",overflow:"hidden",gap:a.gap||1,children:l.map((d,u)=>{let f=d===o,p=u===i,m=r?.profiles[d]?.services??[],h=Object.keys(r?.profiles[d]?.environment??{}).length,v=p?t.borderActive:f?t.success:t.border,b=p?t.primary:f?t.success:t.muted;return Ge("box",{id:`${wk}-${u}`,border:!0,borderStyle:"rounded",borderColor:v,title:d,titleColor:b,flexDirection:"column",flexShrink:0,paddingLeft:1,paddingRight:1,overflow:"hidden",backgroundColor:p?t.highlight:void 0,onMouseDown:()=>s(u),children:[Ge("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,children:Ge("text",{wrapMode:"none",children:[f?Ge("span",{fg:t.success,children:"\u25CF current profile"},void 0,!1,void 0,this):Ge("span",{fg:t.muted,children:"\u25CB not active"},void 0,!1,void 0,this),Ge("span",{fg:t.muted,children:` \xB7 ${m.length} service${m.length===1?"":"s"}`},void 0,!1,void 0,this),h>0?Ge("span",{fg:t.muted,children:` \xB7 ${h} env var${h===1?"":"s"}`},void 0,!1,void 0,this):null]},void 0,!0,void 0,this)},void 0,!1,void 0,this),Ge("box",{flexDirection:"row",overflow:"hidden",flexShrink:0,children:m.length===0?Ge("text",{fg:t.muted,wrapMode:"none",children:"(no services)"},void 0,!1,void 0,this):Ge("text",{wrapMode:"word",children:m.map((g,_)=>{let R=tr(n?.services[g]);return Ge("span",{children:[Ge("span",{fg:et(t,R),children:hr(R)},void 0,!1,void 0,this),Ge("span",{fg:Ze(g,t),children:` ${g}`},void 0,!1,void 0,this),_<m.length-1?Ge("span",{fg:t.muted,children:" "},void 0,!1,void 0,this):null]},g,!0,void 0,this)})},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},d,!0,void 0,this)})},void 0,!1,void 0,this)},void 0,!1,void 0,this),Ge(Ut,{palette:t,backgroundColor:t.element,edge:"top",children:Ge(it,{palette:t,hints:[{key:"space",label:"set current"},{key:"enter",label:"set and start"}]},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}var wk="profile-row";var kk=I(()=>{Yt();Mt();Lt();ke();tt()});import{jsxDEV as V,Fragment as Rk}from"@opentui/react/jsx-dev-runtime";function qM(e){let t=e.toLowerCase();if(t==="iap")return"info";if(t==="service"||t==="service_account")return"primary";return"idle"}function VM(e,t,r){if(r||t>=500)return e.error;if(t===0||t>=400)return e.warning;return e.success}function zM(e,t){if(t.startsWith("sa:"))return{label:"SA",color:e.primary};if(t!=="")return{label:"USR",color:e.info};return{label:"\u2014",color:e.muted}}function QM(e){let{palette:t,req:r,cols:n}=e,o=VM(t,r.status,r.error),i=r.status>0?String(r.status):"ERR",s=zM(t,r.identity),a=r.route||Ek,c=r.error?`${r.path} \u2014 ${Re(r.error,XM)}`:r.path;return V("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,children:[V("box",{width:If,flexShrink:0,overflow:"hidden",children:V("text",{fg:t.muted,children:r.timestamp.slice(11,19)},void 0,!1,void 0,this)},void 0,!1,void 0,this),n.showMethod?V("box",{width:Ol,flexShrink:0,overflow:"hidden",children:V("text",{fg:t.text,wrapMode:"none",children:oe(r.method,Ol)},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,V("box",{width:$l,flexShrink:0,overflow:"hidden",children:V("text",{fg:o,wrapMode:"none",children:oe(i,$l)},void 0,!1,void 0,this)},void 0,!1,void 0,this),n.showDur?V("box",{width:Fl,flexShrink:0,overflow:"hidden",children:V("text",{fg:t.muted,wrapMode:"none",children:oe(`${r.durationMs}ms`,Fl)},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,V("box",{width:Nl,flexShrink:0,overflow:"hidden",children:V("text",{fg:s.color,wrapMode:"none",children:oe(s.label,Nl)},void 0,!1,void 0,this)},void 0,!1,void 0,this),V("box",{width:n.routeCol,flexShrink:0,overflow:"hidden",children:V("text",{fg:r.route?t.info:t.muted,wrapMode:"none",children:oe(a,n.routeCol)},void 0,!1,void 0,this)},void 0,!1,void 0,this),V("box",{flexGrow:1,overflow:"hidden",children:V("text",{fg:r.error?t.error:t.text,wrapMode:"none",children:c},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function ZM(e){let{palette:t,cols:r}=e;return V("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,backgroundColor:t.element,children:[V("box",{width:If,flexShrink:0,overflow:"hidden",children:V("text",{fg:t.muted,children:oe("TIME",If)},void 0,!1,void 0,this)},void 0,!1,void 0,this),r.showMethod?V("box",{width:Ol,flexShrink:0,overflow:"hidden",children:V("text",{fg:t.muted,children:oe("METHOD",Ol)},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,V("box",{width:$l,flexShrink:0,overflow:"hidden",children:V("text",{fg:t.muted,children:oe("ST",$l)},void 0,!1,void 0,this)},void 0,!1,void 0,this),r.showDur?V("box",{width:Fl,flexShrink:0,overflow:"hidden",children:V("text",{fg:t.muted,children:oe("DUR",Fl)},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,V("box",{width:Nl,flexShrink:0,overflow:"hidden",children:V("text",{fg:t.muted,children:oe("WHO",Nl)},void 0,!1,void 0,this)},void 0,!1,void 0,this),V("box",{width:r.routeCol,flexShrink:0,overflow:"hidden",children:V("text",{fg:t.muted,children:oe("ROUTE",r.routeCol)},void 0,!1,void 0,this)},void 0,!1,void 0,this),V("box",{flexGrow:1,overflow:"hidden",children:V("text",{fg:t.muted,children:"PATH"},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function eO(e){let{palette:t,name:r,auth:n,identity:o,upstream:i,match:s,onMouseDown:a}=e;return V("box",{flexDirection:"column",flexShrink:0,overflow:"hidden",onMouseDown:a,children:[V("text",{fg:t.text,wrapMode:"none",children:r},void 0,!1,void 0,this),V("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,children:[V(Be,{palette:t,label:n||"no auth",tone:qM(n)},void 0,!1,void 0,this),o?V(Be,{palette:t,label:o,tone:"idle"},void 0,!1,void 0,this):null]},void 0,!0,void 0,this),s?V("text",{fg:t.muted,wrapMode:"word",children:`match: ${s}`},void 0,!1,void 0,this):null,V("text",{fg:t.muted,wrapMode:"word",children:`\u2192 ${i}`},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function Tk(e){let{palette:t,cfg:r,snap:n,width:o,onSelectRoute:i}=e,s=n?.proxy.routes??[],a=new Map((r?.proxy.routes??[]).map((S)=>[S.name,S])),c=new Map((r?.proxy.routes??[]).map((S)=>[S.name,S.match])),l=n?.proxy.recentRequests??[],d=n?.proxy.requestTotal??0,u=n?.proxy.requestErrors??0,f=o<li,p=Math.max(Ck,Math.min(tO,Math.floor(o*0.36))),m=f?o-Pk:o-p-Pk,h=Math.max(20,m-2),v=h>=_k,b=h>=rO,g=h>=_k?nO:oO,_=l.reduce((S,y)=>Math.min(g,Math.max(S,(y.route||Ek).length+JM)),YM),R={showMethod:v,showDur:b,routeCol:_};return V(kt,{palette:t,title:"proxy",children:[V(ut,{palette:t,items:[{text:n?.proxy.running?"RUNNING":"STOPPED",tone:n?.proxy.running?"success":"idle"},...n?.proxy.address?[{text:n.proxy.address,tone:"info"}]:[],{text:`${s.length} routes`,tone:s.length>0?"primary":"idle"},...d>0?[{text:`${d} requests`,tone:"idle"},...u>0?[{text:`${u} errors`,tone:"warning"}]:[]]:[]]},void 0,!1,void 0,this),V("box",{flexGrow:1,flexDirection:f?"column":"row",overflow:"hidden",children:[V("box",{flexGrow:f?1:0,flexBasis:f?0:void 0,flexShrink:0,minWidth:f?void 0:Ck,width:f?void 0:p,minHeight:f?8:void 0,border:!0,borderStyle:"rounded",borderColor:t.border,title:"routes",titleColor:t.primary,paddingLeft:1,paddingRight:1,flexDirection:"column",overflow:"hidden",children:s.length===0?V(ze,{palette:t,title:"No proxy routes",body:"Add routes under proxy.routes or .devctl/proxy/routes.yaml."},void 0,!1,void 0,this):V(Rk,{children:[i?V("text",{fg:t.muted,wrapMode:"word",children:"click a route for full details"},void 0,!1,void 0,this):null,V("scrollbox",{focused:!1,stickyScroll:!1,scrollX:!1,style:Ue(t),children:V("box",{flexDirection:"column",overflow:"hidden",gap:1,children:s.map((S)=>{let y=c.get(S.name),x=y&&(y.host||y.path)?`${y.host||"*"}${y.path?y.path:""}`:void 0,w=a.get(S.name);return V(eO,{palette:t,name:S.name,auth:S.auth,identity:S.identity,upstream:S.upstream,match:x,onMouseDown:i?()=>i({name:S.name,authType:w?.auth.type||S.auth,identityType:w?.auth.identity.type??"",serviceAccount:w?.auth.identity.service_account||w?.auth.service_account||"",audience:w?.auth.audience??"",clientId:w?.auth.client_id??"",clientSecret:Ba(w?.auth.client_secret??"")??((w?.auth.client_secret??"").trim()?"inline":""),matchHost:w?.match.host??"",matchPath:w?.match.path??"",upstream:S.upstream}):void 0},S.name,!1,void 0,this)})},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this),V("box",{flexGrow:1,flexBasis:0,minWidth:f?void 0:30,minHeight:f?10:void 0,border:!0,borderStyle:"rounded",borderColor:t.borderActive,title:"requests",titleColor:t.primary,flexDirection:"column",overflow:"hidden",children:l.length===0?V("box",{paddingLeft:1,paddingRight:1,children:V("text",{fg:t.muted,wrapMode:"word",children:n?.proxy.running?"No requests seen yet. Send one through the proxy to see it show up here \u2014 no need to restart or press r.":"Start the proxy, then send it a request to see live traffic here."},void 0,!1,void 0,this)},void 0,!1,void 0,this):V(Rk,{children:[V(ZM,{palette:t,cols:R},void 0,!1,void 0,this),V("scrollbox",{focused:!1,stickyScroll:!1,scrollX:!1,style:Ue(t),children:V("box",{flexDirection:"column",overflow:"hidden",children:l.map((S)=>V(QM,{palette:t,req:S,cols:R},S.requestId,!1,void 0,this))},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this),V(Ut,{palette:t,backgroundColor:t.element,edge:"top",children:V(it,{palette:t,hints:[{key:"n",label:"start proxy"},{key:"x",label:"stop proxy"}]},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}var If=9,Ol=8,$l=4,Fl=8,Nl=4,YM=8,JM=2,XM=64,Ek="(none)",Ck=26,tO=56,Pk=2,_k=46,rO=54,nO=20,oO=12;var Ak=I(()=>{Yt();vr();Ot();ke()});import{jsxDEV as pe}from"@opentui/react/jsx-dev-runtime";function Ik(e){let{palette:t,name:r}=e;return pe("box",{flexGrow:1,border:!0,borderStyle:"rounded",borderColor:t.borderActive,backgroundColor:t.panel,title:r,titleColor:Ze(r,t),flexDirection:"column",overflow:"hidden",children:pe(Mf,{...e,compact:!1},void 0,!1,void 0,this)},void 0,!1,void 0,this)}function Mf(e){let{palette:t,cfg:r,snap:n,name:o,reveal:i,width:s,compact:a=!0,envScrollRef:c}=e,l=ye(),d=r?.services[o];if(!d)return pe(ze,{palette:t,title:"Unknown service",body:`No service named ${o}.`,hint:"j/k move"},void 0,!1,void 0,this);let u=n?.services[o],f=tr(u),p=u?.health??"UNKNOWN",m=go(u)||Es(d,u),h=Ab(d,i,r?.secrets.extra_markers??[],r?.secrets.extra_patterns??[],e.resolvedEnv),v=e.envStatus==="error"?"error":e.envStatus==="resolved"?"success":e.envStatus==="loading"?"info":"muted",b=e.envStatus==="resolved"?"resolved":e.envStatus==="loading"?"resolving":e.envStatus==="error"?"config fallback":"config",g=s>=sO,_=[{label:"health",value:Wc(d)},{label:"workdir",value:d.working_dir||"."},{label:"restart",value:Kc(d)},{label:"caps",value:d.capabilities.join(", ")||"\u2014"},{label:"runtime",value:d.container?`${d.container.runtime||"docker"} \xB7 ${d.container.image}`:"host"},...d.watch.enabled?[{label:"watch",value:d.watch.paths.join(" ")||"on"}]:[]],R=[{label:"identity",value:Gc(d,u)},{label:"depends",value:d.dependencies.map(Ra).join(", ")||"\u2014"},{label:"ports",value:Es(d,u)},{label:"tries",value:String(u?.restarts??0),tone:u?.restarts?"warning":"muted"}],S=Math.max(Co+8,s-2),y=g?Math.floor(S/2):S;return pe("box",{padding:l.pad,flexGrow:1,flexDirection:"column",overflow:"hidden",children:[pe(ut,{palette:t,items:[{text:f,tone:Dk(f)},{text:p,tone:Dk(p)},{text:u?.pid?`pid ${u.pid}`:"pid \u2014",tone:u?.pid?"info":"idle"},{text:m==="\u2014"?"port \u2014":`port ${m}`,tone:m==="\u2014"?"idle":"info"}]},void 0,!1,void 0,this),d.description?pe("box",{height:1,overflow:"hidden",children:pe("text",{fg:t.muted,wrapMode:"none",children:Re(d.description,Math.max(8,s-4))},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,u?.last_error?pe("box",{height:1,overflow:"hidden",children:pe("text",{fg:t.error,wrapMode:"none",children:Re(u.last_error,iO)},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,pe(Of,{palette:t,item:{label:"command",value:Uc(d)},valueWidth:Math.max(8,S-Co)},void 0,!1,void 0,this),pe(aO,{palette:t,left:_,right:R,wide:g,colWidth:y},void 0,!1,void 0,this),pe(cO,{palette:t,entries:h,reveal:i,width:s,focused:!a,scrollRef:c,source:b,sourceTone:v,error:e.envError,onSelect:e.onSelectEnv},void 0,!1,void 0,this),a?null:pe(it,{palette:t,hints:[{key:"n",label:"start"},{key:"x",label:"stop"},{key:"R",label:"restart"},{key:"o",label:"config"},{key:"l",label:"logs"},{key:"/reveal",label:"secrets"},{key:"esc",label:"back"}]},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function aO(e){let{palette:t,left:r,right:n,wide:o,colWidth:i}=e,s=Math.max(4,i-Co);if(!o)return pe("box",{width:i,flexShrink:0,flexDirection:"column",overflow:"hidden",children:[...r,...n].map((a)=>pe(Of,{palette:t,item:a,valueWidth:s},a.label,!1,void 0,this))},void 0,!1,void 0,this);return pe("box",{height:r.length,flexShrink:0,flexDirection:"row",overflow:"hidden",children:[pe(Lk,{palette:t,items:r,width:i},void 0,!1,void 0,this),pe(Lk,{palette:t,items:n,width:i},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function Lk(e){let t=Math.max(4,e.width-Co);return pe("box",{width:e.width,flexShrink:0,flexDirection:"column",overflow:"hidden",children:e.items.map((r)=>pe(Of,{palette:e.palette,item:r,valueWidth:t},r.label,!1,void 0,this))},void 0,!1,void 0,this)}function Of(e){let{palette:t,item:r,valueWidth:n}=e,o=r.tone==="warning"?t.warning:r.tone==="muted"?t.muted:t.text;return pe("box",{height:1,width:Co+n,flexDirection:"row",overflow:"hidden",children:[pe("box",{width:Co,flexShrink:0,overflow:"hidden",children:pe("text",{fg:t.muted,wrapMode:"none",children:oe(r.label,Co)},void 0,!1,void 0,this)},void 0,!1,void 0,this),pe("box",{width:n,flexShrink:0,overflow:"hidden",children:pe("text",{fg:o,wrapMode:"none",children:oe(r.value,n)},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function cO(e){let{palette:t,entries:r,reveal:n,width:o,focused:i,scrollRef:s,source:a,sourceTone:c,error:l,onSelect:d}=e;return pe("box",{flexGrow:1,flexDirection:"column",overflow:"hidden",children:[pe(ut,{palette:t,items:[{text:`env ${r.length}`,tone:r.length>0?"info":"idle"},{text:a,tone:c},{text:n?"secrets shown":"redacted",tone:n?"warning":"muted"}],hints:i?[{key:"j/k",label:"scroll"}]:[{key:"/reveal",label:"secrets"}]},void 0,!1,void 0,this),l?pe("box",{height:1,overflow:"hidden",children:pe("text",{fg:t.warning,wrapMode:"none",children:Re(l,Math.max(8,o-4))},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,r.length===0?pe("text",{fg:t.muted,children:"no service env overrides"},void 0,!1,void 0,this):pe("box",{flexGrow:1,height:"100%",overflow:"hidden",children:[d?pe("text",{fg:t.muted,wrapMode:"word",children:"click a variable for its full value"},void 0,!1,void 0,this):null,pe("scrollbox",{ref:s,focused:i,stickyScroll:!1,scrollX:!1,style:{rootOptions:{flexGrow:1,height:"100%",overflow:"hidden",backgroundColor:t.panel},viewportOptions:{backgroundColor:t.panel},contentOptions:{backgroundColor:t.panel},scrollbarOptions:{trackOptions:{foregroundColor:t.primary,backgroundColor:t.element}}},children:pe("box",{flexDirection:"column",overflow:"hidden",children:r.map((u)=>{let f=u.required?t.warning:u.fromConfig?t.accent:t.muted,p=u.value===""&&u.required?"(required)":u.value;return pe("box",{flexDirection:"row",overflow:"hidden",onMouseDown:d?()=>d(u):void 0,children:pe("text",{wrapMode:"word",children:[pe("span",{fg:f,children:u.required?`${u.key}*`:u.key},void 0,!1,void 0,this),pe("span",{fg:u.value===""?t.muted:t.text,children:` ${p}`},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},u.key,!1,void 0,this)})},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)}function Dk(e){switch(e.toUpperCase()){case"HEALTHY":case"RUNNING":return"success";case"UNHEALTHY":case"WARNING":case"WARN":case"STARTING":case"RESTARTING":return"warning";case"FAILED":case"ERROR":return"error";default:return"muted"}}var iO=72,sO=56,Co=9;var $f=I(()=>{ht();Yt();Mt();Ot();Lt();ke();tt()});import{jsxDEV as Hn}from"@opentui/react/jsx-dev-runtime";function Mk(e){let{palette:t,cfg:r,names:n,snap:o,selected:i,checked:s,width:a,reveal:c,onOpen:l,onSelectIndex:d,onToggle:u,resolvedEnv:f,envStatus:p,envError:m,onSelectEnv:h}=e;if(n.length===0)return Hn(ze,{palette:t,title:"No services",body:"This configuration does not define any services."},void 0,!1,void 0,this);let v=a<li,b=n[i]??"",g=Hc(a,n,v),_=4,R=v?Math.max(Ff,a-_):Math.max(Ff,a-g-_);return Hn("box",{flexGrow:1,flexDirection:v?"column":"row",overflow:"hidden",children:[Hn("box",{flexGrow:v?1:0,flexShrink:0,minWidth:v?void 0:Oc,width:v?void 0:g,border:!0,borderStyle:"rounded",borderColor:t.borderActive,backgroundColor:t.panel,title:"services",titleColor:t.primary,flexDirection:"column",overflow:"hidden",children:[Hn(Ml,{palette:t,checked:s},void 0,!1,void 0,this),Hn("box",{flexGrow:1,overflow:"hidden",children:Hn(Il,{palette:t,names:n,snap:o,selected:i,checked:s,width:Nc(g),onOpen:l,onSelectIndex:d,onToggle:u},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this),Hn("box",{flexGrow:2,minWidth:v?void 0:Ff,minHeight:v?lO:void 0,border:!0,borderStyle:"rounded",borderColor:t.border,backgroundColor:t.panel,title:b||"detail",titleColor:b?Ze(b,t):t.primary,overflow:"hidden",flexDirection:"column",children:Hn(Mf,{palette:t,cfg:r,snap:o,name:b,reveal:c,width:R,resolvedEnv:f,envStatus:p,envError:m,onSelectEnv:h},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}var Ff=36,lO=12;var Ok=I(()=>{Yt();vr();Lt();tt();$f();Ef()});import{jsxDEV as we}from"@opentui/react/jsx-dev-runtime";function uO(e){switch(e){case"cycle":return"\u2194";case"toggle":return"\u23FB";case"page":return"\u25B8";case"action":return"!";default:return"\xB7"}}function Hk(e){let{palette:t,items:r,selected:n,locked:o,width:i,onPick:s,onActivate:a}=e,c=ye(),l=vp(r,n),d=Ln(r,l);return we("box",{flexGrow:1,border:!0,borderStyle:"rounded",borderColor:t.borderActive,backgroundColor:t.panel,title:"settings",titleColor:t.primary,flexDirection:"column",overflow:"hidden",children:[we("box",{height:2,paddingLeft:c.pad,flexShrink:0,flexDirection:"column",overflow:"hidden",children:[we("text",{fg:t.primary,wrapMode:"none",children:"Preferences"},void 0,!1,void 0,this),we("text",{fg:t.muted,wrapMode:"none",children:o?"DEVCTL_TUI_CONFIG is set \u2014 changes stay in this session.":"Changes save to your user tui.json unless noted."},void 0,!1,void 0,this)]},void 0,!0,void 0,this),we("box",{flexGrow:1,paddingLeft:c.pad,paddingRight:c.pad,overflow:"hidden",flexShrink:1,children:we(pO,{palette:t,items:r,active:l,scale:c,width:i,onPick:s,onActivate:a},void 0,!1,void 0,this)},void 0,!1,void 0,this),we(mO,{palette:t,item:d,scale:c},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function pO(e){let{palette:t,items:r,active:n,scale:o,width:i,onPick:s,onActivate:a}=e,c=fi(n,Nk),l=Math.max(8,i-$k-Fk-10),d=0,u=Sb(r);return we("scrollbox",{ref:c,focused:!1,stickyScroll:!1,scrollX:!1,style:Ue(t),children:we("box",{flexDirection:"column",overflow:"hidden",children:u.map((f,p)=>{let m=d;d+=f.items.length;let h=n>=m&&n<m+f.items.length;return we("box",{flexDirection:"column",flexShrink:0,marginBottom:p<u.length-1?Math.max(1,o.gap):0,overflow:"hidden",children:[we("box",{height:1,overflow:"hidden",children:we("text",{fg:h?t.primary:t.muted,wrapMode:"none",children:f.group},void 0,!1,void 0,this)},void 0,!1,void 0,this),f.group==="MCP"?we("box",{height:1,overflow:"hidden",children:we("text",{fg:t.muted,wrapMode:"none",children:"dedicated page \u2014 also /mcp or /agent"},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,f.items.map((v,b)=>{let g=m+b;return we(fO,{index:g,palette:t,item:v,active:g===n,rowH:o.rowH,valueWidth:l,onPick:()=>s(g),onActivate:()=>a(v)},v.id,!1,void 0,this)})]},f.group,!0,void 0,this)})},void 0,!1,void 0,this)},void 0,!1,void 0,this)}function fO(e){let{palette:t,index:r,item:n,active:o,rowH:i,valueWidth:s,onPick:a,onActivate:c}=e;return we("box",{id:`${Nk}-${r}`,height:i,flexDirection:"row",overflow:"hidden",flexShrink:0,backgroundColor:o?t.highlight:void 0,onMouseDown:()=>{if(o){c();return}a()},children:[we("box",{width:2,flexShrink:0,overflow:"hidden",children:we("text",{fg:t.primary,children:o?"\u203A":" "},void 0,!1,void 0,this)},void 0,!1,void 0,this),we("box",{width:Fk,flexShrink:0,overflow:"hidden",children:we("text",{fg:o?t.primary:t.muted,children:uO(n.kind)},void 0,!1,void 0,this)},void 0,!1,void 0,this),we("box",{width:$k,flexShrink:0,overflow:"hidden",children:we("text",{fg:o?t.primary:t.text,wrapMode:"none",children:n.name},void 0,!1,void 0,this)},void 0,!1,void 0,this),we("box",{flexGrow:1,overflow:"hidden",children:we("text",{fg:hO(t,n,o),wrapMode:"none",children:Re(n.value,s)},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function mO(e){let{palette:t,item:r,scale:n}=e,o=r?.id==="font",i=r?.kind==="page"?`${r.name} \u2192 dedicated page`:r?.name??"";return we("box",{height:dO,flexShrink:0,border:!0,borderStyle:"rounded",borderColor:t.border,title:"selected",titleColor:t.muted,backgroundColor:t.element,paddingLeft:n.pad,paddingRight:n.pad,flexDirection:"column",overflow:"hidden",children:[we("box",{height:1,overflow:"hidden",children:we("text",{fg:t.primary,wrapMode:"none",children:o?`${xb(n.steps)} ${n.label}`:i},void 0,!1,void 0,this)},void 0,!1,void 0,this),we("box",{height:1,overflow:"hidden",children:we("text",{fg:t.text,wrapMode:"word",children:r?.detail??""},void 0,!1,void 0,this)},void 0,!1,void 0,this),we("box",{height:1,overflow:"hidden",children:we("text",{fg:t.muted,wrapMode:"none",children:r?r.kind==="info"?"read-only":r.hint:""},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function hO(e,t,r){if(t.id==="mouse")return t.value==="on"?e.success:e.text;if(t.kind==="page")return e.info;if(t.kind==="info")return e.text;return r?e.primary:e.text}var $k=14,Fk=2,dO=5,Nk="settings-row";var jk=I(()=>{Mt();Ot();ke();Er()});import{jsxDEV as Ae}from"@opentui/react/jsx-dev-runtime";function Bk(e){let{palette:t,cfg:r,google:n,bootError:o,bootErrorMissing:i=!1,step:s=0}=e,a=ye();if(o&&!r)return i?Ae(ze,{palette:t,title:"No configuration found",body:"Would you like to run setup?",hint:"[Enter] 9-step setup [Esc] Exit"},void 0,!1,void 0,this):Ae(ze,{palette:t,title:"Configuration error",body:o,hint:"Fix .devctl/config.yaml, then restart devctl [Esc] Exit"},void 0,!1,void 0,this);let c=e.issues,l=gO(r,n,c),d=l.filter((g)=>g.ok).length,u=l.length,f=u>0?Math.round(d/u*100):0,p=20,m=Math.round(f/100*p),h="\u2588".repeat(m)+"\u2591".repeat(Math.max(0,p-m)),v=f===100?t.success:f===0?t.muted:t.primary,b=l[s];return Ae(kt,{palette:t,title:"setup",scroll:!0,children:[Ae("box",{flexDirection:"row",overflow:"hidden",flexShrink:0,children:Ae("text",{fg:t.primary,children:"Developer onboarding"},void 0,!1,void 0,this)},void 0,!1,void 0,this),Ae("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,children:Ae("text",{wrapMode:"none",children:[Ae("span",{fg:v,children:`[${h}]`},void 0,!1,void 0,this),Ae("span",{fg:t.text,children:` ${d}/${u} ready`},void 0,!1,void 0,this),Ae("span",{fg:t.muted,children:` (${f}%)`},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this),Ae("box",{height:1,flexShrink:0},void 0,!1,void 0,this),l.map((g,_)=>{let R=_===s;return Ae("box",{height:a.rowH,flexDirection:"row",overflow:"hidden",backgroundColor:R?t.highlight:void 0,paddingLeft:1,children:[Ae("box",{width:2,flexShrink:0,children:Ae("text",{fg:et(t,g.ok?"OK":"WARN"),children:hr(g.ok?"OK":"WARN")},void 0,!1,void 0,this)},void 0,!1,void 0,this),Ae("box",{width:3,flexShrink:0,children:Ae("text",{fg:R?t.primary:t.muted,children:String(_+1).padStart(2," ")},void 0,!1,void 0,this)},void 0,!1,void 0,this),Ae("box",{width:20,flexShrink:0,overflow:"hidden",children:Ae("text",{fg:R?t.primary:t.text,children:g.name},void 0,!1,void 0,this)},void 0,!1,void 0,this),Ae("box",{flexGrow:1,overflow:"hidden",children:Ae("text",{fg:R?t.text:t.muted,wrapMode:"none",children:g.detail},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},g.name,!0,void 0,this)}),Ae("box",{height:1,flexShrink:0},void 0,!1,void 0,this),b?Ae("box",{border:!0,borderStyle:"rounded",borderColor:b.ok?t.success:t.warning,title:b.name,titleColor:b.ok?t.success:t.warning,flexDirection:"column",paddingLeft:1,paddingRight:1,flexShrink:0,overflow:"hidden",children:Ae("text",{fg:b.ok?t.text:t.warning,wrapMode:"word",children:b.detail},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,Ae("box",{height:1,flexShrink:0},void 0,!1,void 0,this),Ae("box",{flexDirection:"row",overflow:"hidden",flexShrink:0,children:Ae(Be,{palette:t,label:"never hard-coded",tone:"idle"},void 0,!1,void 0,this)},void 0,!1,void 0,this),Ae("text",{fg:t.muted,wrapMode:"word",children:"Service accounts and IAP audiences stay in configuration. Run `devctl setup` for the interactive CLI wizard."},void 0,!1,void 0,this),Ae(it,{palette:t,hints:[{key:"j/k",label:"steps"},{key:"enter",label:r?"start":"begin setup"},{key:"esc",label:"exit"},{key:"o",label:"profiles"}]},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function gO(e,t,r=[]){let n=Object.values(e?.services??{}).flatMap((s)=>s.ports.filter((a)=>!a.auto)),o=(e?.proxy.routes??[]).some((s)=>s.auth.type.toLowerCase()==="iap"),i=Tl(e,void 0,t).project;return[{name:dn[0],ok:Boolean(e?.repoRoot),detail:e?.repoRoot||"unknown"},{name:dn[1],ok:Boolean(e?.project.name),detail:e?.project.name||"set project.name"},{name:dn[2],ok:Boolean(i),detail:i||"optional for local-only"},{name:dn[3],ok:t?.adcAvailable===!0,detail:t?.adcAvailable?"ADC available":"needed only for cloud identity / IAP"},{name:dn[4],ok:!0,detail:"read from configuration; never hard-coded"},{name:dn[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:dn[6],ok:n.length===0||Boolean(e),detail:`${n.length} fixed ports from configuration`},{name:dn[7],ok:Object.keys(e?.profiles??{}).length>0,detail:`${Object.keys(e?.profiles??{}).length} profile(s)`},{name:dn[8],ok:r.length===0,detail:r.length===0?"configuration is valid":r[0]??"invalid"}]}var dn;var Uk=I(()=>{Yt();Mt();ke();tt();dn=["Repository","Environment","Google project","Authentication","Service accounts","IAP","Ports","Profiles","Validation"]});import{useEffect as vO,useState as yO}from"react";import{jsxDEV as ee}from"@opentui/react/jsx-dev-runtime";function ki(e){let{palette:t,title:r,tone:n="muted",children:o}=e,i=n==="success"?t.success:n==="warning"?t.warning:n==="error"?t.error:t.border;return ee("box",{border:!0,borderStyle:"rounded",borderColor:i,title:r,titleColor:i,flexDirection:"column",flexShrink:0,paddingLeft:1,paddingRight:1,marginBottom:1,overflow:"hidden",children:o},void 0,!1,void 0,this)}function Nf(e,t){if(t.color)return t.color;switch(t.tone){case"success":return e.success;case"warning":return e.warning;case"error":return e.error;case"primary":return e.primary;case"muted":return e.muted;default:return e.text}}function Us(e){let{palette:t,cells:r,header:n,gap:o=0,stripe:i}=e;return ee("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,backgroundColor:i?t.element:void 0,children:r.map((s,a)=>ee("box",{flexDirection:"row",flexShrink:0,overflow:"hidden",children:[a>0&&o>0?ee("box",{width:o,flexShrink:0},void 0,!1,void 0,this):null,ee("box",{width:s.width,flexShrink:0,overflow:"hidden",children:ee("text",{fg:n?t.primary:Nf(t,s),wrapMode:"none",children:oe(s.text,s.width)},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},`${s.text}-${a}`,!0,void 0,this))},void 0,!1,void 0,this)}function Wk(e){let{palette:t,width:r}=e;return ee("box",{height:1,flexShrink:0,overflow:"hidden",children:ee("text",{fg:t.border,wrapMode:"none",children:oe("\u2500".repeat(Math.max(1,r)),r)},void 0,!1,void 0,this)},void 0,!1,void 0,this)}function Bs(e){let{palette:t,facts:r,width:n}=e,o=Gp(r,n),i=[{text:"what",width:o.what},{text:"reading",width:o.reading}];if(o.meter>0)i.push({text:"used",width:o.meter});return i.push({text:"meaning",width:o.meaning}),ee("box",{flexDirection:"column",overflow:"hidden",flexShrink:0,children:[ee(Us,{palette:t,header:!0,gap:pi,cells:i},void 0,!1,void 0,this),ee(Wk,{palette:t,width:n},void 0,!1,void 0,this),r.map((s,a)=>{let c=s.meaning===""?[""]:Xc(s.meaning,Math.max(1,o.meaning)),l=s.meter?jx(s.meter):o.meter>0?"":void 0,d=[{text:s.what,width:o.what,tone:"text"},{text:s.reading,width:o.reading,tone:s.tone??"text"}];if(o.meter>0)d.push({text:l??"",width:o.meter,tone:s.meter?s.tone??"text":"muted"});return d.push({text:c[0]??"",width:o.meaning,tone:"muted"}),ee("box",{flexDirection:"column",overflow:"hidden",flexShrink:0,children:[ee(Us,{palette:t,gap:pi,stripe:a%2===1,cells:d},void 0,!1,void 0,this),c.slice(1).map((u,f)=>{let p=[{text:"",width:o.what},{text:"",width:o.reading}];if(o.meter>0)p.push({text:"",width:o.meter});return p.push({text:u,width:o.meaning,tone:"muted"}),ee(Us,{palette:t,gap:pi,stripe:a%2===1,cells:p},`${s.what}-more-${f}`,!1,void 0,this)})]},`${s.what}-${s.reading}-${a}`,!0,void 0,this)})]},void 0,!0,void 0,this)}function Gk(e){let{palette:t,cfg:r,snap:n,width:o,onRefresh:i}=e,s=ye(),[,a]=yO(0);if(vO(()=>{let B=setInterval(()=>{a((z)=>{let de=z+1;if(i&&de%xO===0)i();return de})},bO);return()=>clearInterval(B)},[i]),!r)return ee(ze,{palette:t,title:"No configuration loaded",body:"Add services to .devctl/config.yaml or finish setup."},void 0,!1,void 0,this);let c=Object.keys(r.services).sort(),l=c.some((B)=>Bx(r.services[B])),d=Ix(c,n),u=zx(d,l),f=l&&d.live>0?d.healthy/d.live:d.total>0?d.live/d.total:0,p=si(f,wO),m=d.failed>0?"error":l&&d.live>0&&d.healthy<d.live?"warning":"success",h=Fx(o,s.pad),v=n?.session_id?Jl(n.session_id):void 0,b=v?ks(Date.now()-v.getTime()):"just started",g=n?.logs.total??0,_=n?.logs.errors??0,R=$x(n?.logs.counts??{}),S=n?.proxy.routes??[],y=n?.credentials,x=(y?.entries??[]).filter((B)=>B.valid).length,w=n?.system,k=w?w.memAvailableKB??w.memFreeKB:0,D=w?Wx(w.loadAvg1,w.cpuCount):void 0,O=w?Gx(k,w.memTotalKB):void 0,F=[];if(w&&D&&O){F.push(D,O,{what:"Computer",reading:Ux(w.platform),meaning:`on for ${ks(w.hostUptimeSec*SO)}`,tone:"text"});let B=n?.stats_series;if(B&&B.cpu.length>1)F.push({what:"Load trend",reading:Wp(B.cpu),meaning:`${Math.round(B.interval_ms/1000)}s samples`,tone:"text"});if(B&&B.mem.length>1)F.push({what:"RAM trend",reading:Wp(B.mem),meaning:"used memory over the same window",tone:"text"})}let Z=d.failed>0||D?.tone==="error"||O?.tone==="error"?"error":m==="warning"||D?.tone==="warning"||O?.tone==="warning"?"warning":"success",W=[{what:"This session",reading:b,meaning:"how long this run has been going",tone:"text"},{what:"Profile",reading:n?.profile||"none",meaning:"which group of services is selected",tone:n?.profile?"text":"muted"},{what:"This window",reading:n?.detached?"closed":"watching",meaning:n?.detached?"services keep running without this screen":"you are watching the services from here",tone:n?.detached?"warning":"text"},{what:"Project folder",reading:"",meaning:r.repoRoot,tone:"muted"}],C=[{what:"Lines kept",reading:String(g),meaning:"log lines still in memory",tone:"text"},{what:"Error lines",reading:String(_),meaning:_>0?"open Logs and press e to see only errors":"no ERROR or FATAL lines",tone:_>0?"error":"muted"},...R.map(([B,z])=>({what:B,reading:String(z),meaning:"lines from this service",tone:"text"}))],j=[{what:"Web proxy",reading:n?.proxy.running?"on":"off",meaning:n?.proxy.running?`${S.length} apps${n.proxy.address?` at ${n.proxy.address}`:""}`:"not forwarding local URLs",tone:n?.proxy.running?"success":"muted"},{what:"Agent tools",reading:n?.mcp?.running?"on":"off",meaning:n?.mcp?.address?`Cursor talks to devctl at ${n.mcp.address}`:"turn on from Settings \u2192 MCP",tone:n?.mcp?.running?"success":"muted"},{what:"Google account",reading:n?.identity.user?"signed in":"not signed in",meaning:[n?.identity.user,n?.identity.project].filter(Boolean).join(" \xB7 ")||"no Google user for this session",tone:n?.identity.user?"success":"muted"},{what:"Saved tokens",reading:y?`${x} of ${y.entries.length} good`:"none",meaning:y?Vx(y.backend):"nothing stored yet",tone:y&&x<y.entries.length?"warning":y?"success":"muted"}];return ee(kt,{palette:t,title:"stats",scroll:!0,children:[ee(ki,{palette:t,title:`Your services ${d.live} of ${d.total} started`,tone:Z,children:[ee(Bs,{palette:t,facts:u,width:h},void 0,!1,void 0,this),ee("box",{height:1,flexShrink:0},void 0,!1,void 0,this),ee("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,children:ee("text",{wrapMode:"none",children:[ee("span",{fg:t.muted,children:oe(l?"Ready":"Started",Gp(u,h).what)},void 0,!1,void 0,this),ee("span",{children:" ".repeat(pi)},void 0,!1,void 0,this),ee("span",{fg:Nf(t,{text:"",width:1,tone:m}),children:`[${p}]`},void 0,!1,void 0,this),ee("span",{fg:Nf(t,{text:"",width:1,tone:m}),children:l?` ${d.healthy} of ${d.live||d.total} passing checks`:` ${d.live} of ${d.total} up`},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this),w?ee(ki,{palette:t,title:"This computer",tone:D?.tone==="error"||O?.tone==="error"?"error":D?.tone==="warning"||O?.tone==="warning"?"warning":"success",children:ee(Bs,{palette:t,facts:F,width:h},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,ee(ki,{palette:t,title:"This session",children:ee(Bs,{palette:t,facts:W,width:h},void 0,!1,void 0,this)},void 0,!1,void 0,this),ee(ki,{palette:t,title:"Each service",tone:d.failed>0?"error":l&&d.live>0&&d.healthy<d.live?"warning":"success",children:d.total===0?ee("text",{fg:t.muted,children:"No services in this project yet."},void 0,!1,void 0,this):ee("box",{flexDirection:"column",overflow:"hidden",flexShrink:0,children:[ee(kO,{palette:t,names:c,snap:n,width:h},void 0,!1,void 0,this),ee("box",{height:1,flexShrink:0},void 0,!1,void 0,this),ee("text",{fg:t.muted,wrapMode:"none",children:oe("CPU is % of one core. RAM is what that process is holding now.",h)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this),ee(ki,{palette:t,title:"Log lines",tone:_>0?"warning":"muted",children:ee(Bs,{palette:t,facts:C,width:h},void 0,!1,void 0,this)},void 0,!1,void 0,this),ee(ki,{palette:t,title:"Other parts",children:ee(Bs,{palette:t,facts:j,width:h},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function kO(e){let{palette:t,names:r,snap:n,width:o}=e,i=Nx(o),s=[{text:"service",width:i.name},{text:"status",width:Tr}];if(i.health)s.push({text:"check",width:Ur});if(i.cpu)s.push({text:"cpu",width:_s});if(i.mem)s.push({text:"ram",width:Rs});if(i.up)s.push({text:"up",width:Ps});if(i.rst)s.push({text:"restarts",width:ll});if(i.pid)s.push({text:"pid",width:an});return ee("box",{flexDirection:"column",overflow:"hidden",flexShrink:0,children:[ee(Us,{palette:t,header:!0,gap:$t,cells:s},void 0,!1,void 0,this),ee(Wk,{palette:t,width:o},void 0,!1,void 0,this),r.map((a,c)=>ee(RO,{palette:t,name:a,rt:n?.services[a],cols:i,stripe:c%2===1},a,!1,void 0,this))]},void 0,!0,void 0,this)}function _O(e){if(e===void 0)return"muted";if(e>PO)return"error";if(e>CO)return"warning";return"text"}function RO(e){let{palette:t,name:r,rt:n,cols:o,stripe:i}=e,s=Kx(n),a=qx(n),c=n?.restarts??0,l=[{text:r,width:o.name,color:Ze(r,t)},{text:s,width:Tr,color:et(t,n?.state??"STOPPED")}];if(o.health)l.push({text:a,width:Ur,color:et(t,n?.health??"UNKNOWN")});if(o.cpu)l.push({text:n?.cpuPercent!==void 0?Pb(n.cpuPercent):"\u2014",width:_s,tone:_O(n?.cpuPercent)});if(o.mem)l.push({text:n?.memoryKB!==void 0?Cs(n.memoryKB):"\u2014",width:Rs,tone:"text"});if(o.up)l.push({text:Ox(n),width:Ps,tone:"text"});if(o.rst)l.push({text:String(c),width:ll,tone:c>0?"warning":"muted"});if(o.pid)l.push({text:n?.pid?String(n.pid):"\u2014",width:an,tone:"muted"});return ee("box",{flexDirection:"column",flexShrink:0,overflow:"hidden",children:[ee(Us,{palette:t,gap:$t,stripe:i,cells:l},void 0,!1,void 0,this),n?.last_error?ee("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,children:ee("text",{fg:t.error,wrapMode:"none",children:oe(` ${n.last_error}`,Math.max(24,o.name+$t+Tr+Ur))},void 0,!1,void 0,this)},void 0,!1,void 0,this):null]},void 0,!0,void 0,this)}var bO=1000,xO=5,wO=16,SO=1000,CO=80,PO=100;var Kk=I(()=>{Yt();Mt();Ot();gr();Lt();dl();ke();tt()});import{useRenderer as EO,useTerminalDimensions as TO}from"@opentui/react";import{useCallback as jn,useEffect as AO,useMemo as qk,useRef as Vr,useState as Je}from"react";import{jsxDEV as he,Fragment as LO}from"@opentui/react/jsx-dev-runtime";function Vk({controller:e,tui:t,onQuit:r,onDown:n,onAttached:o,bootError:i,bootErrorMissing:s=!1,terminalBackground:a,workspace:c}){let{saveTuiPreferences:l,resolveTuiOverridePath:d,userTuiConfigPath:u,validateConfigText:f,freePort:p,readTextFile:m,writeTextFile:h,createStarterConfig:v,validate:b}=c,[g,_]=Je(e),[R,S]=Je(i),y=EO(),{width:x,height:w}=TO(),[k,D]=Je(g?"dashboard":"setup"),[O,F]=Je("none"),[Z,W]=Je(""),[C,j]=Je("commands"),[B,z]=Je(0),[de,Ee]=Je(0),[Ne,ne]=Je(0),[H,ae]=Je([]),[Q,nt]=Je(),[ft,ie]=Je(R??""),[Le,Me]=Je({}),[Ke,gt]=Je(),[Xe,Ct]=Je(Tb(g?.cfg)),[ge,vt]=Je(""),[at,yt]=Je(!1),[rr,Qe]=Je(),[Xt,qe]=Je(),[mt,br]=Je(),Kt=Vr(null),xr=Vr(null),nr=Vr(null),qt=Vr(null),or=Vr(null),ir=Vr(null),sr=Vr(null),Vt=Vr(""),[ce,K]=Je("quit"),A=Vr(void 0),ve=Vr(0),[J,Dt]=Je(g?.cfg),ct=g?.previousPersisted,[Ce,Po]=Je(void 0),P=wS({tui:t,controller:g,resolveTuiOverridePath:d,terminalBackground:a,userTuiConfigPath:u,snap:Q,setStatus:ie,saveTuiPreferences:l,setPaletteIndex:z,setOverlay:F,setConfirmKind:K,setScreen:D,setSelected:ne,screen:k}),{themeName:ue,setThemeName:Yr,fontSize:Jr,prefsLocked:wr,palette:se,rootBackground:Bn,settingRows:Un,persistPrefs:Dr,persistTheme:un,activateSetting:Ws}=P,Ci=lS({controller:g,workspace:c,cfg:J,snap:Q,screen:k,configReloadError:Ce,setSnap:nt,setStatus:ie}),{google:Pi,doctor:le,doctorLoading:bt,doctorError:Pt,doctorProgress:Xr,setDoctorTick:Gs}=Ci,_o=t.keybinds.copy??Zr(),Sr=qk(()=>Object.keys(J?.services??{}).sort(),[J]),pn=jn(async()=>{if(!g)return;try{let X=await g.status();if(nt(X),X.profile&&Xe==="")Ct(X.profile);return X}catch(X){ie(L(X));return}},[g,Xe]),Hl=hS({controller:g,tui:t,names:Sr,screen:k,refresh:pn,setStatus:ie}),{logs:Gf,setLogs:yC,paused:Kf,errorOnly:jl,setErrorOnly:Bl,showSystemLogs:qf,logSearch:Vf,setLogSearch:zf,logSearchFocused:bC,setLogSearchFocused:xC,logFollow:Yf,logSince:wC,logService:Jf,setLogService:Ul,logServiceB:SC,setLogServiceB:kC,splitLogs:Wl,splitFocus:Gl,setSplitFocus:CC,logSliceB:Xf,logWindowB:Qf,logSelectedB:Zf,logPinnedB:PC,pinLogViewB:_C,logServices:RC,logShowTimestamps:EC,logShowMeta:TC,logSource:AC,logRegex:em,logWrap:tm,logPinned:rm,logSelected:nm,logsFullscreen:Kl,dashboardLogCursor:LC,setDashboardLogCursor:DC,logFacets:om,logSources:im,filteredLogs:sm,filteredLogsB:IC,logWindow:Ks,logSlice:qs,pinLogView:ql,applyLogCursorA:MC,applyLogCursorB:OC,jumpToLatestLogs:am}=Hl,Ro=qk(()=>{if(C==="tasks")return Np(Object.keys(J?.tasks??{}),Z,"tasks","run this task");if(C==="services")return Np(Object.keys(J?.services??{}),Z,"services","exec in this service");return Tx(Z,{services:Object.keys(J?.services??{}),tasks:Object.keys(J?.tasks??{})})},[J,Z,C]);AO(()=>{Ee(0)},[Z]);let Vl=ex(k,{doctor:le?.checks.length??0,settings:Un.length,profiles:Object.keys(J?.profiles??{}).length,services:Sr.length,logs:Wl&&Gl===1?Xf.length:qs.length,mcp:mw(),config:Object.keys(J?.tasks??{}).length}),cm=k==="logs"?Wl&&Gl===1?Zf:nm:Ne,Ir=Vl<=0?Math.max(0,cm):Math.max(0,Math.min(cm,Vl-1)),$C=k==="detail"?ge:k==="services"?Sr[Ir]??"":"",{inspectorEnv:lm,inspectorEnvStatus:dm,inspectorEnvError:um,resolveEnvironment:FC}=ES({controller:g,cfg:J,envService:$C}),Eo=jn(()=>{if(F("none"),W(""),j("commands"),xC(!1),A.current)clearTimeout(A.current)},[]),zl=jn((X)=>{ae((xt)=>xt.includes(X)?xt.filter((QC)=>QC!==X):[...xt,X])},[]),pm=yS({tui:t,controller:g,cfg:J,persistPrefs:Dr,snap:Q,refresh:pn,setStatus:ie}),{mcpPortDraft:NC,mcpPort:HC,toggleMcpTool:jC,toggleMcp:BC,copyMcpSnippet:UC}=pm;iS({controller:g,cfg:J,paused:Kf,logSince:wC,refresh:pn,setLogs:yC,setCfg:Dt,setConfigReloadError:Po,setStatus:ie});let Vs=jn((X)=>{vt(X),D("detail")},[]),fm=jn((X)=>{let xt=X.value!==""?X.value:X.required?"(required, not set)":"(empty)";br({title:X.key,body:xt}),F("scroll-text")},[]),Yl=uS({controller:g,workspace:c,cfg:J,snap:Q,refresh:pn,setStatus:ie,setOverlay:F}),{plan:zs,planInitiallyRunning:WC,planBusy:GC,lifecycle:mm}=Yl,Qr=PS({workspace:c,renderer:y,setOverlay:F,setStatus:ie,setScreen:D,onAttached:o,setController:_,setCfg:Dt,setBootError:S}),{configEditRef:KC,configEditText:qC,configEditError:VC,setConfigEditError:zC,openConfigBuffer:hm,saveConfigBuffer:YC}=nS({cfg:J,readTextFile:m,setOverlay:F,setStatus:ie,validateConfigText:f,writeTextFile:h,controller:g,setConfirmKind:K,refresh:pn}),gm=jn(async()=>{let X=y.getSelection()?.getSelectedText()??"";if(X.trim()===""){ie("Nothing selected \u2014 drag to highlight text");return}try{await ml(X);let xt=X.split(`
|
|
2233
|
-
`).length;ie(xt===1?"Copied selection":`Copied ${xt} selected lines`)}catch(xt){ie(L(xt))}},[y]),{runCommand:Ys}=Zw({setOverlay:F,setQuery:W,setSlashPicker:j,checked:H,setConfirmKind:K,setStatus:ie,persistTheme:un,setPaletteIndex:z,themeName:ue,setScreen:D,setSelected:ne,screen:k,renderer:y,cfg:J,setScrollText:br,reveal:at,controller:g,refresh:pn,profile:Xe,setReveal:yt,resolveEnvironment:FC,openDetail:Vs,copySelection:gm,lastExportPath:Vt,openConfigBuffer:hm,onDown:n??((X)=>r(X)),workspace:c,logView:Hl,diagnostics:Ci,lifecycleActions:Yl}),vm=jn(()=>{let X=ui(Ro,de);if(C==="tasks"){if(!X){ie(Ro.length===0?"no tasks configured":"pick a task");return}j("commands");let xt=Wr("run");if(xt)Ys(xt,[X.name]);return}if(C==="services"){if(!X){ie(Ro.length===0?"no services configured":"pick a service");return}j("commands"),W(`exec ${X.name} -- `);return}if(!X){ie(Z.trim()===""?"pick a command from the list":`unknown command /${Z}`),Eo();return}Ys(X,Ex(X,Z))},[Eo,Ro,Z,Ys,de,C]),ym=jn((X)=>{un(X),Eo()},[Eo,un]);Mw({tui:t,controller:g,cfg:J,snap:Q,ui:{screen:k,overlay:O,onQuit:r,onDown:n??((X)=>r(X)),closeOverlay:Eo,confirmKind:ce,confirmDetail:Le,portTarget:Ke,profile:Xe,listCursor:Ir,names:Sr,listCount:Vl,openDetail:Vs,copySelection:gm,height:w,paletteIndex:B,applyTheme:ym,runCommand:Ys,saveConfigBuffer:YC,filtered:Ro,slashIndex:de,submitSlash:vm,advanceWizard:()=>void Qr.advanceWizard(),bootErrorMissing:s,bootError:R,createStarterConfig:v,startWizard:Qr.startWizard,toggleChecked:zl,checked:H,detailName:ge,refresh:pn,openConfigBuffer:hm,setOverlay:F,setConfirmKind:K,setConfirmDetail:Me,setPortTarget:gt,setLogDetail:Qe,logDetail:rr,setLogSearch:zf,setProfile:Ct,setStatus:ie,setPaletteIndex:z,setSlashIndex:Ee,setQuery:W,setSlashPicker:j,setScreen:D,setSelected:ne,setChecked:ae,setConfigEditError:zC,freePort:p},logView:Hl,lifecycleActions:Yl,mcp:pm,preferences:P,diagnostics:Ci,refs:{interruptArmedAt:ve,leaderTimer:A,logDetailsScrollRef:qt,scrollTextScrollRef:ir,routeDetailsScrollRef:or,planScrollRef:sr,helpScrollRef:xr,configScrollRef:Kt,detailScrollRef:nr}});let bm=cx(ce,Xe,Le),JC=new Set(zs?.waves.flat()??[]),XC=zs===void 0||mm==="stop"?"":Object.entries(Q?.services??{}).find(([X,xt])=>JC.has(X)&&xt.state==="FAILED")?.[0]??"";return he(bp.Provider,{value:ws(Jr),children:he("box",{flexDirection:"column",width:x,height:w,backgroundColor:Bn,overflow:"hidden",children:[Kl?null:he(LO,{children:[he(Xx,{palette:se,cfg:J,snap:Q,google:Pi,profile:Xe,reveal:at,width:x},void 0,!1,void 0,this),he(Qx,{palette:se,screen:k,width:x,onSelect:D},void 0,!1,void 0,this),Ce?he("box",{height:1,paddingLeft:1,backgroundColor:se.panel,overflow:"hidden",children:he("text",{fg:se.error,wrapMode:"none",children:`\u26A0 configuration reload failed: ${Ce}`},void 0,!1,void 0,this)},void 0,!1,void 0,this):null]},void 0,!0,void 0,this),he("box",{flexGrow:1,overflow:"hidden",children:[k==="dashboard"?he(mk,{palette:se,cfg:J,snap:Q,logs:Gf,names:Sr,selected:Ir,checked:H,width:x,followTick:Yf,logService:Jf,errorOnly:jl,showSystemLogs:qf,onOpen:Vs,onSelectIndex:ne,selectedLog:LC,onToggle:zl,logSources:im,onFilterService:Ul,onToggleErrors:()=>Bl((X)=>!X),onShowErrors:()=>{Ul(""),Bl(!0)},wrapMode:tm,view:qs,follow:!rm,onLeaveLatest:ql,onPickLog:(X)=>{let xt=qs[X];if(!xt)return;DC(X),Qe(xt),ql()},viewStart:Ks.start,viewTotal:sm.length,newer:Ks.newer,onJumpLatest:am,facets:om,leftover:ct,search:Vf,regex:em},void 0,!1,void 0,this):null,k==="services"?he(Mk,{palette:se,cfg:J,names:Sr,snap:Q,selected:Ir,checked:H,width:x,reveal:at,onOpen:Vs,onSelectIndex:ne,onToggle:zl,resolvedEnv:lm,envStatus:dm,envError:um,onSelectEnv:fm},void 0,!1,void 0,this):null,k==="detail"?he(Ik,{palette:se,cfg:J,snap:Q,name:ge,reveal:at,width:x,envScrollRef:nr,resolvedEnv:lm,envStatus:dm,envError:um,onSelectEnv:fm},void 0,!1,void 0,this):null,k==="logs"?he(bk,{palette:se,logs:Gf,names:Sr,logSources:im,service:Jf,errorOnly:jl,showSystemLogs:qf,search:Vf,searchFocused:bC,followTick:Yf,width:x,onSearch:zf,onService:Ul,onToggleErrors:()=>Bl((X)=>!X),source:AC,regex:em,services:RC,showTimestamps:EC,showMeta:TC,view:qs,wrapMode:tm,selected:nm,follow:!rm,newer:Ks.newer,viewStart:Ks.start,viewTotal:sm.length,onSelect:MC,fullscreen:Kl,onLeaveLatest:ql,onJumpLatest:am,facets:om,split:Wl,splitFocus:Gl,serviceB:SC,viewB:Xf,selectedB:Zf,followB:!PC,newerB:Qf.newer,viewStartB:Qf.start,viewTotalB:IC.length,onServiceB:kC,onSelectB:OC,onLeaveLatestB:_C,onFocusPane:CC},void 0,!1,void 0,this):null,k==="auth"?he(ZS,{palette:se,cfg:J,google:Pi,identity:Q?.identity},void 0,!1,void 0,this):null,k==="credentials"?he(sk,{palette:se,credentials:Q?.credentials},void 0,!1,void 0,this):null,k==="proxy"?he(Tk,{palette:se,cfg:J,snap:Q,width:x,onSelectRoute:(X)=>{qe(X),F("route-details")}},void 0,!1,void 0,this):null,k==="mcp"?he(hw,{palette:se,snap:Q,port:HC,portDraft:NC,selected:Ir,onPick:ne,onToggle:()=>{BC()},onCopy:UC,onToggleTool:(X)=>{jC(X)}},void 0,!1,void 0,this):null,k==="doctor"?he(gk,{palette:se,report:le,loading:bt,progress:Xr,error:Pt,selected:Ir,onPick:ne,onReload:()=>{Gs((X)=>X+1),ie("Re-running doctor")}},void 0,!1,void 0,this):null,k==="stats"?he(Gk,{palette:se,cfg:J,snap:Q,width:x,onRefresh:pn},void 0,!1,void 0,this):null,k==="config"?he(ok,{palette:se,cfg:J,width:x,selectedTask:Ir,scrollRef:Kt},void 0,!1,void 0,this):null,k==="profiles"?he(Sk,{palette:se,cfg:J,snap:Q,profile:Xe,selected:Ir,onPick:ne},void 0,!1,void 0,this):null,k==="setup"?he(Bk,{issues:J?b(J):["configuration not loaded"],palette:se,cfg:J,google:Pi,bootError:R,bootErrorMissing:s,step:Ir},void 0,!1,void 0,this):null,k==="settings"?he(Hk,{palette:se,items:Un,selected:Ir,locked:wr,width:x,onPick:ne,onActivate:Ws},void 0,!1,void 0,this):null]},void 0,!0,void 0,this),O==="slash"?he(qS,{palette:se,items:Ro,query:Z,selected:de,title:C==="tasks"?"tasks":C==="services"?"services":"commands",onQuery:W,onSubmit:vm},void 0,!1,void 0,this):null,O==="themes"?he(XS,{palette:se,themeName:ue,selected:B%St.length,termW:x,termH:w,onIndex:z,onPreview:Yr,onPick:ym},void 0,!1,void 0,this):null,O==="help"?he(bw,{palette:se,termW:x,termH:w,copyKey:_o,scrollRef:xr},void 0,!1,void 0,this):null,O==="leader"?he(OS,{palette:se,termW:x,termH:w},void 0,!1,void 0,this):null,O==="confirm"?he(DS,{palette:se,title:bm.title,body:bm.body,kind:ce,termW:x,termH:w},void 0,!1,void 0,this):null,O==="setup-wizard"?he(kS,{palette:se,termW:x,termH:w,step:Qr.step,answers:Qr.answers,repo:Qr.repo,draft:Qr.draft,authStatus:Qr.authStatus,onDraft:Qr.setDraft,onSubmit:()=>void Qr.advanceWizard()},void 0,!1,void 0,this):null,O==="log-details"?he(FS,{palette:se,event:rr,termW:x,termH:w,scrollRef:qt},void 0,!1,void 0,this):null,O==="route-details"?he(US,{palette:se,route:Xt,termW:x,termH:w,scrollRef:or},void 0,!1,void 0,this):null,O==="scroll-text"&&mt?he(GS,{palette:se,title:mt.title,body:mt.body,termW:x,termH:w,scrollRef:ir},void 0,!1,void 0,this):null,O==="config-edit"&&J?he(AS,{palette:se,path:J.configPath,initialValue:qC,error:VC,termW:x,termH:w,textareaRef:KC},void 0,!1,void 0,this):null,O==="plan"&&zs?he(jS,{palette:se,plan:zs,snap:Q,busy:GC,failed:XC,kind:mm,termW:x,termH:w,scrollRef:sr,initiallyRunning:WC,onDismiss:Eo},void 0,!1,void 0,this):null,Kl?null:he(Zx,{palette:se,screen:k,overlay:O,status:ft,paused:Kf,errorOnly:jl,width:x,copyKey:_o},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this)}var zk=I(()=>{te();Yt();qp();Mt();vr();wo();In();Lt();Ow();eS();oS();sS();dS();pS();gS();bS();SS();_S();TS();LS();IS();ff();$S();NS();BS();WS();KS();bf();VS();QS();ek();ik();ak();hk();vk();xk();$s();kk();Ak();$f();Ok();jk();Uk();Kk();Er();tt();Rr()});var Yk={};rP(Yk,{renderApp:()=>jf,runTui:()=>Hf,runTuiWithController:()=>MO});import{createCliRenderer as DO}from"@opentui/core";import{createRoot as IO}from"@opentui/react";import{jsxDEV as OO}from"@opentui/react/jsx-dev-runtime";async function MO(e,t){let r=e.loadTuiConfig(t.cfg.repoRoot,t.cfg.ui.keymap);await jf(e,t,r)}async function Hf(e,t){let r,n,o=!1;try{r=await e.openTui("",t)}catch(s){n=L(s),o=ba(s,qn)}let i=e.loadTuiConfig(r?.cfg.repoRoot??process.cwd(),r?.cfg.ui.keymap);await jf(e,r,i,n,o)}async function jf(e,t,r=e.loadTuiConfig(t?.cfg.repoRoot??process.cwd(),t?.cfg.ui.keymap),n,o=!1){Js();let i=Sm(),s=await DO({exitOnCtrlC:!1,useMouse:r.mouse,enableMouseMovement:r.mouse,useKittyKeyboard:{}}),a=IO(s),c=null;try{c=(await s.getPalette({timeout:120,size:16})).defaultBackground}catch{}await new Promise((l)=>{let d=(f)=>{let p=()=>{a.unmount(),i(),s.destroy(),l(),process.exit(0)};if(!t){p();return}t.close({detach:f,shutdownSupervisor:!0}).finally(p)},u=(f)=>{let p=()=>{a.unmount(),i(),s.destroy(),l(),process.exit(0)};if(!t){p();return}t.shutdown({stopServices:!f}).then(()=>t?.close({detach:!0})).finally(p)};a.render(OO(Vk,{workspace:db(e),controller:t,tui:r,onQuit:d,onDown:u,onAttached:(f)=>{t=f},bootError:n,bootErrorMissing:o,terminalBackground:c},void 0,!1,void 0,this))})}var Bf=I(()=>{ub();te();zk()});if(process.env.METADATA_SERVER_DETECTION===void 0)process.env.METADATA_SERVER_DETECTION="bios-only";if(process.env.GCE_METADATA_TIMEOUT===void 0)process.env.GCE_METADATA_TIMEOUT="0";import{existsSync as ia,readFileSync as ra}from"fs";import{dirname as IP,isAbsolute as Dm,join as oa,resolve as MP}from"path";Xs();Xs();import{spawnSync as dP}from"child_process";import{randomBytes as Zl}from"crypto";import{copyFileSync as uP,existsSync as fn,mkdirSync as pP,readdirSync as fP,readFileSync as ed,renameSync as td,statSync as mP,unlinkSync as Xl,writeFileSync as hP}from"fs";import{homedir as km}from"os";import{basename as gP,dirname as Ql,isAbsolute as vP,join as _t,resolve as yP}from"path";var bP=448,xP=384;function _i(e,t){let r=e.startsWith("~/")?_t(km(),e.slice(2)):e;return vP(r)?r:yP(t,r)}function cr(){let e=process.env.DEVCTL_HOME;if(e&&e!=="")return e;return _t(km(),".devctl")}function Mr(e){pP(e,{recursive:!0,mode:bP})}function To(e){let t=_t(cr(),"state",ar(e)),r=_t(cr(),"sessions",ar(e));if(!fn(t)&&fn(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(fn(o))uP(o,_t(t,n))}}}return Mr(t),t}function Qs(){let e=_t(cr(),"logs");return Mr(e),e}function Zs(){let e=_t(cr(),"exports");return Mr(e),e}function kr(){let e=_t(cr(),"credentials");return Mr(e),e}function ea(e,t=process.platform){if(t==="win32")return`\\\\.\\pipe\\devctl-${ar(e)}`;return _t(To(e),"devctl.sock")}function wP(e){return _t(To(e),"devctl.lock")}function Cm(e){return _t(To(e),"state.json")}function Ri(e){return _t(To(e),"bootstrap.log")}var SP=5,Pm="bootstrap-";function _m(e){let t=Ri(e);if(!fn(t))return;let r=To(e),n=_t(r,`${Pm}${rd()}.log`);try{td(t,n)}catch{return}kP(r)}function kP(e){let t;try{t=fP(e).filter((n)=>n.startsWith(Pm)&&n.endsWith(".log"))}catch{return}let r=t.map((n)=>{let o=_t(e,n);try{return{path:o,mtime:mP(o).mtimeMs}}catch{return}}).filter((n)=>n!==void 0).sort((n,o)=>o.mtime-n.mtime);for(let n=SP;n<r.length;n++)try{Xl(r[n].path)}catch{}}function Cr(e,t){Mr(Ql(e));let r=_t(Ql(e),`.${gP(e)}.tmp-${process.pid}-${Zl(4).toString("hex")}`);hP(r,t,{mode:xP}),td(r,e)}function rd(e=new Date){return`${e.toISOString().slice(0,19).replace(/:/g,"-")+"Z"}-${Zl(3).toString("hex")}`}function mn(e){return nd(Cm(e))}function nd(e){if(!fn(e))return;try{let t=JSON.parse(ed(e,"utf8"));if(Array.isArray(t.processes)&&typeof t.repo_root==="string")return t;return}catch{return}}function Rm(e,t){Cr(Cm(e),`${JSON.stringify(t,null,2)}
|
|
2234
|
-
`)}function
|
|
2235
|
-
`),t}function
|
|
2236
|
-
`,l=
|
|
2228
|
+
`:""}`;if(ue){if(Wn.length>0){f({title:"compose import",body:Gn}),e("scroll-text"),i("import is invalid; not written");return}if(z(Sr)){i(`configuration already exists at ${Sr}; not overwriting`);return}Ee(Sr,se.yaml),i(`wrote ${Sr}; /reload to apply`);return}f({title:"compose import (dry-run)",body:`${Gn}
|
|
2229
|
+
Enter is preview only. /import compose ${Jr} --write to save.`}),e("scroll-text")}catch(se){i(L(se))}return}case"diff":if(!p){i("no configuration loaded");return}f({title:"config sources",body:tS(p,h)}),e("scroll-text");return;case"daemon":{let _=p?.repoRoot;if(!_){i("no repository");return}let ue=B(_);if(!z(ue)){i("no daemon bootstrap log yet for this repository");return}f({title:"daemon bootstrap",body:de(ue)}),e("scroll-text");return}case"reload":if(!v)return;v.reload().then((_)=>{if(i(_.restart_required.length===0?"Configuration reloaded":`Reload requires restart: ${_.restart_required.join(", ")}`),_.restart_required.length>0)o("reload"),e("confirm");b()});return;case"doctor":l("doctor");return;case"stats":l("stats");return;case"start":await K(_o,g);return;case"stop":await A(_o);return;case"restart":{let _=kx(Re),ue=Ip(_.services,n);await ve(ue,g,_.cascade);return}case"run":{let _=Re[0]??"";if(!_){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(_);Ke(`task:${_}`),l("logs"),i(`task ${_} exited ${ue.code}`)}catch(ue){throw Ke(`task:${_}`),l("logs"),ue}return}case"exec":{let _=Cx(Re);if(!_.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(_.printEnv){if(!p?.services[_.service]){i(`unknown service ${_.service}`);return}if(_.reveal)C(!0);i(`Resolving environment for ${_.service}\u2026`);try{await E(_.service),S(_.service),i(`Resolved environment for ${_.service}`)}catch(ue){S(_.service),i(L(ue))}return}if(_.command.length===0){i("usage: /exec <service> -- <command\u2026>");return}try{let ue=await v.execService(_.service,_.command);Ke(`${_.service}:exec`),l("logs"),i(`${_.service}:exec exited ${ue.code}`)}catch(ue){throw Ke(`${_.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((_)=>!_),l("logs");return;case"filter":Ct((_)=>!_),l("logs");return;case"system":ge(),l("logs");return;case"clear":vt();return;case"reveal":{let _=!h;C(_),i(_?"Secrets visible this session":"Secrets hidden");return}case"wrap":{let _=nl(at);yt(_),i(`Log ${il(_)}`);return}case"copy":await y();return;case"export":{let _=ne(Re[0]);if(v)await v.logs({export:_,services:nr,level:Qe?"ERROR":Qt,search:qe,regex:ht,source:xr});else H(_,Kt);x.current=_,i(`Exported ${_}`);return}case"exports":{let _=x.current||ae();Q(_),i(`Opened ${ae()}`);return}case"regex":wr((_)=>!_),i(ht?"Substring search":"Regex search");return;case"since":or(Re[0]||new Date(Date.now()-3600000).toISOString()),i(`Logs since ${Re[0]||"1h"}`);return;case"until":qt(Re[0]||new Date().toISOString()),i(`Logs until ${Re[0]||"now"}`);return;case"history":{let _=nt(),ue=Re[0]||_[0];if(!ue){i("No persisted log sessions");return}ir(ft(ue)),i(`Loaded session ${ue}`);return}case"buffer":w();return;case"edit":{if(!p)return;let _=process.env.DEVCTL_EDITOR||process.env.EDITOR,ue=_?[_,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(_){i(L(_))}finally{setTimeout(()=>{J.current=!1},yM)}},[ve,K,A,n,p,vt,v,y,Qe,Kt,Qt,ht,qe,nr,xr,at,k,w,S,s,g,b,Vt,m,h,u,sr,r,c,ar,ge])}}var yM=50;var sS=I(()=>{te();xm();wm();vr();Lt();tt()});import{useCallback as aS,useRef as bM,useState as cS}from"react";function lS({cfg:e,readTextFile:t,setOverlay:r,setStatus:n,validateConfigText:o,writeTextFile:i,controller:s,setConfirmKind:a,refresh:c}){let l=bM(null),[d,u]=cS(""),[m,p]=cS(""),f=aS(()=>{if(!e)return;try{u(t(e.configPath)),p(""),r("config-edit")}catch(v){n(L(v))}},[e]),h=aS(()=>{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:m,setConfigEditError:p,openConfigBuffer:f,saveConfigBuffer:h}}var dS=I(()=>{te()});import{useEffect as xM}from"react";function uS({controller:e,cfg:t,paused:r,logSince:n,refresh:o,setLogs:i,setCfg:s,setConfigReloadError:a,setStatus:c}){return xM(()=>{if(!e)return;let l,d=!1,u=[],m=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)=>Jb(v,h,n,m))}if(d)d=!1,o()},f=e.onEvent((h)=>{if(h.type===Oo&&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===Ii){a(gx(h));return}if(h.type===ga)a(void 0),e.configSnapshot().then(s).catch((v)=>c(L(v)));if(d=!0,!l)l=setTimeout(p,30)});return()=>{if(f(),l)clearTimeout(l)}},[e,n,r,o]),{}}var pS=I(()=>{te();yr();vr()});import{useCallback as wM,useEffect as mS,useRef as fS,useState as wi}from"react";function hS({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,m]=wi(),[p,f]=wi(),[h,v]=wi(!1),[b,g]=wi(""),[C,E]=wi(0),[S,y]=wi({active:"Preparing diagnostics",checks:[]}),x=fS(void 0),w=fS(0),k=wM(async()=>{if(!e){a("Supervisor is not running");return}a("Probing configured identities\u2026");try{let D=await e.refreshAuth();s((G)=>G?{...G,identity:D}:G);let O=Object.values(D.service_account_status),F=O.filter((G)=>G==="available").length,Z=O.filter((G)=>G==="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 mS(()=>{c(r?.google.project_id??"").then(m).catch((D)=>{a(L(D))})},[r?.google.project_id,n?.identity.project]),mS(()=>{if(o!=="doctor"||!r)return;let D=x.current,O=n?.session_id??"";if(D?.cfg===r&&D.tick===C&&D.sessionID===O&&D.reloadError===i)return;x.current={cfg:r,tick:C,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(G){Z=L(G)}}l(r,(G)=>{if(F===w.current)y(G)},{services:n?.services,proxyRunning:n?.proxy.running,repositoryConfigError:Z}).then((G)=>{if(F!==w.current)return;f(G)}).catch((G)=>{if(F!==w.current)return;g(L(G))}).finally(()=>{if(F===w.current)v(!1)})},[o,r,C,n?.session_id,i]),{google:u,setGoogle:m,doctor:p,doctorLoading:h,doctorError:b,doctorProgress:S,setDoctorTick:E,refreshAuth:k}}var gS=I(()=>{te()});import{useCallback as Sm,useState as Tl}from"react";function vS({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,m]=Tl(),[p,f]=Tl([]),[h,v]=Tl(!1),[b,g]=Tl("start"),C=Sm(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"),f(D.waves.flat().filter((Z)=>bo(n?.services[Z]))),m(D),s("plan"),v(!0);let O=k.services.filter((Z)=>!bo(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(Aw(F))}catch(w){v(!1),await o(),i(L(w))}},[r,e,o,n]),E=Sm(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"),f([]),m(w),s("plan"),v(!0),await e.stop(y),await o(),v(!1),i(Lw(w))}catch(w){v(!1),await o(),i(L(w))}},[r,e,o,n]),S=Sm(async(y,x,w=!1)=>{if(!e||!r){i("no configuration loaded");return}try{let k=Tw(r,y,x),D=w?l(r,k.services):d(r,k.services);g("restart"),f([]),m(D),s("plan"),v(!0),await e.restart(k.services,w),await o(),v(!1);let O=Cl(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:C,beginStop:E,beginRestart:S}}var yS=I(()=>{te();Pl();Lt()});import{useCallback as Or,useEffect as qr,useMemo as Hn,useRef as SM,useState as xe}from"react";function xS(e,t,r,n,o){let i=$p(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(ol(t.filteredLen));n(a),o(i.selected)}function wS({controller:e,tui:t,names:r,screen:n,refresh:o,setStatus:i}){let[s,a]=xe([]),[c,l]=xe(!1),[d,u]=xe(!1),[m,p]=xe(!0),[f,h]=xe(""),[v,b]=xe(!1),[g,C]=xe(0),[E,S]=xe(""),[y,x]=xe(""),[w,k]=xe(""),[D,O]=xe(""),[F,Z]=xe(!1),[G,R]=xe(0),[j,B]=xe(Mr-1),[z,de]=xe(!1),[Ee,Ne]=xe(0),ne=bS,[H,ae]=xe(t.log_timestamps!==!1),[Q,nt]=xe(t.log_metadata!==!1),[ft,ie]=xe(()=>[...Yb]),[Le]=xe(""),[Me]=xe(""),[Ke,gt]=xe(!1),[Xe,Ct]=xe("focus"),[ge,vt]=xe(!1),[at,yt]=xe(Mr-1),[nr,Qe]=xe(!1),[Qt,qe]=xe(-1),[ht,xr]=xe(),[Kt,wr]=xe(""),[or,qt]=xe(!1),[ir,sr]=xe(!1),ar=SM([]),[Vt,ce]=xe(0),K=Hn(()=>sl(r,s,ft),[r,s,ft]);qr(()=>{ie((le)=>{let bt=new Set(le),Pt=!1;for(let Qr of s)if(Qr.service!==""&&!r.includes(Qr.service)&&!bt.has(Qr.service))bt.add(Qr.service),Pt=!0;return Pt?[...bt].sort():le})},[s,r]);let A=Hn(()=>xo(s,{service:w,services:ne,errorOnly:d,search:f,regex:Ke,source:Me,since:E,until:y,systemLogs:m}),[d,Ke,f,w,ne,E,Me,y,s,m]),ve=Hn(()=>Op(A,ge,Vt),[A,ge,Vt]),J=ve.items,Dt=Hn(()=>xo(s,{service:D,services:ne,errorOnly:d,search:f,regex:Ke,source:Me,since:E,until:y,systemLogs:m}),[d,Ke,f,D,ne,E,Me,y,s,m]),ct=Hn(()=>Op(Dt,z,Ee),[Dt,z,Ee]),Re=ct.items;qr(()=>{qe(-1),vt(!1),yt(Math.max(0,Math.min(Mr,A.length)-1))},[d,f,w,m]),qr(()=>{de(!1),B(Math.max(0,Math.min(Mr,Dt.length)-1))},[d,f,D,m]),qr(()=>{if(n!=="logs")Qe(!1)},[n]),qr(()=>{if(n!=="logs"||ge)return;yt(Math.max(0,Math.min(Mr,A.length)-1))},[A.length,ge,n]),qr(()=>{if(n!=="logs"||z)return;B(Math.max(0,Math.min(Mr,Dt.length)-1))},[Dt.length,z,n]);let _o=Or(()=>{if(!ge)ce(ol(A.length)),vt(!0)},[A.length,ge]),_=Or(()=>{if(!z)Ne(ol(Dt.length)),de(!0)},[Dt.length,z]),ue=Hn(()=>({sliceLen:J.length,windowStart:ve.start,windowNewer:ve.newer,filteredLen:A.length,pinned:ge}),[A.length,ge,J.length,ve.newer,ve.start]),Jr=Hn(()=>({sliceLen:Re.length,windowStart:ct.start,windowNewer:ct.newer,filteredLen:Dt.length,pinned:z}),[Dt.length,z,Re.length,ct.newer,ct.start]),Xr=Or((le)=>xS(le,ue,ce,vt,yt),[ue]),Sr=Or((le)=>xS(le,Jr,Ne,de,B),[Jr]),se=Or((le)=>{if(F&&G===1){Sr(le);return}Xr(le)},[Xr,Sr,G,F]),Wn=Or((le)=>{let bt=J.length,Pt=$p(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]),Gn=Or(()=>{if(vt(!1),de(!1),qe(-1),n==="logs")yt(Math.max(0,Math.min(Mr,A.length)-1)),B(Math.max(0,Math.min(Mr,Dt.length)-1));C((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]),$r=Hn(()=>({services:F?bS:w!==""?[w]:ne,level:d?"ERROR":Le,search:f,regex:Ke,source:Me,since:E,until:y}),[d,Le,Ke,f,w,ne,E,Me,y,F]),mn=Or(async()=>{if(!e)return;try{xr(await e.logsStats($r))}catch(le){i(L(le))}},[e,$r]),Ys=Or(async()=>{if(!e||c)return;try{let le=await e.logsPage($r);a((bt)=>Xb(bt,le.events)),wr(le.prevCursor),qt(le.hasPrev),await mn()}catch(le){i(L(le))}},[e,$r,c,mn]),_i=Or(()=>{a([]),S(new Date().toISOString()),vt(!1),de(!1),i("Cleared on-screen log buffer")},[]),Ei=Or(()=>{p((le)=>!le)},[]);return qr(()=>{o().then(()=>Ys())},[o,Ys]),qr(()=>{ar.current=s},[s]),qr(()=>{let le=Fp(ge,ve.start,or),bt=F&&Fp(z,ct.start,or);if(!e||ir||!le&&!bt)return;sr(!0),e.logsPage({...$r,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 Qr=ar.current,Js=Qb(Qr,Pt.events);a(Js);let Eo=Js.length-Qr.length;if(Eo>0){if(ge)ce((kr)=>kr+Eo);if(z)Ne((kr)=>kr+Eo)}wr(Pt.prevCursor),qt(Pt.hasPrev)}).catch((Pt)=>i(L(Pt))).finally(()=>sr(!1))},[e,$r,ir,or,ge,z,Kt,ve.start,ct.start,F]),qr(()=>{if(!e||c||n!=="logs")return;let le=setInterval(()=>{mn()},kM);return()=>clearInterval(le)},[e,c,mn,n]),{logs:s,setLogs:a,paused:c,setPaused:l,errorOnly:d,setErrorOnly:u,showSystemLogs:m,logSearch:f,setLogSearch:h,logSearchFocused:v,setLogSearchFocused:b,logFollow:g,logSince:E,setLogSince:S,setLogUntil:x,logService:w,setLogService:k,logServiceB:D,setLogServiceB:O,splitLogs:F,splitFocus:G,setSplitFocus:R,toggleSplitLogs:()=>{if(F){R(0),Z(!1);return}if(D===""){let le=r.find((bt)=>bt!==w);if(le!==void 0)O(le)}Z(!0)},cycleSplitFocus:()=>R((le)=>le===0?1:0),setActiveLogService:(le)=>{let bt=(Pt)=>typeof le==="function"?le(Pt):le;if(F&&G===1){O(bt);return}k(bt)},logSliceB:Re,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:nr,setLogsFullscreen:Qe,dashboardLogCursor:Qt,setDashboardLogCursor:qe,logFacets:ht,logSources:K,filteredLogs:A,filteredLogsB:Dt,logWindow:ve,logSlice:J,pinLogView:_o,pinLogViewB:_,applyLogCursor:se,applyLogCursorA:Xr,applyLogCursorB:Sr,applyDashboardLogCursor:Wn,jumpToLatestLogs:Gn,clearLogs:_i,toggleSystemLogs:Ei}}var bS,kM=2000;var SS=I(()=>{te();vr();bS=[]});import{useCallback as Po,useState as kS}from"react";function CS({tui:e,controller:t,cfg:r,persistPrefs:n,snap:o,refresh:i,setStatus:s}){let[a,c]=kS(""),[l,d]=kS(()=>e.mcp_port??fo(t?.cfg.repoRoot??process.cwd())),u=Po((g)=>{let C=Tn(g);d(C);let E=r?.repoRoot??process.cwd();if(Fy(E,C)){n({mcp_port:null},`MCP port ${C} (default)`);return}n({mcp_port:C},`MCP port ${C}`)},[r,n]),m=Po(()=>{if(a==="")return l;let g=$y(a,l);return c(""),u(g),g},[l,a,u]),p=Po(async(g)=>{if(!t||o?.mcp?.running!==!0)return;await t.mcpStop(),await t.mcpStart({port:g}),await i()},[t,i,o?.mcp?.running]),f=Po(async(g)=>{if(!t){s("Supervisor is not running");return}let C=o?.mcp?.disabled_tools??[],E=Dn(g.name,C),S=E?[...C,g.name]:C.filter((y)=>y!==g.name);try{let y=await t.mcpSetTools(S);n({mcp_disabled_tools:y},`${g.label} ${E?"disabled":"enabled"}`),await i()}catch(y){s(L(y))}},[t,n,i,o]),h=Po(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=Po((g)=>{bl(g.text).then(()=>{s(`Copied ${g.title} snippet`)}).catch((C)=>{s(L(C))})},[]),b=Po((g)=>{let C=lm(g);if(C===void 0)return!1;let E=o?.mcp?.address??vi(o?.mcp?.port??l),S=js(E,o?.mcp?.token??"")[C];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:m,restartMcpOnPort:p,toggleMcpTool:f,toggleMcp:h,copyMcpSnippet:v,copyFocusedMcpSnippet:b}}var PS=I(()=>{te();kl();bs();Qp();Us()});import{RGBA as CM}from"@opentui/core";import{useCallback as un,useEffect as PM,useMemo as RS,useRef as Al,useState as Ll}from"react";function _S({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:m,screen:p}){let[f,h]=Ll(e.theme||t?.cfg.ui.theme||"devctl"),v=Al(f),[b,g]=Ll(e.mouse),C=Al(e.mouse),[E,S]=Ll(e.leader_timeout),y=Al(e.leader_timeout),[x,w]=Ll(()=>Ps(e.font_size)),k=Al(x),D=kb(r()),O=RS(()=>{let H=yb(f);if(Mc(f)!=="terminal"||!bp(n))return H;let ae=n??H.background;return{...H,background:ae,panel:ae,element:ae,inverse:ae}},[n,f]),F=Mc(f)==="terminal"&&bp(n)?CM.defaultBackground(n??void 0):O.background,Z=RS(()=>_b({themeName:f,fontSize:x,mouse:b,leaderMs:E,locked:D,configPath:D?e.path||Cp(r(),o()):Cp(r(),o()),mcpRunning:i?.mcp?.running===!0}),[x,E,b,D,i?.mcp?.running,f,e.path]),G=un((H,ae)=>{if(D){s(`${ae} session only`);return}let Q=a(H);s(`${ae} saved ${Q}`)},[D]),R=un((H)=>{h(H),v.current=H,G({theme:H},`theme ${H}`)},[G]),j=un(()=>{let H=!b;g(H),C.current=H,G({mouse:H},`mouse ${H?"on":"off"} restart TUI to apply clicks`)},[b,G]),B=un((H)=>{S(H),y.current=H,G({leader_timeout:H},`leader ${H}ms`)},[G]),z=un((H)=>{let ae=Ps(H);w(ae),k.current=ae,G({font_size:ae},`display ${Nc(ae)}`)},[G]),de=un(()=>{let H=Hc();h(H.theme),v.current=H.theme,g(H.mouse),C.current=H.mouse,S(H.leader_timeout),y.current=H.leader_timeout,w(H.font_size),k.current=H.font_size,G(H,"restored default preferences")},[G]),Ee=un((H)=>{if(H.id==="theme"){c(Math.max(0,St.indexOf(f))),l("themes");return}if(H.id==="mouse"){j();return}if(H.id==="leader"){B(E);return}if(H.id==="font"){z(x);return}if(H.id==="reset"){d("reset-prefs"),l("confirm");return}if(H.id==="mcp"){u("mcp"),m(0);return}s(H.detail)},[z,B,x,E,f,j]),Ne=un((H,ae)=>{let Q=In(Z,ae);if(!Q)return;if(Q.id==="theme"){R(Cb(f,H));return}if(Q.id==="leader"){B(Pb(E,H));return}if(Q.id==="font"){z(Rs(x,H));return}if(Q.id==="mouse")j()},[z,B,x,E,R,Z,f,j]),ne=un(()=>{h(v.current)},[]);return PM(()=>{if(p==="settings")return;h(v.current),w(k.current),S(y.current),g(C.current)},[p]),{themeName:f,setThemeName:h,leaderMs:E,fontSize:x,prefsLocked:D,palette:O,rootBackground:F,settingRows:Z,persistPrefs:G,persistTheme:R,toggleMouse:j,applyFont:z,applyReset:de,activateSetting:Ee,cycleSetting:Ne,revertThemePreview:ne}}var ES=I(()=>{Dr();tt()});import{jsxDEV as Gs}from"@opentui/react/jsx-dev-runtime";function TS(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],m=wt.length,p=RM(u.id,i,s);return Gs(mt,{palette:t,title:`setup ${o+1}/${m} ${u.title}`,bottomTitle:"enter next \xB7 esc cancel",termW:r,termH:n,preferW:64,preferH:12,borderColor:t.primary,gap:1,children:[Gs("text",{fg:t.muted,wrapMode:"word",children:p},void 0,!1,void 0,this),u.id==="auth"||u.id==="write"||u.id==="repo"?Gs("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):Gs("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),Gs(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 km(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 AS(e,t,r,n){let o={...t};if(e==="name")o.name=r.trim()||Cn(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=$a(r,t.proxyPort);else if(e==="profile")o.profile=r.trim();return o}function RM(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 Cm=I(()=>{Fa();Pe()});import{useCallback as Pm,useState as Dl}from"react";function LS({workspace:e,renderer:t,setOverlay:r,setStatus:n,setScreen:o,onAttached:i,setController:s,setCfg:a,setBootError:c}){let l=process.cwd(),[d,u]=Dl(0),[m,p]=Dl(()=>Cn(l)),[f,h]=Dl(""),[v,b]=Dl("Checking Application Default Credentials\u2026"),g=Pm(()=>{let S=Cn(l);u(0),p(S),h(km("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]),C=Pm(async(S)=>{try{Ha(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]),E=Pm(async()=>{let S=wt[d];if(!S)return;let y=AS(S.id,m,f,l);if(S.id==="auth"){if(!(await e.detectGoogle(y.project)).adcAvailable)try{await El(t,()=>e.loginGoogle())}catch(D){n(L(D))}}if(p(y),S.id==="write"){await C(y);return}let x=Math.min(d+1,wt.length-1),w=wt[x];u(x),h(w?km(w.id,y,l):"")},[m,f,C,t,l,d,e,n]);return{step:d,answers:m,draft:f,authStatus:v,repo:l,setDraft:h,startWizard:g,advanceWizard:E}}var DS=I(()=>{Fa();ja();te();Cm()});import{useCallback as _M,useEffect as IS,useState as Rm}from"react";function MS({controller:e,cfg:t,envService:r}){let[n,o]=Rm(),[i,s]=Rm(!1),[a,c]=Rm(""),l=n?.name===r,d=l?n?.env:void 0,u=r===""?"config":i&&!l?"loading":l?"resolved":a!==""?"error":"config",m=l||r===""?"":a;IS(()=>{o(void 0),c("")},[t]),IS(()=>{if(!e||r===""||!t?.services[r])return;if(n?.name===r)return;let f=!1;return s(!0),c(""),e.execService(r,[],!0).then((h)=>{if(f)return;o({name:r,env:h.environment??{}}),c("")}).catch((h)=>{if(f)return;c(L(h))}).finally(()=>{s(!1)}),()=>{f=!0}},[t,e,r,n?.name]);let p=_M(async(f)=>{if(!e)return;try{let h=await e.execService(f,[],!0);o({name:f,env:h.environment??{}}),c("")}catch(h){throw c(L(h)),h}},[e]);return{inspectorEnv:d,inspectorEnvStatus:u,inspectorEnvError:m,resolveEnvironment:p}}var OS=I(()=>{te()});import{jsxDEV as jn}from"@opentui/react/jsx-dev-runtime";function $S(e){let{palette:t,path:r,initialValue:n,error:o,termW:i,termH:s,textareaRef:a}=e;return jn(mt,{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:[jn("box",{height:1,overflow:"hidden",children:jn("text",{fg:t.muted,wrapMode:"none",children:r},void 0,!1,void 0,this)},void 0,!1,void 0,this),jn("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!==""?jn("box",{height:2,overflow:"hidden",children:jn("text",{fg:t.error,wrapMode:"word",children:o},void 0,!1,void 0,this)},void 0,!1,void 0,this):jn("box",{height:1,overflow:"hidden",children:jn("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 FS=I(()=>{Pe();Lr()});import{jsxDEV as _m}from"@opentui/react/jsx-dev-runtime";function NS(e){let{palette:t,title:r,body:n,kind:o,termW:i,termH:s}=e,a=hx(o),c=a.map((l)=>`${l.key} ${l.label}`).join(" \xB7 ");return _m(mt,{palette:t,title:r,bottomTitle:c,termW:i,termH:s,preferW:52,preferH:8,borderColor:t.warning,gap:1,children:[_m("text",{fg:t.text,wrapMode:"word",children:n},void 0,!1,void 0,this),_m(it,{palette:t,hints:a},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}var HS=I(()=>{yr();Pe()});import{jsxDEV as jS}from"@opentui/react/jsx-dev-runtime";function BS(e){let{palette:t,termW:r,termH:n}=e,o=Kp();return jS(mt,{palette:t,title:"leader",termW:r,termH:n,preferW:68,preferH:5,anchor:"bottom",children:jS(it,{palette:t,hints:o},void 0,!1,void 0,this)},void 0,!1,void 0,this)}var US=I(()=>{ko();Pe()});import{jsxDEV as Xt,Fragment as EM}from"@opentui/react/jsx-dev-runtime";function WS(e){let{palette:t,event:r,termW:n,termH:o,scrollRef:i}=e;if(!r)return null;let s=r.raw?Wb(r.raw):void 0,a=r.message.length>120||r.message.includes(`
|
|
2231
|
+
`)||s!==void 0;return Xt(mt,{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:Xt("scrollbox",{ref:i,focused:!1,stickyScroll:!1,scrollX:!1,style:Ue(t),children:Xt("box",{flexDirection:"column",overflow:"hidden",children:[Xt("text",{fg:t.text,wrapMode:"word",children:tl(r.message)},void 0,!1,void 0,this),Xt("text",{fg:t.muted,children:`time ${r.timestamp}`},void 0,!1,void 0,this),Xt("text",{fg:Ze(r.service,t),children:`service ${r.service}`},void 0,!1,void 0,this),Xt("text",{fg:t.muted,children:`source ${r.source}${r.stream?` / ${r.stream}`:""}`},void 0,!1,void 0,this),Xt("text",{fg:t.muted,children:`level ${el(r.level)}`},void 0,!1,void 0,this),Xt("text",{fg:t.muted,children:`pid ${r.pid||"\u2014"}`},void 0,!1,void 0,this),Xt("text",{fg:t.muted,children:`request ${r.request_id||"\u2014"}`},void 0,!1,void 0,this),Xt("text",{fg:t.muted,children:`identity ${r.identity||"\u2014"}`},void 0,!1,void 0,this),s===void 0?null:Xt(EM,{children:[Xt("text",{fg:t.muted,children:"raw json"},void 0,!1,void 0,this),Xt("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 GS=I(()=>{vr();Pe();tt()});import{jsxDEV as W}from"@opentui/react/jsx-dev-runtime";function Em(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 TM(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 KS(e){return W("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,children:W("text",{wrapMode:"none",children:[W("span",{fg:e.palette.primary,children:e.label.toUpperCase()},void 0,!1,void 0,this),e.detail?W("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 qS(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,m=new Set(d),p=r.waves.map((R,j)=>({wave:R.filter((B)=>!m.has(B)),waveIdx:j})).filter(({wave:R})=>R.length>0),f=Iw(r,n,s),h=r.waves[f.currentWaveIndex]??[],v=Ws(h,n,s),b=Em(v,t),g=TM(o,i,v,t),C=Ow(s,o,i,r.profile),E=$w(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,G=r.profile?`PROFILE ${r.profile}`:s.toUpperCase();return W(mt,{palette:t,title:C,bottomTitle:"enter close \xB7 esc hide (operation continues)",termW:a,termH:c,preferW:98,preferH:28,borderColor:g.color,gap:0,children:[W("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,backgroundColor:t.element,children:[W(Be,{palette:t,label:`${g.icon} ${g.label.toUpperCase()}`,tone:g.tone},void 0,!1,void 0,this),W("box",{paddingLeft:1,flexGrow:1,overflow:"hidden",children:W("text",{fg:t.text,wrapMode:"none",children:G},void 0,!1,void 0,this)},void 0,!1,void 0,this),W(Be,{palette:t,label:`${f.ready}/${f.total} READY`,tone:f.isComplete?"success":"info"},void 0,!1,void 0,this),W(Be,{palette:t,label:`WAVE ${Math.min(f.currentWaveIndex+1,Math.max(1,f.totalWaves))}/${Math.max(1,f.totalWaves)}`,tone:b.tone},void 0,!1,void 0,this)]},void 0,!0,void 0,this),W("box",{height:2,flexDirection:"column",overflow:"hidden",flexShrink:0,paddingLeft:1,paddingRight:1,children:[W("box",{height:1,flexDirection:"row",overflow:"hidden",children:[W("text",{wrapMode:"none",children:[W("span",{fg:t.muted,children:"PROGRESS "},void 0,!1,void 0,this),W("span",{fg:g.color,children:`[${f.progressBar}]`},void 0,!1,void 0,this),W("span",{fg:t.text,children:` ${f.percent}%`},void 0,!1,void 0,this)]},void 0,!0,void 0,this),W("box",{flexGrow:1},void 0,!1,void 0,this),W("text",{wrapMode:"none",children:W("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),W("box",{height:1,overflow:"hidden",children:W("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.":E.primary},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this),W(KS,{palette:t,label:"Pipeline",detail:`${r.waves.length} execution wave${r.waves.length===1?"":"s"}`},void 0,!1,void 0,this),W("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,backgroundColor:t.element,children:r.waves.map((R,j)=>{let B=Ws(R,n,s),z=Em(B,t),de=Z?`W${j+1} ${z.icon} ${R.length}`:`W${j+1} ${z.icon} ${z.label} \xB7 ${R.length}`;return W("box",{flexDirection:"row",overflow:"hidden",flexShrink:0,children:[W(Be,{palette:t,label:de,tone:z.tone},void 0,!1,void 0,this),j<r.waves.length-1?W("box",{width:3,flexShrink:0,overflow:"hidden",children:W("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?W("box",{minHeight:1,flexDirection:"row",overflow:"hidden",flexShrink:0,backgroundColor:t.element,paddingLeft:1,children:W("text",{fg:t.error,wrapMode:"word",children:`BLOCKED ${r.blockers?.map((R)=>`${R.name}: ${R.message}`).join(" \xB7 ")}`},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,d.length>0?W("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,paddingLeft:1,children:W("text",{wrapMode:"none",children:[W("span",{fg:t.success,children:"\u2713 ALREADY READY"},void 0,!1,void 0,this),W("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,W(KS,{palette:t,label:"Wave details",detail:"live process and health state"},void 0,!1,void 0,this),W("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,backgroundColor:t.element,paddingLeft:1,children:[W("box",{width:2,flexShrink:0},void 0,!1,void 0,this),W("box",{width:w,flexShrink:0,overflow:"hidden",children:W("text",{fg:t.muted,children:oe("SERVICE",w)},void 0,!1,void 0,this)},void 0,!1,void 0,this),W("box",{width:12,flexShrink:0,overflow:"hidden",children:W("text",{fg:t.muted,children:oe("STATE",12)},void 0,!1,void 0,this)},void 0,!1,void 0,this),O?W("box",{width:9,flexShrink:0,overflow:"hidden",children:W("text",{fg:t.muted,children:oe("PORT",9)},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,F?W("box",{flexGrow:1,overflow:"hidden",children:W("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),W("scrollbox",{ref:l,focused:!1,stickyScroll:!1,scrollX:!1,style:Ue(t),children:W("box",{flexDirection:"column",overflow:"hidden",gap:1,children:p.map(({wave:R,waveIdx:j})=>{let B=Ws(R,n,s),z=Em(B,t),de=`${Mw(s,j,B)} \xB7 ${R.length} service${R.length===1?"":"s"}`;return W("box",{border:!0,borderStyle:"rounded",borderColor:z.color,title:de,titleColor:z.color,flexDirection:"column",paddingLeft:1,paddingRight:1,flexShrink:0,overflow:"hidden",children:R.map((Ee)=>{let Ne=n?.services[Ee],ne=rr(Ne),H=Dw(Ee,r,n,s),ae=Ee===i||ne==="FAILED",Q=ne==="UNHEALTHY",nt=ne==="HEALTHY"||s==="stop"&&ne==="STOPPED",ft=yo(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 W("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,children:[W("box",{width:2,flexShrink:0,overflow:"hidden",children:W("text",{fg:Le,children:ie},void 0,!1,void 0,this)},void 0,!1,void 0,this),W("box",{width:w,flexShrink:0,overflow:"hidden",children:W("text",{fg:Ze(Ee,t),wrapMode:"none",children:oe(Ee,w)},void 0,!1,void 0,this)},void 0,!1,void 0,this),W("box",{width:12,flexShrink:0,overflow:"hidden",children:W("text",{fg:et(t,ne),wrapMode:"none",children:oe(ne,12)},void 0,!1,void 0,this)},void 0,!1,void 0,this),O?W("box",{width:9,flexShrink:0,overflow:"hidden",children:W("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?W("box",{flexGrow:1,overflow:"hidden",children:W("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),W("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:[W("text",{fg:S,wrapMode:"none",children:E.primary},void 0,!1,void 0,this),W("text",{fg:i?t.inverse:t.muted,wrapMode:"none",children:E.secondary},void 0,!1,void 0,this)]},void 0,!0,void 0,this)]},void 0,!0,void 0,this)}var VS=I(()=>{Ot();Pl();Lt();Pe();tt()});import{jsxDEV as br}from"@opentui/react/jsx-dev-runtime";function zS(e){let{palette:t,route:r,termW:n,termH:o,scrollRef:i}=e;if(!r)return null;return br(mt,{palette:t,title:r.name,bottomTitle:"j/k scroll \xB7 esc close",termW:n,termH:o,preferW:78,preferH:18,gap:1,children:br("scrollbox",{ref:i,focused:!1,stickyScroll:!1,scrollX:!1,style:Ue(t),children:br("box",{flexDirection:"column",overflow:"hidden",children:[br("text",{fg:t.muted,children:`auth ${r.authType||"none"}`},void 0,!1,void 0,this),br("text",{fg:t.muted,children:`identity ${r.identityType||"\u2014"}`},void 0,!1,void 0,this),br("text",{fg:t.text,wrapMode:"word",children:`account ${r.serviceAccount||"\u2014"}`},void 0,!1,void 0,this),br("text",{fg:t.text,wrapMode:"word",children:`audience ${r.audience||"\u2014"}`},void 0,!1,void 0,this),br("text",{fg:t.muted,wrapMode:"word",children:`client id ${r.clientId||"ADC default"}`},void 0,!1,void 0,this),br("text",{fg:t.muted,wrapMode:"word",children:`client secret ${r.clientSecret||"\u2014"}`},void 0,!1,void 0,this),br("text",{fg:t.muted,children:`match host ${r.matchHost||"*"}`},void 0,!1,void 0,this),br("text",{fg:t.muted,children:`match path ${r.matchPath||"*"}`},void 0,!1,void 0,this),br("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 YS=I(()=>{Pe()});import{jsxDEV as Il}from"@opentui/react/jsx-dev-runtime";function JS(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 Il(mt,{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:Il("scrollbox",{ref:s,focused:!1,stickyScroll:!1,scrollX:!1,style:Ue(t),children:Il("box",{flexDirection:"column",overflow:"hidden",children:Il("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 XS=I(()=>{Pe()});import{jsxDEV as Wt}from"@opentui/react/jsx-dev-runtime";function QS(e){let{palette:t,items:r,query:n,selected:o,onQuery:i,onSubmit:s,title:a="commands"}=e,c=So(n)!=="",l=Fx(o,Tm,r.length),d=c?r.slice(l,l+Tm):Nx(r,o,Tm),u=c?[{group:"",items:d}]:Up(d),m=Math.max(d.length+(c?0:u.length),1),p=fi(r,o),f=Dx(r);return Wt("box",{height:m+AM,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=ul(b)===(p?ul(p):"");return Wt("box",{height:1,flexDirection:"row",overflow:"hidden",paddingLeft:1,paddingRight:1,backgroundColor:g?t.highlight:t.panel,children:[Wt("box",{width:f,flexShrink:0,overflow:"hidden",children:Wt("text",{fg:g?t.primary:t.text,wrapMode:"none",children:`${g?"\u203A":" "} /${Wp(b).slice(0,Bp)}`},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:Lx(b)},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},ul(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 Tm=10,AM=3;var ZS=I(()=>{ko()});import{jsxDEV as rk}from"@opentui/react/jsx-dev-runtime";function nk(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?`${yp[u]} \xB7 current`:yp[u],value:u})),d=ll(o,i,ek,tk,"center",!vo(be()));return rk(mt,{palette:t,title:"/themes",bottomTitle:"enter save \xB7 esc revert",termW:o,termH:i,preferW:ek,preferH:tk,children:rk("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 m=St[u];if(m)a(m)},onSelect:(u,m)=>{let p=m?.value;if(typeof p==="string"&&p!=="")c(p)}},void 0,!1,void 0,this)},void 0,!1,void 0,this)}var ek=52,tk=22;var ok=I(()=>{Mt();yr();Pe();Dr();tt()});function Ml(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 Ol(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 ik(e){let{palette:t,cfg:r,google:n,identity:o}=e;if(!n&&!o)return st(yl,{palette:t,label:"Checking Application Default Credentials\u2026"},void 0,!1,void 0,this);let i=LM(r,o),s=o?.user||n?.userEmail||"",{project:a,source:c}=Ml(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),m=u.some((h)=>h.ok===!0),p=u.some((h)=>h.ok===!1),f=m?"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(Ol,{palette:t,title:"Google identity",tone:l?"success":"warning",children:[st(Co,{palette:t,label:"user",value:s||"(unknown)"},void 0,!1,void 0,this),st(Co,{palette:t,label:"project",value:a||"(unset)"},void 0,!1,void 0,this),st(Co,{palette:t,label:"source",value:c||"\u2014"},void 0,!1,void 0,this),st(Co,{palette:t,label:"ADC",value:l?"\u2713 available":"\u2717 missing",tone:l?"success":"error"},void 0,!1,void 0,this),st(Co,{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(Ol,{palette:t,title:`Service accounts (${i.length})`,tone:u.length===0?"muted":m?"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(Co,{palette:t,label:"impersonation",value:f,tone:m?"success":u.length>0?"warning":"muted"},void 0,!1,void 0,this)]},void 0,!0,void 0,this),st(Ol,{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(Ol,{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 LM(e,t){let r=new Map((e?lu(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 sk=I(()=>{Tr();Jt();Pe()});import{jsxDEV as M}from"@opentui/react/jsx-dev-runtime";function dk(e){let{palette:t,cfg:r,width:n,selectedTask:o=0}=e,i=be();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(xi+8,n-6-i.pad*2),a=s>=oS,c=a?Math.floor((s-ck)/2):s,l=Jw(r),d=Xw(r),u=Qw(r),m=Zw(r),p=eS(r);return M(kt,{palette:t,title:`config ${r.project.name||"unnamed"}`,children:[M(pt,{palette:t,items:Gw(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:lk,children:[M(Am,{wide:a,left:M(Vr,{palette:t,title:"project",children:M(Si,{palette:t,facts:Kw(r,jt),width:c},void 0,!1,void 0,this)},void 0,!1,void 0,this),right:M(Vr,{palette:t,title:"google",children:M(Si,{palette:t,facts:qw(r),width:c},void 0,!1,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this),M(Am,{wide:a,left:M(Vr,{palette:t,title:"runtime",children:M(Si,{palette:t,facts:Vw(r),width:c},void 0,!1,void 0,this)},void 0,!1,void 0,this),right:M(Vr,{palette:t,title:"logs",children:M(Si,{palette:t,facts:zw(r),width:c},void 0,!1,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this),M(Vr,{palette:t,title:`proxy ${r.proxy.enabled?"enabled":"disabled"}`,children:[M(Si,{palette:t,facts:Yw(r),width:s},void 0,!1,void 0,this),M(IM,{palette:t,routes:d,width:s},void 0,!1,void 0,this)]},void 0,!0,void 0,this),M(Vr,{palette:t,title:`services ${l.length}`,children:M(DM,{palette:t,rows:l,width:s},void 0,!1,void 0,this)},void 0,!1,void 0,this),M(Vr,{palette:t,title:`tasks ${p.length}`,children:M(ak,{palette:t,rows:p,empty:"no tasks",width:s,selected:o},void 0,!1,void 0,this)},void 0,!1,void 0,this),M(Am,{wide:a,left:M(Vr,{palette:t,title:`profiles ${u.length}`,children:M(MM,{palette:t,rows:u,width:c},void 0,!1,void 0,this)},void 0,!1,void 0,this),right:M(Vr,{palette:t,title:`templates ${m.length}`,children:M(ak,{palette:t,rows:m,empty:"no templates",width:c},void 0,!1,void 0,this)},void 0,!1,void 0,this)},void 0,!1,void 0,this),M(Vr,{palette:t,title:"extras",children:M(Si,{palette:t,facts:rS(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 Am(e){let{wide:t,left:r,right:n}=e;if(!t)return M("box",{flexDirection:"column",overflow:"hidden",gap:lk,flexShrink:0,children:[r,n]},void 0,!0,void 0,this);return M("box",{flexDirection:"row",overflow:"hidden",gap:ck,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 Vr(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 Si(e){let{palette:t,facts:r,width:n}=e,o=Math.max(4,n-xi-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:xi,flexShrink:0,overflow:"hidden",children:M("text",{fg:t.muted,wrapMode:"none",children:oe(i.label,xi)},void 0,!1,void 0,this)},void 0,!1,void 0,this),M("box",{width:o,flexShrink:0,overflow:"hidden",children:M("text",{fg:FM(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 DM(e){let{palette:t,rows:r,width:n}=e;if(r.length===0)return M($l,{palette:t,text:"no services"},void 0,!1,void 0,this);let o=nS(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(OM(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 IM(e){let{palette:t,routes:r,width:n}=e;if(r.length===0)return M($l,{palette:t,text:"no routes"},void 0,!1,void 0,this);let o=Math.min(xi+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($M(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 MM(e){let{palette:t,rows:r,width:n}=e;if(r.length===0)return M($l,{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 ak(e){let{palette:t,rows:r,empty:n,width:o,selected:i}=e;if(r.length===0)return M($l,{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 $l(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 OM(e){let t=e.extends==="\u2014"?"":` ${e.extends}`;return` ${e.ports} ${e.identity} ${e.health} ${e.restart} \u2190 ${e.depends}${t}`}function $M(e){return` ${e.auth} ${e.identity} ${e.match} \u2192 ${e.upstream}`}function FM(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 ck=1,lk=1;var uk=I(()=>{Jt();wm();Mt();Ot();Pe();tt()});import{jsxDEV as Ft}from"@opentui/react/jsx-dev-runtime";function pk(e){let{palette:t,credentials:r}=e;if(!r)return Ft(yl,{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(vl,{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 mk=I(()=>{Jt();Pe()});import{jsxDEV as Fl}from"@opentui/react/jsx-dev-runtime";function ki(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 Fl("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:Fl("text",{wrapMode:"none",children:[Fl("span",{fg:e.palette.primary,children:"g"},void 0,!1,void 0,this),Fl("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 fk=()=>{};import{jsxDEV as Ks}from"@opentui/react/jsx-dev-runtime";function Ci(e){let{palette:t,logs:r,names:n,service:o,errorOnly:i,width:s,onService:a,onToggleErrors:c,facets:l}=e,d=l?tx(n,l):ex(n,r),u=s<80,m=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)),f=i?"ERROR+":u?"lvls":"all levels",h=Math.max(ui(m[p]?.label??"all"),s-ui(f));return Ks(Ut,{palette:t,backgroundColor:t.element,children:Ks("box",{height:1,flexDirection:"row",overflow:"hidden",backgroundColor:t.element,children:[Ks("box",{flexGrow:1,overflow:"hidden",children:Ks(gl,{palette:t,items:m,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),Ks(Be,{palette:t,label:f,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 hk=I(()=>{vr();On();Pe();tt()});import{jsxDEV as NM}from"@opentui/react/jsx-dev-runtime";function qs(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 NM(pt,{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 gk=I(()=>{Pe()});import{useEffect as Lm,useRef as vk}from"react";import{jsxDEV as Ye}from"@opentui/react/jsx-dev-runtime";function Pi(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:m,onLeaveLatest:p,viewStart:f=0,search:h="",regex:v=!1}=e,b=vk(null),g=vk(""),C=n===void 0?r:r.slice(-n),E=[...new Set(r.map((k)=>k.service))],S=zb(o,E),y=Vb({width:o,serviceWidth:S,showTimestamps:d,showMeta:u}),x=C[C.length-1],w=x===void 0?"empty":String(x.seq);return Lm(()=>{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]),Lm(()=>{let k=`${f}:${c}:${a}`;if(g.current===k)return;if(g.current=k,l&&(c<0||c>=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,C.length,f,a]),Lm(()=>{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 G=F.scrollHeight;if(F.scrollTop+Z>=G-jM){k=!0,D=G;return}if(G>D){F.stickyScroll=!0,F.stickyStart="bottom",F.scrollTo({x:F.scrollLeft,y:Math.max(0,G)}),D=G;return}if(k)p()},HM);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:C.map((k,D)=>Ye(BM,{id:`log-row-${D}`,palette:t,event:k,serviceWidth:S,msgWidth:y,expanded:Kb(a,D===c),active:D===c,showTimestamps:d,showMeta:u,onPick:m?()=>m(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 BM(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:m=!1}=e,p=Gb(rl(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((f,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:$s,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?el(String(n.level)):""},void 0,!1,void 0,this)},void 0,!1,void 0,this),l?Ye("box",{width:Xc,flexShrink:0,overflow:"hidden",children:Ye("text",{fg:r.muted,children:h===0&&n.source?oe(n.source,Xc):""},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,Ye("box",{width:$s,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(UM,{palette:r,level:n.level,text:f,mark:h===0?p.mark:"",search:u,regex:m},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 UM(e){let{palette:t,level:r,text:n,mark:o,search:i="",regex:s=!1}=e;return Ye("text",{wrapMode:"none",children:[qb(n,i,s).map((a,c)=>Ye("span",{fg:xb(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 HM=200,jM=2;var yk=I(()=>{Ot();vr();tt()});var Dm=I(()=>{fk();hk();gk();yk()});import{useRef as WM}from"react";import{jsxDEV as Ie}from"@opentui/react/jsx-dev-runtime";function Nl(e){let{palette:t,names:r,snap:n,selected:o,checked:i,width:s,onOpen:a,onSelectIndex:c,onToggle:l}=e,d=be(),u=WM(!1),m=new Set(i),p=Dp(s),f=Lp(s),h=Ap(s),v=Mb(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:Ir,fg:t.muted,text:"state"},void 0,!1,void 0,this),h?Ie(Gt,{width:Wr,fg:t.muted,text:"health"},void 0,!1,void 0,this):null,f?Ie(Gt,{width:Bc,fg:t.muted,text:"port"},void 0,!1,void 0,this):null,p?Ie(Gt,{width:ln,fg:t.muted,text:"pid"},void 0,!1,void 0,this):null]},void 0,!0,void 0,this),r.map((b,g)=>{let C=n?.services[b],E=rr(C),S=C?.health??"UNKNOWN",y=m.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:GM(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,E),children:gr(E)},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:Ir,fg:et(t,E),text:E},void 0,!1,void 0,this),h?Ie(Gt,{width:Wr,fg:et(t,S),text:S},void 0,!1,void 0,this):null,f?Ie(Gt,{width:Bc,fg:t.text,text:yo(C)},void 0,!1,void 0,this):null,p?Ie(Gt,{width:ln,fg:t.text,text:C?.pid?String(C.pid):""},void 0,!1,void 0,this):null]},b,!0,void 0,this)})]},void 0,!0,void 0,this)}function Hl(e){let{palette:t,checked:r}=e,n=r.length;if(n>0)return Ie(pt,{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 GM(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 Im=I(()=>{Mt();Ot();Lt();Pe();tt()});import{jsxDEV as me}from"@opentui/react/jsx-dev-runtime";function xk(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:m,errorOnly:p,showSystemLogs:f,onOpen:h,onSelectIndex:v,onToggle:b,onFilterService:g,onToggleErrors:C,onShowErrors:E,wrapMode:S="clip",view:y,follow:x=!0,onLeaveLatest:w,onPickLog:k,viewStart:D=0,viewTotal:O,newer:F=0,onJumpLatest:Z,facets:G,leftover:R,search:j="",regex:B=!1}=e,z=be();if(!r||i.length===0)return me(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<pi,Ee=Kc(n),Ne=ml(n,i),ne=i.filter((ge)=>n?.services[ge]?.state==="FAILED").length,H=Zb(o),ae=Ee?Fb(R,n?.session_id):void 0,Q=Gc(l,i,de),nt=Math.max(24,de?l-4:l-Q-4),ft=f?o:o.filter((ge)=>!di(ge.source)),ie=xo(o,{service:u,errorOnly:p,systemLogs:f,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:E}]:[]];return me("box",{flexGrow:1,flexDirection:de?"column":"row",overflow:"hidden",children:[me("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?me(pt,{palette:t,items:Ct},void 0,!1,void 0,this):null,me(Hl,{palette:t,checked:c},void 0,!1,void 0,this),me("box",{flexGrow:1,paddingLeft:z.pad,paddingRight:z.pad,overflow:"hidden",children:me(Nl,{palette:t,names:i,snap:n,selected:s,checked:c,width:Wc(Q,z.pad),onOpen:h,onSelectIndex:v,onToggle:b},void 0,!1,void 0,this)},void 0,!1,void 0,this),me(zM,{palette:t,names:i,snap:n,width:Q,onOpen:h},void 0,!1,void 0,this),ae?me(JM,{palette:t,leftover:ae,width:Q},void 0,!1,void 0,this):null]},void 0,!0,void 0,this),me("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:[me(Ci,{palette:t,logs:ft,names:m??i,service:u,errorOnly:p,width:nt,onService:g,onToggleErrors:C,facets:G},void 0,!1,void 0,this),Me>Le.length?me(qs,{palette:t,start:D,count:Le.length,total:Me},void 0,!1,void 0,this):null,ie.length===0?me(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):me(Pi,{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?me(ki,{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 qM(e){if(!e)return!1;return e.state===ut||e.health===Oe||e.state===fr||e.restarts>0||e.last_error!==""}function bk(e){if(e.state===ut)return 0;if(e.health===Oe)return 1;if(e.state===fr)return 2;return 3}function VM(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 zM(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)=>qM(d.rt)).sort((d,u)=>bk(d.rt)-bk(u.rt));if(s.length===0)return null;let a=s.slice(0,KM),c=s.length-a.length,l=Math.max(8,o-Mm-4);return me("box",{flexDirection:"column",flexShrink:0,paddingLeft:1,paddingRight:1,overflow:"hidden",children:[me("box",{height:1,overflow:"hidden",children:me("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 m=rr(u);return me("box",{height:1,flexDirection:"row",overflow:"hidden",onMouseDown:()=>i(d),children:[me("box",{width:2,flexShrink:0,children:me("text",{fg:et(t,m),children:gr(m)},void 0,!1,void 0,this)},void 0,!1,void 0,this),me("box",{width:Mm,flexShrink:0,overflow:"hidden",children:me("text",{fg:Ze(d,t),wrapMode:"none",children:oe(d,Mm)},void 0,!1,void 0,this)},void 0,!1,void 0,this),me("box",{width:l,flexShrink:0,overflow:"hidden",children:me("text",{fg:t.muted,wrapMode:"none",children:_e(VM(u),l)},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},d,!0,void 0,this)}),c>0?me("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 JM(e){let{palette:t,leftover:r,width:n}=e,o=r.processes.slice(0,YM),i=r.processes.length-o.length,s=Math.max(8,n-Om-4);return me("box",{flexDirection:"column",flexShrink:0,paddingLeft:1,paddingRight:1,overflow:"hidden",children:[me("box",{height:1,overflow:"hidden",children:me("text",{fg:t.warning,wrapMode:"none",children:"last session"},void 0,!1,void 0,this)},void 0,!1,void 0,this),me("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)=>me("box",{height:1,flexDirection:"row",overflow:"hidden",children:[me("box",{width:Om,flexShrink:0,overflow:"hidden",children:me("text",{fg:t.text,wrapMode:"none",children:oe(a.name,Om)},void 0,!1,void 0,this)},void 0,!1,void 0,this),me("box",{width:s,flexShrink:0,overflow:"hidden",children:me("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?me("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 KM=4,Mm=14,YM=4,Om=14;var wk=I(()=>{At();Jt();Mt();yr();Ot();vr();Lt();hl();Pe();tt();Dm();Im()});import XM from"cli-spinners";import{useEffect as QM,useState as ZM}from"react";import{jsxDEV as re}from"@opentui/react/jsx-dev-runtime";function Sk(e){let{palette:t,report:r,loading:n,progress:o,error:i,selected:s,onPick:a,onReload:c}=e,l=gi(s,Fm);if(n)return re(eO,{palette:t,progress:o},void 0,!1,void 0,this);if(i)return re(sw,{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,m=r.checks.filter((v)=>v.severity==="error").length,p=r.checks.length,f=li(p>0?d/p:0),h=m>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:`[${f}]`},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,m>0?re("span",{fg:t.error,children:` \xB7 ${m} error`},void 0,!1,void 0,this):null,u===0&&m===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(tO,{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 eO({palette:e,progress:t}){let[r,n]=ZM(0);return QM(()=>{let o=setInterval(()=>n((i)=>(i+1)%$m.frames.length),$m.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:`${$m.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:`${gr(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 tO(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:`${Fm}-${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:gr(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:`${Fm}-${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:`${gr(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 Fm="doctor-row",$m;var kk=I(()=>{Jt();Ot();Pe();tt();$m=XM.bouncingBall});import{jsxDEV as We,Fragment as rO}from"@opentui/react/jsx-dev-runtime";function Pk(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:m,onService:p,onToggleErrors:f,onSelect:h,wrapMode:v="focus",selected:b=-1,follow:g=!0,newer:C=0,viewStart:E=0,viewTotal:S,onLeaveLatest:y,onJumpLatest:x,view:w,fullscreen:k=!1,facets:D,split:O=!1,splitFocus:F=0,serviceB:Z="",viewB:G,selectedB:R=-1,followB:j=!0,newerB:B=0,viewStartB:z=0,viewTotalB:de,onServiceB:Ee,onSelectB:Ne,onLeaveLatestB:ne,onFocusPane:H}=e,ae=be(),Q=Mp(u,ae.pad,k),nt=a?r:r.filter((ge)=>!di(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,E+ie.length),Ke=Le===0?"empty":`${E+1}\u2013${Me} of ${Le}`,gt=e.services??[],Xe=gt.length>0?gt.join(","):i===""?"all services":i,Ct=[...g?[]:[{text:C>0?`${O?"left ":""}pinned \xB7 +${C} 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=G??[],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:m,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(pt,{palette:t,items:Ct},void 0,!1,void 0,this),We("box",{flexGrow:1,flexDirection:"row",overflow:"hidden",children:[We(Ck,{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:C,viewStart:E,wrapMode:v,showTimestamps:e.showTimestamps!==!1,showMeta:e.showMeta!==!1,followTick:d,facets:D,focused:F===0,onService:p,onToggleErrors:f,onSelect:h,onLeaveLatest:y,onFocus:()=>H?.(0)},void 0,!1,void 0,this),We(Ck,{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:R,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:f,onSelect:Ne,onLeaveLatest:ne,onFocus:()=>H?.(1)},void 0,!1,void 0,this)]},void 0,!0,void 0,this),!g||!j?We(ki,{palette:t,width:u,newer:Math.max(C,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(rO,{children:[Ct.length>0?We(pt,{palette:t,items:Ct},void 0,!1,void 0,this):null,We(Ci,{palette:t,logs:nt,names:o??n,service:i,errorOnly:s,width:u,onService:p,onToggleErrors:f,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:m,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(qs,{palette:t,start:E,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(Pi,{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:E,search:c,regex:e.regex===!0},d,!1,void 0,this)},void 0,!1,void 0,this),!g?We(ki,{palette:t,width:u,newer:C,onJump:x},void 0,!1,void 0,this):null]},void 0,!0,void 0,this)}function Ck(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)=>!di(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(Ci,{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(Pi,{palette:t,logs:o,width:Mp(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 Rk=I(()=>{Dm();Jt();Mt();vr();Pe();Lr()});import{jsxDEV as Ge}from"@opentui/react/jsx-dev-runtime";function Ek(e){let{palette:t,cfg:r,snap:n,profile:o,selected:i,onPick:s}=e,a=be(),c=gi(i,_k),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 m=d===o,p=u===i,f=r?.profiles[d]?.services??[],h=Object.keys(r?.profiles[d]?.environment??{}).length,v=p?t.borderActive:m?t.success:t.border,b=p?t.primary:m?t.success:t.muted;return Ge("box",{id:`${_k}-${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:[m?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 ${f.length} service${f.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:f.length===0?Ge("text",{fg:t.muted,wrapMode:"none",children:"(no services)"},void 0,!1,void 0,this):Ge("text",{wrapMode:"word",children:f.map((g,C)=>{let E=rr(n?.services[g]);return Ge("span",{children:[Ge("span",{fg:et(t,E),children:gr(E)},void 0,!1,void 0,this),Ge("span",{fg:Ze(g,t),children:` ${g}`},void 0,!1,void 0,this),C<f.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 _k="profile-row";var Tk=I(()=>{Jt();Mt();Lt();Pe();tt()});import{jsxDEV as V,Fragment as Ik}from"@opentui/react/jsx-dev-runtime";function nO(e){let t=e.toLowerCase();if(t==="iap")return"info";if(t==="service"||t==="service_account")return"primary";return"idle"}function oO(e,t,r){if(r||t>=500)return e.error;if(t===0||t>=400)return e.warning;return e.success}function iO(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 lO(e){let{palette:t,req:r,cols:n}=e,o=oO(t,r.status,r.error),i=r.status>0?String(r.status):"ERR",s=iO(t,r.identity),a=r.route||Mk,c=r.error?`${r.path} \u2014 ${_e(r.error,cO)}`:r.path;return V("box",{height:1,flexDirection:"row",overflow:"hidden",flexShrink:0,children:[V("box",{width:Nm,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:jl,flexShrink:0,overflow:"hidden",children:V("text",{fg:t.text,wrapMode:"none",children:oe(r.method,jl)},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,V("box",{width:Bl,flexShrink:0,overflow:"hidden",children:V("text",{fg:o,wrapMode:"none",children:oe(i,Bl)},void 0,!1,void 0,this)},void 0,!1,void 0,this),n.showDur?V("box",{width:Ul,flexShrink:0,overflow:"hidden",children:V("text",{fg:t.muted,wrapMode:"none",children:oe(`${r.durationMs}ms`,Ul)},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,V("box",{width:Wl,flexShrink:0,overflow:"hidden",children:V("text",{fg:s.color,wrapMode:"none",children:oe(s.label,Wl)},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 dO(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:Nm,flexShrink:0,overflow:"hidden",children:V("text",{fg:t.muted,children:oe("TIME",Nm)},void 0,!1,void 0,this)},void 0,!1,void 0,this),r.showMethod?V("box",{width:jl,flexShrink:0,overflow:"hidden",children:V("text",{fg:t.muted,children:oe("METHOD",jl)},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,V("box",{width:Bl,flexShrink:0,overflow:"hidden",children:V("text",{fg:t.muted,children:oe("ST",Bl)},void 0,!1,void 0,this)},void 0,!1,void 0,this),r.showDur?V("box",{width:Ul,flexShrink:0,overflow:"hidden",children:V("text",{fg:t.muted,children:oe("DUR",Ul)},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,V("box",{width:Wl,flexShrink:0,overflow:"hidden",children:V("text",{fg:t.muted,children:oe("WHO",Wl)},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 uO(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:nO(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 Ok(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,m=o<pi,p=Math.max(Ak,Math.min(pO,Math.floor(o*0.36))),f=m?o-Lk:o-p-Lk,h=Math.max(20,f-2),v=h>=Dk,b=h>=mO,g=h>=Dk?fO:hO,C=l.reduce((S,y)=>Math.min(g,Math.max(S,(y.route||Mk).length+aO)),sO),E={showMethod:v,showDur:b,routeCol:C};return V(kt,{palette:t,title:"proxy",children:[V(pt,{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:m?"column":"row",overflow:"hidden",children:[V("box",{flexGrow:m?1:0,flexBasis:m?0:void 0,flexShrink:0,minWidth:m?void 0:Ak,width:m?void 0:p,minHeight:m?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(Ik,{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(uO,{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:za(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:m?void 0:30,minHeight:m?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(Ik,{children:[V(dO,{palette:t,cols:E},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(lO,{palette:t,req:S,cols:E},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 Nm=9,jl=8,Bl=4,Ul=8,Wl=4,sO=8,aO=2,cO=64,Mk="(none)",Ak=26,pO=56,Lk=2,Dk=46,mO=54,fO=20,hO=12;var $k=I(()=>{Jt();yr();Ot();Pe()});import{jsxDEV as pe}from"@opentui/react/jsx-dev-runtime";function Hk(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(Hm,{...e,compact:!1},void 0,!1,void 0,this)},void 0,!1,void 0,this)}function Hm(e){let{palette:t,cfg:r,snap:n,name:o,reveal:i,width:s,compact:a=!0,envScrollRef:c}=e,l=be(),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],m=rr(u),p=u?.health??"UNKNOWN",f=yo(u)||Ms(d,u),h=$b(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>=vO,C=[{label:"health",value:zc(d)},{label:"workdir",value:d.working_dir||"."},{label:"restart",value:Jc(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"}]:[]],E=[{label:"identity",value:Yc(d,u)},{label:"depends",value:d.dependencies.map(Ia).join(", ")||"\u2014"},{label:"ports",value:Ms(d,u)},{label:"tries",value:String(u?.restarts??0),tone:u?.restarts?"warning":"muted"}],S=Math.max(Ro+8,s-2),y=g?Math.floor(S/2):S;return pe("box",{padding:l.pad,flexGrow:1,flexDirection:"column",overflow:"hidden",children:[pe(pt,{palette:t,items:[{text:m,tone:Nk(m)},{text:p,tone:Nk(p)},{text:u?.pid?`pid ${u.pid}`:"pid \u2014",tone:u?.pid?"info":"idle"},{text:f==="\u2014"?"port \u2014":`port ${f}`,tone:f==="\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,gO)},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,pe(jm,{palette:t,item:{label:"command",value:Vc(d)},valueWidth:Math.max(8,S-Ro)},void 0,!1,void 0,this),pe(yO,{palette:t,left:C,right:E,wide:g,colWidth:y},void 0,!1,void 0,this),pe(bO,{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 yO(e){let{palette:t,left:r,right:n,wide:o,colWidth:i}=e,s=Math.max(4,i-Ro);if(!o)return pe("box",{width:i,flexShrink:0,flexDirection:"column",overflow:"hidden",children:[...r,...n].map((a)=>pe(jm,{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(Fk,{palette:t,items:r,width:i},void 0,!1,void 0,this),pe(Fk,{palette:t,items:n,width:i},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function Fk(e){let t=Math.max(4,e.width-Ro);return pe("box",{width:e.width,flexShrink:0,flexDirection:"column",overflow:"hidden",children:e.items.map((r)=>pe(jm,{palette:e.palette,item:r,valueWidth:t},r.label,!1,void 0,this))},void 0,!1,void 0,this)}function jm(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:Ro+n,flexDirection:"row",overflow:"hidden",children:[pe("box",{width:Ro,flexShrink:0,overflow:"hidden",children:pe("text",{fg:t.muted,wrapMode:"none",children:oe(r.label,Ro)},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 bO(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(pt,{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 m=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:m,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 Nk(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 gO=72,vO=56,Ro=9;var Bm=I(()=>{dt();Jt();Mt();Ot();Lt();Pe();tt()});import{jsxDEV as Bn}from"@opentui/react/jsx-dev-runtime";function jk(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:m,envStatus:p,envError:f,onSelectEnv:h}=e;if(n.length===0)return Bn(ze,{palette:t,title:"No services",body:"This configuration does not define any services."},void 0,!1,void 0,this);let v=a<pi,b=n[i]??"",g=Gc(a,n,v),C=4,E=v?Math.max(Um,a-C):Math.max(Um,a-g-C);return Bn("box",{flexGrow:1,flexDirection:v?"column":"row",overflow:"hidden",children:[Bn("box",{flexGrow:v?1:0,flexShrink:0,minWidth:v?void 0:jc,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:[Bn(Hl,{palette:t,checked:s},void 0,!1,void 0,this),Bn("box",{flexGrow:1,overflow:"hidden",children:Bn(Nl,{palette:t,names:n,snap:o,selected:i,checked:s,width:Wc(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),Bn("box",{flexGrow:2,minWidth:v?void 0:Um,minHeight:v?xO: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:Bn(Hm,{palette:t,cfg:r,snap:o,name:b,reveal:c,width:E,resolvedEnv:m,envStatus:p,envError:f,onSelectEnv:h},void 0,!1,void 0,this)},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}var Um=36,xO=12;var Bk=I(()=>{Jt();yr();Lt();tt();Bm();Im()});import{jsxDEV as ke}from"@opentui/react/jsx-dev-runtime";function SO(e){switch(e){case"cycle":return"\u2194";case"toggle":return"\u23FB";case"page":return"\u25B8";case"action":return"!";default:return"\xB7"}}function Kk(e){let{palette:t,items:r,selected:n,locked:o,width:i,onPick:s,onActivate:a}=e,c=be(),l=kp(r,n),d=In(r,l);return ke("box",{flexGrow:1,border:!0,borderStyle:"rounded",borderColor:t.borderActive,backgroundColor:t.panel,title:"settings",titleColor:t.primary,flexDirection:"column",overflow:"hidden",children:[ke("box",{height:2,paddingLeft:c.pad,flexShrink:0,flexDirection:"column",overflow:"hidden",children:[ke("text",{fg:t.primary,wrapMode:"none",children:"Preferences"},void 0,!1,void 0,this),ke("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),ke("box",{flexGrow:1,paddingLeft:c.pad,paddingRight:c.pad,overflow:"hidden",flexShrink:1,children:ke(kO,{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),ke(PO,{palette:t,item:d,scale:c},void 0,!1,void 0,this)]},void 0,!0,void 0,this)}function kO(e){let{palette:t,items:r,active:n,scale:o,width:i,onPick:s,onActivate:a}=e,c=gi(n,Gk),l=Math.max(8,i-Uk-Wk-10),d=0,u=Eb(r);return ke("scrollbox",{ref:c,focused:!1,stickyScroll:!1,scrollX:!1,style:Ue(t),children:ke("box",{flexDirection:"column",overflow:"hidden",children:u.map((m,p)=>{let f=d;d+=m.items.length;let h=n>=f&&n<f+m.items.length;return ke("box",{flexDirection:"column",flexShrink:0,marginBottom:p<u.length-1?Math.max(1,o.gap):0,overflow:"hidden",children:[ke("box",{height:1,overflow:"hidden",children:ke("text",{fg:h?t.primary:t.muted,wrapMode:"none",children:m.group},void 0,!1,void 0,this)},void 0,!1,void 0,this),m.group==="MCP"?ke("box",{height:1,overflow:"hidden",children:ke("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,m.items.map((v,b)=>{let g=f+b;return ke(CO,{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)})]},m.group,!0,void 0,this)})},void 0,!1,void 0,this)},void 0,!1,void 0,this)}function CO(e){let{palette:t,index:r,item:n,active:o,rowH:i,valueWidth:s,onPick:a,onActivate:c}=e;return ke("box",{id:`${Gk}-${r}`,height:i,flexDirection:"row",overflow:"hidden",flexShrink:0,backgroundColor:o?t.highlight:void 0,onMouseDown:()=>{if(o){c();return}a()},children:[ke("box",{width:2,flexShrink:0,overflow:"hidden",children:ke("text",{fg:t.primary,children:o?"\u203A":" "},void 0,!1,void 0,this)},void 0,!1,void 0,this),ke("box",{width:Wk,flexShrink:0,overflow:"hidden",children:ke("text",{fg:o?t.primary:t.muted,children:SO(n.kind)},void 0,!1,void 0,this)},void 0,!1,void 0,this),ke("box",{width:Uk,flexShrink:0,overflow:"hidden",children:ke("text",{fg:o?t.primary:t.text,wrapMode:"none",children:n.name},void 0,!1,void 0,this)},void 0,!1,void 0,this),ke("box",{flexGrow:1,overflow:"hidden",children:ke("text",{fg:RO(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 PO(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 ke("box",{height:wO,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:[ke("box",{height:1,overflow:"hidden",children:ke("text",{fg:t.primary,wrapMode:"none",children:o?`${Rb(n.steps)} ${n.label}`:i},void 0,!1,void 0,this)},void 0,!1,void 0,this),ke("box",{height:1,overflow:"hidden",children:ke("text",{fg:t.text,wrapMode:"word",children:r?.detail??""},void 0,!1,void 0,this)},void 0,!1,void 0,this),ke("box",{height:1,overflow:"hidden",children:ke("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 RO(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 Uk=14,Wk=2,wO=5,Gk="settings-row";var qk=I(()=>{Mt();Ot();Pe();Dr()});import{jsxDEV as Ae}from"@opentui/react/jsx-dev-runtime";function Vk(e){let{palette:t,cfg:r,google:n,bootError:o,bootErrorMissing:i=!1,step:s=0}=e,a=be();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=_O(r,n,c),d=l.filter((g)=>g.ok).length,u=l.length,m=u>0?Math.round(d/u*100):0,p=20,f=Math.round(m/100*p),h="\u2588".repeat(f)+"\u2591".repeat(Math.max(0,p-f)),v=m===100?t.success:m===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:` (${m}%)`},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,C)=>{let E=C===s;return Ae("box",{height:a.rowH,flexDirection:"row",overflow:"hidden",backgroundColor:E?t.highlight:void 0,paddingLeft:1,children:[Ae("box",{width:2,flexShrink:0,children:Ae("text",{fg:et(t,g.ok?"OK":"WARN"),children:gr(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:E?t.primary:t.muted,children:String(C+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:E?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:E?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 _O(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=Ml(e,void 0,t).project;return[{name:pn[0],ok:Boolean(e?.repoRoot),detail:e?.repoRoot||"unknown"},{name:pn[1],ok:Boolean(e?.project.name),detail:e?.project.name||"set project.name"},{name:pn[2],ok:Boolean(i),detail:i||"optional for local-only"},{name:pn[3],ok:t?.adcAvailable===!0,detail:t?.adcAvailable?"ADC available":"needed only for cloud identity / IAP"},{name:pn[4],ok:!0,detail:"read from configuration; never hard-coded"},{name:pn[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:pn[6],ok:n.length===0||Boolean(e),detail:`${n.length} fixed ports from configuration`},{name:pn[7],ok:Object.keys(e?.profiles??{}).length>0,detail:`${Object.keys(e?.profiles??{}).length} profile(s)`},{name:pn[8],ok:r.length===0,detail:r.length===0?"configuration is valid":r[0]??"invalid"}]}var pn;var zk=I(()=>{Jt();Mt();Pe();tt();pn=["Repository","Environment","Google project","Authentication","Service accounts","IAP","Ports","Profiles","Validation"]});import{useEffect as EO,useState as TO}from"react";import{jsxDEV as ee}from"@opentui/react/jsx-dev-runtime";function Ri(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 Wm(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 zs(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:Wm(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 Yk(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 Vs(e){let{palette:t,facts:r,width:n}=e,o=Jp(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(zs,{palette:t,header:!0,gap:hi,cells:i},void 0,!1,void 0,this),ee(Yk,{palette:t,width:n},void 0,!1,void 0,this),r.map((s,a)=>{let c=s.meaning===""?[""]:rl(s.meaning,Math.max(1,o.meaning)),l=s.meter?qx(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(zs,{palette:t,gap:hi,stripe:a%2===1,cells:d},void 0,!1,void 0,this),c.slice(1).map((u,m)=>{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(zs,{palette:t,gap:hi,stripe:a%2===1,cells:p},`${s.what}-more-${m}`,!1,void 0,this)})]},`${s.what}-${s.reading}-${a}`,!0,void 0,this)})]},void 0,!0,void 0,this)}function Jk(e){let{palette:t,cfg:r,snap:n,width:o,onRefresh:i}=e,s=be(),[,a]=TO(0);if(EO(()=>{let B=setInterval(()=>{a((z)=>{let de=z+1;if(i&&de%LO===0)i();return de})},AO);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)=>Vx(r.services[B])),d=Hx(c,n),u=ew(d,l),m=l&&d.live>0?d.healthy/d.live:d.total>0?d.live/d.total:0,p=li(m,DO),f=d.failed>0?"error":l&&d.live>0&&d.healthy<d.live?"warning":"success",h=Wx(o,s.pad),v=n?.session_id?td(n.session_id):void 0,b=v?Ts(Date.now()-v.getTime()):"just started",g=n?.logs.total??0,C=n?.logs.errors??0,E=Ux(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?Yx(w.loadAvg1,w.cpuCount):void 0,O=w?Jx(k,w.memTotalKB):void 0,F=[];if(w&&D&&O){F.push(D,O,{what:"Computer",reading:zx(w.platform),meaning:`on for ${Ts(w.hostUptimeSec*IO)}`,tone:"text"});let B=n?.stats_series;if(B&&B.cpu.length>1)F.push({what:"Load trend",reading:Yp(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:Yp(B.mem),meaning:"used memory over the same window",tone:"text"})}let Z=d.failed>0||D?.tone==="error"||O?.tone==="error"?"error":f==="warning"||D?.tone==="warning"||O?.tone==="warning"?"warning":"success",G=[{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"}],R=[{what:"Lines kept",reading:String(g),meaning:"log lines still in memory",tone:"text"},{what:"Error lines",reading:String(C),meaning:C>0?"open Logs and press e to see only errors":"no ERROR or FATAL lines",tone:C>0?"error":"muted"},...E.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?Zx(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(Ri,{palette:t,title:`Your services ${d.live} of ${d.total} started`,tone:Z,children:[ee(Vs,{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",Jp(u,h).what)},void 0,!1,void 0,this),ee("span",{children:" ".repeat(hi)},void 0,!1,void 0,this),ee("span",{fg:Wm(t,{text:"",width:1,tone:f}),children:`[${p}]`},void 0,!1,void 0,this),ee("span",{fg:Wm(t,{text:"",width:1,tone:f}),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(Ri,{palette:t,title:"This computer",tone:D?.tone==="error"||O?.tone==="error"?"error":D?.tone==="warning"||O?.tone==="warning"?"warning":"success",children:ee(Vs,{palette:t,facts:F,width:h},void 0,!1,void 0,this)},void 0,!1,void 0,this):null,ee(Ri,{palette:t,title:"This session",children:ee(Vs,{palette:t,facts:G,width:h},void 0,!1,void 0,this)},void 0,!1,void 0,this),ee(Ri,{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(MO,{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(Ri,{palette:t,title:"Log lines",tone:C>0?"warning":"muted",children:ee(Vs,{palette:t,facts:R,width:h},void 0,!1,void 0,this)},void 0,!1,void 0,this),ee(Ri,{palette:t,title:"Other parts",children:ee(Vs,{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 MO(e){let{palette:t,names:r,snap:n,width:o}=e,i=Gx(o),s=[{text:"service",width:i.name},{text:"status",width:Ir}];if(i.health)s.push({text:"check",width:Wr});if(i.cpu)s.push({text:"cpu",width:Ds});if(i.mem)s.push({text:"ram",width:Is});if(i.up)s.push({text:"up",width:Ls});if(i.rst)s.push({text:"restarts",width:fl});if(i.pid)s.push({text:"pid",width:ln});return ee("box",{flexDirection:"column",overflow:"hidden",flexShrink:0,children:[ee(zs,{palette:t,header:!0,gap:$t,cells:s},void 0,!1,void 0,this),ee(Yk,{palette:t,width:o},void 0,!1,void 0,this),r.map((a,c)=>ee(NO,{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 FO(e){if(e===void 0)return"muted";if(e>$O)return"error";if(e>OO)return"warning";return"text"}function NO(e){let{palette:t,name:r,rt:n,cols:o,stripe:i}=e,s=Xx(n),a=Qx(n),c=n?.restarts??0,l=[{text:r,width:o.name,color:Ze(r,t)},{text:s,width:Ir,color:et(t,n?.state??"STOPPED")}];if(o.health)l.push({text:a,width:Wr,color:et(t,n?.health??"UNKNOWN")});if(o.cpu)l.push({text:n?.cpuPercent!==void 0?Lb(n.cpuPercent):"\u2014",width:Ds,tone:FO(n?.cpuPercent)});if(o.mem)l.push({text:n?.memoryKB!==void 0?As(n.memoryKB):"\u2014",width:Is,tone:"text"});if(o.up)l.push({text:Bx(n),width:Ls,tone:"text"});if(o.rst)l.push({text:String(c),width:fl,tone:c>0?"warning":"muted"});if(o.pid)l.push({text:n?.pid?String(n.pid):"\u2014",width:ln,tone:"muted"});return ee("box",{flexDirection:"column",flexShrink:0,overflow:"hidden",children:[ee(zs,{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+Ir+Wr))},void 0,!1,void 0,this)},void 0,!1,void 0,this):null]},void 0,!0,void 0,this)}var AO=1000,LO=5,DO=16,IO=1000,OO=80,$O=100;var Xk=I(()=>{Jt();Mt();Ot();vr();Lt();hl();Pe();tt()});import{useRenderer as HO,useTerminalDimensions as jO}from"@opentui/react";import{useCallback as Un,useEffect as BO,useMemo as Qk,useRef as zr,useState as Je}from"react";import{jsxDEV as he,Fragment as UO}from"@opentui/react/jsx-dev-runtime";function Zk({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:m,freePort:p,readTextFile:f,writeTextFile:h,createStarterConfig:v,validate:b}=c,[g,C]=Je(e),[E,S]=Je(i),y=HO(),{width:x,height:w}=jO(),[k,D]=Je(g?"dashboard":"setup"),[O,F]=Je("none"),[Z,G]=Je(""),[R,j]=Je("commands"),[B,z]=Je(0),[de,Ee]=Je(0),[Ne,ne]=Je(0),[H,ae]=Je([]),[Q,nt]=Je(),[ft,ie]=Je(E??""),[Le,Me]=Je({}),[Ke,gt]=Je(),[Xe,Ct]=Je(Ob(g?.cfg)),[ge,vt]=Je(""),[at,yt]=Je(!1),[nr,Qe]=Je(),[Qt,qe]=Je(),[ht,xr]=Je(),Kt=zr(null),wr=zr(null),or=zr(null),qt=zr(null),ir=zr(null),sr=zr(null),ar=zr(null),Vt=zr(""),[ce,K]=Je("quit"),A=zr(void 0),ve=zr(0),[J,Dt]=Je(g?.cfg),ct=g?.previousPersisted,[Re,_o]=Je(void 0),_=_S({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:Jr,fontSize:Xr,prefsLocked:Sr,palette:se,rootBackground:Wn,settingRows:Gn,persistPrefs:$r,persistTheme:mn,activateSetting:Ys}=_,_i=hS({controller:g,workspace:c,cfg:J,snap:Q,screen:k,configReloadError:Re,setSnap:nt,setStatus:ie}),{google:Ei,doctor:le,doctorLoading:bt,doctorError:Pt,doctorProgress:Qr,setDoctorTick:Js}=_i,Eo=t.keybinds.copy??en(),kr=Qk(()=>Object.keys(J?.services??{}).sort(),[J]),fn=Un(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]),Gl=wS({controller:g,tui:t,names:kr,screen:k,refresh:fn,setStatus:ie}),{logs:Ym,setLogs:CC,paused:Jm,errorOnly:Kl,setErrorOnly:ql,showSystemLogs:Xm,logSearch:Qm,setLogSearch:Zm,logSearchFocused:PC,setLogSearchFocused:RC,logFollow:ef,logSince:_C,logService:tf,setLogService:Vl,logServiceB:EC,setLogServiceB:TC,splitLogs:zl,splitFocus:Yl,setSplitFocus:AC,logSliceB:rf,logWindowB:nf,logSelectedB:of,logPinnedB:LC,pinLogViewB:DC,logServices:IC,logShowTimestamps:MC,logShowMeta:OC,logSource:$C,logRegex:sf,logWrap:af,logPinned:cf,logSelected:lf,logsFullscreen:Jl,dashboardLogCursor:FC,setDashboardLogCursor:NC,logFacets:df,logSources:uf,filteredLogs:pf,filteredLogsB:HC,logWindow:Xs,logSlice:Qs,pinLogView:Xl,applyLogCursorA:jC,applyLogCursorB:BC,jumpToLatestLogs:mf}=Gl,To=Qk(()=>{if(R==="tasks")return Gp(Object.keys(J?.tasks??{}),Z,"tasks","run this task");if(R==="services")return Gp(Object.keys(J?.services??{}),Z,"services","exec in this service");return Ox(Z,{services:Object.keys(J?.services??{}),tasks:Object.keys(J?.tasks??{})})},[J,Z,R]);BO(()=>{Ee(0)},[Z]);let Ql=sx(k,{doctor:le?.checks.length??0,settings:Gn.length,profiles:Object.keys(J?.profiles??{}).length,services:kr.length,logs:zl&&Yl===1?rf.length:Qs.length,mcp:xw(),config:Object.keys(J?.tasks??{}).length}),ff=k==="logs"?zl&&Yl===1?of:lf:Ne,Fr=Ql<=0?Math.max(0,ff):Math.max(0,Math.min(ff,Ql-1)),UC=k==="detail"?ge:k==="services"?kr[Fr]??"":"",{inspectorEnv:hf,inspectorEnvStatus:gf,inspectorEnvError:vf,resolveEnvironment:WC}=MS({controller:g,cfg:J,envService:UC}),Ao=Un(()=>{if(F("none"),G(""),j("commands"),RC(!1),A.current)clearTimeout(A.current)},[]),Zl=Un((X)=>{ae((xt)=>xt.includes(X)?xt.filter((oP)=>oP!==X):[...xt,X])},[]),yf=CS({tui:t,controller:g,cfg:J,persistPrefs:$r,snap:Q,refresh:fn,setStatus:ie}),{mcpPortDraft:GC,mcpPort:KC,toggleMcpTool:qC,toggleMcp:VC,copyMcpSnippet:zC}=yf;uS({controller:g,cfg:J,paused:Jm,logSince:_C,refresh:fn,setLogs:CC,setCfg:Dt,setConfigReloadError:_o,setStatus:ie});let Zs=Un((X)=>{vt(X),D("detail")},[]),bf=Un((X)=>{let xt=X.value!==""?X.value:X.required?"(required, not set)":"(empty)";xr({title:X.key,body:xt}),F("scroll-text")},[]),ed=vS({controller:g,workspace:c,cfg:J,snap:Q,refresh:fn,setStatus:ie,setOverlay:F}),{plan:ea,planInitiallyRunning:YC,planBusy:JC,lifecycle:xf}=ed,Zr=LS({workspace:c,renderer:y,setOverlay:F,setStatus:ie,setScreen:D,onAttached:o,setController:C,setCfg:Dt,setBootError:S}),{configEditRef:XC,configEditText:QC,configEditError:ZC,setConfigEditError:eP,openConfigBuffer:wf,saveConfigBuffer:tP}=lS({cfg:J,readTextFile:f,setOverlay:F,setStatus:ie,validateConfigText:m,writeTextFile:h,controller:g,setConfirmKind:K,refresh:fn}),Sf=Un(async()=>{let X=y.getSelection()?.getSelectedText()??"";if(X.trim()===""){ie("Nothing selected \u2014 drag to highlight text");return}try{await bl(X);let xt=X.split(`
|
|
2233
|
+
`).length;ie(xt===1?"Copied selection":`Copied ${xt} selected lines`)}catch(xt){ie(L(xt))}},[y]),{runCommand:ta}=iS({setOverlay:F,setQuery:G,setSlashPicker:j,checked:H,setConfirmKind:K,setStatus:ie,persistTheme:mn,setPaletteIndex:z,themeName:ue,setScreen:D,setSelected:ne,screen:k,renderer:y,cfg:J,setScrollText:xr,reveal:at,controller:g,refresh:fn,profile:Xe,setReveal:yt,resolveEnvironment:WC,openDetail:Zs,copySelection:Sf,lastExportPath:Vt,openConfigBuffer:wf,onDown:n??((X)=>r(X)),workspace:c,logView:Gl,diagnostics:_i,lifecycleActions:ed}),kf=Un(()=>{let X=fi(To,de);if(R==="tasks"){if(!X){ie(To.length===0?"no tasks configured":"pick a task");return}j("commands");let xt=Gr("run");if(xt)ta(xt,[X.name]);return}if(R==="services"){if(!X){ie(To.length===0?"no services configured":"pick a service");return}j("commands"),G(`exec ${X.name} -- `);return}if(!X){ie(Z.trim()===""?"pick a command from the list":`unknown command /${Z}`),Ao();return}ta(X,Mx(X,Z))},[Ao,To,Z,ta,de,R]),Cf=Un((X)=>{mn(X),Ao()},[Ao,mn]);jw({tui:t,controller:g,cfg:J,snap:Q,ui:{screen:k,overlay:O,onQuit:r,onDown:n??((X)=>r(X)),closeOverlay:Ao,confirmKind:ce,confirmDetail:Le,portTarget:Ke,profile:Xe,listCursor:Fr,names:kr,listCount:Ql,openDetail:Zs,copySelection:Sf,height:w,paletteIndex:B,applyTheme:Cf,runCommand:ta,saveConfigBuffer:tP,filtered:To,slashIndex:de,submitSlash:kf,advanceWizard:()=>void Zr.advanceWizard(),bootErrorMissing:s,bootError:E,createStarterConfig:v,startWizard:Zr.startWizard,toggleChecked:Zl,checked:H,detailName:ge,refresh:fn,openConfigBuffer:wf,setOverlay:F,setConfirmKind:K,setConfirmDetail:Me,setPortTarget:gt,setLogDetail:Qe,logDetail:nr,setLogSearch:Zm,setProfile:Ct,setStatus:ie,setPaletteIndex:z,setSlashIndex:Ee,setQuery:G,setSlashPicker:j,setScreen:D,setSelected:ne,setChecked:ae,setConfigEditError:eP,freePort:p},logView:Gl,lifecycleActions:ed,mcp:yf,preferences:_,diagnostics:_i,refs:{interruptArmedAt:ve,leaderTimer:A,logDetailsScrollRef:qt,scrollTextScrollRef:sr,routeDetailsScrollRef:ir,planScrollRef:ar,helpScrollRef:wr,configScrollRef:Kt,detailScrollRef:or}});let Pf=fx(ce,Xe,Le),rP=new Set(ea?.waves.flat()??[]),nP=ea===void 0||xf==="stop"?"":Object.entries(Q?.services??{}).find(([X,xt])=>rP.has(X)&&xt.state==="FAILED")?.[0]??"";return he(Pp.Provider,{value:_s(Xr),children:he("box",{flexDirection:"column",width:x,height:w,backgroundColor:Wn,overflow:"hidden",children:[Jl?null:he(UO,{children:[he(nw,{palette:se,cfg:J,snap:Q,google:Ei,profile:Xe,reveal:at,width:x},void 0,!1,void 0,this),he(ow,{palette:se,screen:k,width:x,onSelect:D},void 0,!1,void 0,this),Re?he("box",{height:1,paddingLeft:1,backgroundColor:se.panel,overflow:"hidden",children:he("text",{fg:se.error,wrapMode:"none",children:`\u26A0 configuration reload failed: ${Re}`},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(xk,{palette:se,cfg:J,snap:Q,logs:Ym,names:kr,selected:Fr,checked:H,width:x,followTick:ef,logService:tf,errorOnly:Kl,showSystemLogs:Xm,onOpen:Zs,onSelectIndex:ne,selectedLog:FC,onToggle:Zl,logSources:uf,onFilterService:Vl,onToggleErrors:()=>ql((X)=>!X),onShowErrors:()=>{Vl(""),ql(!0)},wrapMode:af,view:Qs,follow:!cf,onLeaveLatest:Xl,onPickLog:(X)=>{let xt=Qs[X];if(!xt)return;NC(X),Qe(xt),Xl()},viewStart:Xs.start,viewTotal:pf.length,newer:Xs.newer,onJumpLatest:mf,facets:df,leftover:ct,search:Qm,regex:sf},void 0,!1,void 0,this):null,k==="services"?he(jk,{palette:se,cfg:J,names:kr,snap:Q,selected:Fr,checked:H,width:x,reveal:at,onOpen:Zs,onSelectIndex:ne,onToggle:Zl,resolvedEnv:hf,envStatus:gf,envError:vf,onSelectEnv:bf},void 0,!1,void 0,this):null,k==="detail"?he(Hk,{palette:se,cfg:J,snap:Q,name:ge,reveal:at,width:x,envScrollRef:or,resolvedEnv:hf,envStatus:gf,envError:vf,onSelectEnv:bf},void 0,!1,void 0,this):null,k==="logs"?he(Pk,{palette:se,logs:Ym,names:kr,logSources:uf,service:tf,errorOnly:Kl,showSystemLogs:Xm,search:Qm,searchFocused:PC,followTick:ef,width:x,onSearch:Zm,onService:Vl,onToggleErrors:()=>ql((X)=>!X),source:$C,regex:sf,services:IC,showTimestamps:MC,showMeta:OC,view:Qs,wrapMode:af,selected:lf,follow:!cf,newer:Xs.newer,viewStart:Xs.start,viewTotal:pf.length,onSelect:jC,fullscreen:Jl,onLeaveLatest:Xl,onJumpLatest:mf,facets:df,split:zl,splitFocus:Yl,serviceB:EC,viewB:rf,selectedB:of,followB:!LC,newerB:nf.newer,viewStartB:nf.start,viewTotalB:HC.length,onServiceB:TC,onSelectB:BC,onLeaveLatestB:DC,onFocusPane:AC},void 0,!1,void 0,this):null,k==="auth"?he(ik,{palette:se,cfg:J,google:Ei,identity:Q?.identity},void 0,!1,void 0,this):null,k==="credentials"?he(pk,{palette:se,credentials:Q?.credentials},void 0,!1,void 0,this):null,k==="proxy"?he(Ok,{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(ww,{palette:se,snap:Q,port:KC,portDraft:GC,selected:Fr,onPick:ne,onToggle:()=>{VC()},onCopy:zC,onToggleTool:(X)=>{qC(X)}},void 0,!1,void 0,this):null,k==="doctor"?he(Sk,{palette:se,report:le,loading:bt,progress:Qr,error:Pt,selected:Fr,onPick:ne,onReload:()=>{Js((X)=>X+1),ie("Re-running doctor")}},void 0,!1,void 0,this):null,k==="stats"?he(Jk,{palette:se,cfg:J,snap:Q,width:x,onRefresh:fn},void 0,!1,void 0,this):null,k==="config"?he(dk,{palette:se,cfg:J,width:x,selectedTask:Fr,scrollRef:Kt},void 0,!1,void 0,this):null,k==="profiles"?he(Ek,{palette:se,cfg:J,snap:Q,profile:Xe,selected:Fr,onPick:ne},void 0,!1,void 0,this):null,k==="setup"?he(Vk,{issues:J?b(J):["configuration not loaded"],palette:se,cfg:J,google:Ei,bootError:E,bootErrorMissing:s,step:Fr},void 0,!1,void 0,this):null,k==="settings"?he(Kk,{palette:se,items:Gn,selected:Fr,locked:Sr,width:x,onPick:ne,onActivate:Ys},void 0,!1,void 0,this):null]},void 0,!0,void 0,this),O==="slash"?he(QS,{palette:se,items:To,query:Z,selected:de,title:R==="tasks"?"tasks":R==="services"?"services":"commands",onQuery:G,onSubmit:kf},void 0,!1,void 0,this):null,O==="themes"?he(nk,{palette:se,themeName:ue,selected:B%St.length,termW:x,termH:w,onIndex:z,onPreview:Jr,onPick:Cf},void 0,!1,void 0,this):null,O==="help"?he(Pw,{palette:se,termW:x,termH:w,copyKey:Eo,scrollRef:wr},void 0,!1,void 0,this):null,O==="leader"?he(BS,{palette:se,termW:x,termH:w},void 0,!1,void 0,this):null,O==="confirm"?he(NS,{palette:se,title:Pf.title,body:Pf.body,kind:ce,termW:x,termH:w},void 0,!1,void 0,this):null,O==="setup-wizard"?he(TS,{palette:se,termW:x,termH:w,step:Zr.step,answers:Zr.answers,repo:Zr.repo,draft:Zr.draft,authStatus:Zr.authStatus,onDraft:Zr.setDraft,onSubmit:()=>void Zr.advanceWizard()},void 0,!1,void 0,this):null,O==="log-details"?he(WS,{palette:se,event:nr,termW:x,termH:w,scrollRef:qt},void 0,!1,void 0,this):null,O==="route-details"?he(zS,{palette:se,route:Qt,termW:x,termH:w,scrollRef:ir},void 0,!1,void 0,this):null,O==="scroll-text"&&ht?he(JS,{palette:se,title:ht.title,body:ht.body,termW:x,termH:w,scrollRef:sr},void 0,!1,void 0,this):null,O==="config-edit"&&J?he($S,{palette:se,path:J.configPath,initialValue:QC,error:ZC,termW:x,termH:w,textareaRef:XC},void 0,!1,void 0,this):null,O==="plan"&&ea?he(qS,{palette:se,plan:ea,snap:Q,busy:JC,failed:nP,kind:xf,termW:x,termH:w,scrollRef:ar,initiallyRunning:YC,onDismiss:Ao},void 0,!1,void 0,this):null,Jl?null:he(iw,{palette:se,screen:k,overlay:O,status:ft,paused:Jm,errorOnly:Kl,width:x,copyKey:Eo},void 0,!1,void 0,this)]},void 0,!0,void 0,this)},void 0,!1,void 0,this)}var eC=I(()=>{te();Jt();Qp();Mt();yr();ko();On();Lt();Bw();sS();dS();pS();gS();yS();SS();PS();ES();DS();OS();FS();HS();ym();US();GS();VS();YS();XS();Cm();ZS();ok();sk();uk();mk();wk();kk();Rk();Us();Tk();$k();Bm();Bk();qk();zk();Xk();Dr();tt();Lr()});var tC={};cP(tC,{renderApp:()=>Km,runTui:()=>Gm,runTuiWithController:()=>KO});import{createCliRenderer as WO}from"@opentui/core";import{createRoot as GO}from"@opentui/react";import{jsxDEV as qO}from"@opentui/react/jsx-dev-runtime";async function KO(e,t){let r=e.loadTuiConfig(t.cfg.repoRoot,t.cfg.ui.keymap);await Km(e,t,r)}async function Gm(e,t){let r,n,o=!1;try{r=await e.openTui("",t)}catch(s){n=L(s),o=Pa(s,zn)}let i=e.loadTuiConfig(r?.cfg.repoRoot??process.cwd(),r?.cfg.ui.keymap);await Km(e,r,i,n,o)}async function Km(e,t,r=e.loadTuiConfig(t?.cfg.repoRoot??process.cwd(),t?.cfg.ui.keymap),n,o=!1){ra();let i=Ef(),s=await WO({exitOnCtrlC:!1,useMouse:r.mouse,enableMouseMovement:r.mouse,useKittyKeyboard:{}}),a=GO(s),c=null;try{c=(await s.getPalette({timeout:120,size:16})).defaultBackground}catch{}await new Promise((l)=>{let d=(m)=>{let p=()=>{a.unmount(),i(),s.destroy(),l(),process.exit(0)};if(!t){p();return}t.close({detach:m,shutdownSupervisor:!0}).finally(p)},u=(m)=>{let p=()=>{a.unmount(),i(),s.destroy(),l(),process.exit(0)};if(!t){p();return}t.shutdown({stopServices:!m}).then(()=>t?.close({detach:!0})).finally(p)};a.render(qO(Zk,{workspace:gb(e),controller:t,tui:r,onQuit:d,onDown:u,onAttached:(m)=>{t=m},bootError:n,bootErrorMissing:o,terminalBackground:c},void 0,!1,void 0,this))})}var qm=I(()=>{vb();te();eC()});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 ua,readFileSync as ca}from"fs";import{dirname as HP,isAbsolute as Nf,join as da,resolve as jP}from"path";na();na();import{spawnSync as gP}from"child_process";import{randomBytes as od}from"crypto";import{copyFileSync as vP,existsSync as hn,mkdirSync as yP,readdirSync as bP,readFileSync as id,renameSync as sd,statSync as xP,unlinkSync as rd,writeFileSync as wP}from"fs";import{homedir as Tf}from"os";import{basename as SP,dirname as nd,isAbsolute as kP,join as Rt,resolve as CP}from"path";var PP=448,RP=384;function Ti(e,t){let r=e.startsWith("~/")?Rt(Tf(),e.slice(2)):e;return kP(r)?r:CP(t,r)}function lr(){let e=process.env.DEVCTL_HOME;if(e&&e!=="")return e;return Rt(Tf(),".devctl")}function Nr(e){yP(e,{recursive:!0,mode:PP})}function Lo(e){let t=Rt(lr(),"state",cr(e)),r=Rt(lr(),"sessions",cr(e));if(!hn(t)&&hn(r)){Nr(nd(t));try{sd(r,t)}catch{Nr(t);for(let n of["state.json","devctl.lock","devctl.sock"]){let o=Rt(r,n);if(hn(o))vP(o,Rt(t,n))}}}return Nr(t),t}function oa(){let e=Rt(lr(),"logs");return Nr(e),e}function ia(){let e=Rt(lr(),"exports");return Nr(e),e}function Cr(){let e=Rt(lr(),"credentials");return Nr(e),e}function sa(e,t=process.platform){if(t==="win32")return`\\\\.\\pipe\\devctl-${cr(e)}`;return Rt(Lo(e),"devctl.sock")}function _P(e){return Rt(Lo(e),"devctl.lock")}function Af(e){return Rt(Lo(e),"state.json")}function Ai(e){return Rt(Lo(e),"bootstrap.log")}var EP=5,Lf="bootstrap-";function Df(e){let t=Ai(e);if(!hn(t))return;let r=Lo(e),n=Rt(r,`${Lf}${ad()}.log`);try{sd(t,n)}catch{return}TP(r)}function TP(e){let t;try{t=bP(e).filter((n)=>n.startsWith(Lf)&&n.endsWith(".log"))}catch{return}let r=t.map((n)=>{let o=Rt(e,n);try{return{path:o,mtime:xP(o).mtimeMs}}catch{return}}).filter((n)=>n!==void 0).sort((n,o)=>o.mtime-n.mtime);for(let n=EP;n<r.length;n++)try{rd(r[n].path)}catch{}}function Pr(e,t){Nr(nd(e));let r=Rt(nd(e),`.${SP(e)}.tmp-${process.pid}-${od(4).toString("hex")}`);wP(r,t,{mode:RP}),sd(r,e)}function ad(e=new Date){return`${e.toISOString().slice(0,19).replace(/:/g,"-")+"Z"}-${od(3).toString("hex")}`}function gn(e){return cd(Af(e))}function cd(e){if(!hn(e))return;try{let t=JSON.parse(id(e,"utf8"));if(Array.isArray(t.processes)&&typeof t.repo_root==="string")return t;return}catch{return}}function If(e,t){Pr(Af(e),`${JSON.stringify(t,null,2)}
|
|
2234
|
+
`)}function Mf(e,t){let r=_P(e);if(hn(r)){let n=AP(r);if(n&&Zt(n.pid))throw Error(`supervisor already running (pid ${n.pid})`);try{rd(r)}catch{}}return Pr(r,JSON.stringify({pid:process.pid,socket:t})),{release:()=>{try{rd(r)}catch{}}}}function AP(e){if(!hn(e))return;try{let t=JSON.parse(id(e,"utf8"));if(typeof t.pid==="number"&&typeof t.socket==="string")return{pid:t.pid,socket:t.socket}}catch{return}return}function Zt(e){if(e<=0)return!1;if(process.platform==="win32")return LP(e);try{return process.kill(e,0),!0}catch{return!1}}function ld(e){try{let t=gP("cmd.exe",["/d","/c",`tasklist /FO CSV /NH /FI "PID eq ${e}"`],{encoding:"buffer",windowsHide:!0,timeout:5000});return DP(t.stdout)}catch{return""}}function LP(e){try{return process.kill(e,0),!0}catch{}let t=ld(e).toLowerCase();if(t===""||t.includes("no tasks")||t.includes("no matching"))return!1;return t.includes(String(e))}function DP(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 dd(){return od(24).toString("hex")}function IP(e){return Rt(Lo(e),"mcp-token")}function Of(e){let t=IP(e);if(hn(t)){let n=id(t,"utf8").trim();if(n!=="")return n}let r=dd();return Pr(t,r),r}aa();aa();var BP="OPENCODE_TUI_CONFIG";function pa(){return da(lr(),"tui.json")}function ma(e=""){for(let t of[$f,BP]){let r=process.env[t];if(!r||r==="")continue;let n=Nf(r)?r:jP(e||process.cwd(),r);if(ua(n))return n}return}function UP(e,t){if(!t||Object.keys(t).length===0)return e;return{...e,keybinds:{...e.keybinds,...t}}}function Mo(e,t){let r=UP(Ff(),t),n=ma(e);if(n)return md(r,la(ca(n,"utf8")),n);let o=WP(e),i=o?md(r,la(ca(o,"utf8")),o):r,s=pa();if(!ua(s)||o===s)return i;return md(i,la(ca(s,"utf8")),s)}function Hf(e){let t=pa(),r={};if(ua(t)){let o=la(ca(t,"utf8"));if(Kn(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 Pr(t,`${JSON.stringify(n,null,2)}
|
|
2235
|
+
`),t}function WP(e){let t=ma(e);if(t)return t;let r=[e||process.cwd(),da(e||process.cwd(),".devctl"),lr()],n=["tui.json","tui.jsonc"];for(let o of r)for(let i of n){let s=da(o,i);if(ua(s))return s}return}function md(e,t,r){let n=Kn(t)?t:{},o={...e.keybinds};if(Kn(n.keybinds)){for(let[c,l]of Object.entries(n.keybinds))if(typeof l==="string")o[c]=l}let i={...e.cursor};if(Kn(n.cursor)){if(KP(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(Kn(n.scroll_acceleration)&&typeof n.scroll_acceleration.enabled==="boolean")s.enabled=n.scroll_acceleration.enabled;let a={...e.attention,sounds:{...e.attention.sounds}};if(Kn(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(Kn(n.attention.sounds)){for(let[c,l]of Object.entries(n.attention.sounds))if(typeof l==="string")a.sounds[c]=GP(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 GP(e,t){if(e.startsWith("file://")||Nf(e))return e;return da(HP(t),e)}function KP(e){return e==="block"||e==="underline"||e==="line"||e==="default"}function Kn(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}function la(e){let t=e.replace(/\/\*[\s\S]*?\*\//g,"").replace(/^\s*\/\/.*$/gm,"");return JSON.parse(t)}import{existsSync as KT,readFileSync as qT,writeFileSync as VT}from"fs";import{spawn as Rd}from"child_process";import{createWriteStream as xR,existsSync as Hi,mkdirSync as wR,readdirSync as Td,readFileSync as SR,rmSync as kR,statSync as Ed,writeFileSync as CR}from"fs";import{dirname as ch,join as jo}from"path";Sa();Sa();var PR=50000,Ad="session-";function ah(e){return Buffer.from(JSON.stringify(e),"utf8").toString("base64url")}function RR(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 _d(e,t){let r={...e};return r[t]=void 0,r}class Ld{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:PR,this.bus=t,this.detector=r,this.sessionID=i;let c=o===""||o.startsWith("~/")?oa():o;if(this.persist=n&&i!=="",this.persistDir=this.persist?jo(c,`${Ad}${i}`):"",this.persist)wR(this.persistDir,{recursive:!0,mode:448}),TR(c,s,a)}sessionDir(){return this.persistDir}setParsers(e){this.parsers=e}append(e){let t=xd(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(xd(r.message)):void 0,a={...e,timestamp:e.timestamp||new Date().toISOString(),level:e.level||r.level||Sd(t),request_id:e.request_id||r.request_id||kd(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(ye(Oo,a.service,{event:a,level:a.level})),this.persist){let c=`${a.timestamp} ${a.service} ${a.level} ${a.raw??a.message}
|
|
2236
|
+
`,l=ER(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=xR(jo(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=Pd(e),r=[];return this.forEachEvent((n)=>{if(t(n))r.push(n)}),r}queryPage(e,t={}){let r=ih(t.limit),n=t.cursor?RR(t.cursor):void 0,o=n!==void 0&&n.session!==this.sessionID,i=o?void 0:n,s=i?t.direction??"backward":"backward",a=Pd(e),c=[];this.forEachEvent((f)=>{if(a(f))c.push(f)});let l;if(!i)l=c.slice(Math.max(0,c.length-r));else if(s==="forward")l=c.filter((f)=>f.seq>i.seq).slice(0,r);else{let f=c.filter((h)=>h.seq<i.seq);l=f.slice(Math.max(0,f.length-r))}let d=l[0]?.seq,u=l[l.length-1]?.seq,m=d!==void 0&&c.some((f)=>f.seq<d),p=u!==void 0&&c.some((f)=>f.seq>u);return{events:l,prevCursor:ah({session:this.sessionID,seq:d??i?.seq??0}),nextCursor:ah({session:this.sessionID,seq:u??i?.seq??this.nextSeq-1}),hasNext:p,hasPrev:m,sessionChanged:o}}queryFacets(e){let t=_d(e,"services"),r=_d(e,"level"),n=_d(e,"source"),o=Cd(e),i=0,s={},a={},c={};return this.forEachEvent((l)=>{if(!o(l))return;if(Ho(e,l))i+=1;if(Ho(t,l))s[l.service]=(s[l.service]??0)+1;if(Ho(r,l))a[l.level]=(a[l.level]??0)+1;if(Ho(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===Vn||r.level===Fo)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){Dd(e,this.query(t))}}function lh(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 _R(e=new Date){let t=e.toISOString().replace(/[:.]/g,"-");return jo(ia(),`devctl-logs-${t}.log`)}function dh(e=""){if(e==="")return _R();return Ti(e,process.cwd())}function Dd(e,t){Nr(ch(e));let r=t.map((n)=>`${n.timestamp} ${n.service} ${n.level} ${n.raw??n.message}`);CR(e,`${r.join(`
|
|
2237
2237
|
`)}
|
|
2238
|
-
`,{mode:384})}function
|
|
2239
|
-
`)){if(a.trim()==="")continue;let c=a.split(" "),l=c.slice(3).join(" "),d=
|
|
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
|
|
2241
|
-
`)){if(!r.includes("LISTENING")&&!r.includes("LISTEN"))continue;if(!r.includes(`:${t}`)&&!r.includes(` ${t} `))continue;let n=r.trim().match(/(\d+)\s*$/),o=n?Number(n[1]):0;if(Number.isInteger(o)&&o>0)return{port:t,pid:o,command:"process"}}return}async function D_(e){let t=Ld({cmd:["netstat","-ano"],stdout:"pipe",stderr:"ignore"}),r=t.stdout?await new Response(t.stdout).text():"";return await t.exited,L_(r,e)}var I_=400;async function wa(e){if(e.pid===process.pid)throw Te(He,`port ${e.port} is held by this TUI`,"stop the proxy from the proxy screen, or quit the TUI");try{process.kill(e.pid,"SIGTERM")}catch(t){throw Pe(He,`could not stop pid ${e.pid} on port ${e.port}`,t)}await M_(I_);try{process.kill(e.pid,0),process.kill(e.pid,"SIGKILL")}catch{return}}function M_(e){return new Promise((t)=>{setTimeout(t,e)})}Ma();te();import{readdirSync as KR,readFileSync as qR}from"fs";import{basename as Eg,dirname as VR,extname as zR,join as eo,resolve as YR}from"path";import{parse as JR}from"yaml";ht();function N(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}function Ud(e){return N(e)?new Set(Object.keys(e)):new Set}function G(e){if(typeof e==="string")return e;if(typeof e==="number"||typeof e==="boolean")return String(e);return""}function _e(e){if(typeof e==="number")return e;if(typeof e==="string"){let t=Number(e);return Number.isNaN(t)?0:t}return 0}function ot(e){return e===!0}function Rt(e){if(!Array.isArray(e))return[];return e.map((t)=>G(t))}function Wd(e){if(!Array.isArray(e))return[];return e.map((t)=>N(t)?{service:G(t.service),condition:G(t.condition)||"service_started"}:G(t))}function rn(e){if(!N(e))return{};let t={};for(let[r,n]of Object.entries(e))t[r]=G(n);return t}function Pr(e){if(typeof e==="string")return{args:e.split(/\s+/).filter((t)=>t!==""),shell:!1};if(Array.isArray(e))return{args:e.map((t)=>G(t)),shell:!1};return Bo()}function Gd(e){if(e===void 0||e===null)return[];if(typeof e==="string"||typeof e==="number")return[Bd(e,"http")];if(Array.isArray(e))return e.map((t,r)=>Bd(t,`port_${r}`));if(N(e))return Object.entries(e).map(([t,r])=>Bd(r,t));return[]}function Bd(e,t){if(typeof e==="string"&&e.toLowerCase()==="auto")return{name:t,value:0,auto:!0};return{name:t,value:_e(e),auto:!1}}function _h(e){if(!N(e))return Md();let t={},r=[],n={};for(let[o,i]of Object.entries(e))if(o==="required")r=Rt(i);else if(o==="defaults")n=rn(i);else t[o]=G(i);return{vars:t,required:r,defaults:n}}function G_(e){if(!N(e))return $d();return{type:G(e.type),mode:G(e.mode),service_account:G(e.service_account),config:N(e.config)?{...e.config}:{}}}function K_(e){if(!N(e))return Od();return{type:G(e.type),url:G(e.url),address:G(e.address),command:Pr(e.command),interval_seconds:_e(e.interval_seconds),timeout_seconds:_e(e.timeout_seconds),start_period_seconds:_e(e.start_period_seconds),unhealthy_threshold:e.unhealthy_threshold===void 0?3:_e(e.unhealthy_threshold),healthy_reset_threshold:e.healthy_reset_threshold===void 0?10:_e(e.healthy_reset_threshold)}}function q_(e){if(!N(e))return{policy:"",max_retries:0,backoff_seconds:0};return{enabled:e.enabled===void 0?void 0:ot(e.enabled),policy:G(e.policy),max_retries:_e(e.max_retries),backoff_seconds:_e(e.backoff_seconds)}}function V_(e){if(!N(e))return{wait_for_healthy:!1,timeout_seconds:0};return{wait_for_healthy:ot(e.wait_for_healthy),timeout_seconds:_e(e.timeout_seconds)}}function ji(e){if(!N(e))return yn();let t=N(e.logs)?e.logs:{};return{extends:G(e.extends),description:G(e.description),command:Pr(e.command),shell:ot(e.shell),working_dir:G(e.working_dir),dependencies:Wd(e.dependencies),ports:Gd(e.ports),environment:_h(e.environment),health:K_(e.health),identity:G_(e.identity),logs:{stdout:ot(t.stdout),stderr:ot(t.stderr)},restart:q_(e.restart),startup:V_(e.startup),capabilities:Rt(e.capabilities),proxy:Vd(e.proxy),expose:qd(e.expose),container:Kd(e.container),watch:z_(e.watch),hooks:Y_(e.hooks)}}function z_(e){if(!N(e))return Dd();return{enabled:ot(e.enabled),paths:Rt(e.paths),debounce_ms:Pa(_e(e.debounce_ms)),ignore:e.ignore===void 0?[...$i]:Rt(e.ignore)}}function Y_(e){let t=N(e)?e:{};return{pre_start:Pr(t.pre_start),post_start:Pr(t.post_start)}}function Rh(e){let t=N(e)?e:{};return{command:Pr(t.command),shell:ot(t.shell),working_dir:G(t.working_dir),dependencies:Rt(t.dependencies),environment:_h(t.environment)}}function Kd(e){if(!N(e))return;let t={};if(N(e.ports))for(let[r,n]of Object.entries(e.ports))t[r]=_e(n);return{image:G(e.image),runtime:G(e.runtime),ports:t,env:rn(e.env),volumes:Rt(e.volumes)}}function qd(e){if(e===!0)return{enabled:!0,host:"",port:""};if(e===!1)return{enabled:!1,host:"",port:""};if(N(e))return{enabled:e.enabled!==void 0?ot(e.enabled):!0,host:G(e.host),port:G(e.port)};return Id()}function Vd(e){if(e===void 0||e===null)return[];if(Array.isArray(e))return e.map((t)=>Xn(t));if(N(e))return[Xn(e)];return[]}function Oa(e){if(!N(e))return{services:[],environment:{}};return{services:Rt(e.services),environment:rn(e.environment)}}function J_(e){if(typeof e==="string")return{type:e,service_account:""};if(!N(e))return{type:"",service_account:""};return{type:G(e.type),service_account:G(e.service_account)}}function X_(e){if(!N(e))return Fi();return{type:G(e.type),identity:J_(e.identity),audience:G(e.audience),service_account:G(e.service_account),client_id:G(e.client_id),client_secret:G(e.client_secret),credentials:G(e.credentials),headers:rn(e.headers)}}function Xn(e){if(!N(e))return{name:"",match:{host:"",path:""},upstream:{url:""},auth:Fi()};let t=N(e.match)?e.match:{},r=N(e.upstream)?e.upstream:{};return{name:G(e.name),match:{host:G(t.host),path:G(t.path)},upstream:{url:G(r.url),service:G(r.service),port:G(r.port)},auth:X_(e.auth)}}te();import{existsSync as Q_,statSync as Th}from"fs";import{basename as Eh,dirname as $a,isAbsolute as Z_,join as Fa,resolve as Ah}from"path";function Qn(e,t){if(t!=="")return eR(t);let r=e===""?process.cwd():e;r=Z_(r)?r:Ah(r);for(;;){let n=Fa(r,Nt,lr);if(Bi(n))return{repoRoot:r,configPath:n};let o=Fa(r,"devctl.yaml");if(Bi(o))return{repoRoot:r,configPath:o};let i=$a(r);if(i===r)break;r=i}throw Te(qn,"no devctl configuration found","run `devctl setup` or create a .devctl/config.yaml in the repository root")}function eR(e){let t=Ah(e),r;try{r=Th(t)}catch(o){throw Pe(qn,"config path not found",o)}if(r.isDirectory()){if(Eh(t)===Nt)return{repoRoot:$a(t),configPath:Fa(t,lr)};return{repoRoot:t,configPath:Fa(t,lr)}}let n=$a(t);if(Eh(n)===Nt)n=$a(n);return{repoRoot:n,configPath:t}}function Bi(e){return Q_(e)&&!Th(e).isDirectory()}ht();function ja(){return{services:{},templates:{},provenance:{}}}function nn(e,t,r,n,o=""){if(N(t)&&Object.keys(t).length>0){for(let[i,s]of Object.entries(t))nn(e,s,r,n,o===""?i:`${o}.${i}`);return}if(o==="")return;(e[o]??=[]).push({source:r,layer:n})}var tR=["environment","restart","startup","health","logs","identity","container","watch","hooks"];function Na(e,t,r){let n=Ud(r);if(N(r))for(let i of tR){let s=r[i];if(N(s))for(let a of Object.keys(s))n.add(`${i}.${a}`)}let o=e[t];e[t]=o?new Set([...o,...n]):n}function Lh(e,t,r=ja(),n={source:"unknown",layer:"unknown"}){if(nn(r.provenance,t,n.source,n.layer),t.version!==void 0)e.version=_e(t.version);if(N(t.project)&&t.project.name!==void 0)e.project.name=G(t.project.name);if(N(t.google)){if(t.google.project_id!==void 0)e.google.project_id=G(t.google.project_id);if(t.google.region!==void 0)e.google.region=G(t.google.region)}if(N(t.profiles))for(let[o,i]of Object.entries(t.profiles)){let s=e.profiles[o];e.profiles[o]=s?rR(s,i):Oa(i)}if(N(t.templates))for(let[o,i]of Object.entries(t.templates)){let s=e.templates[o];e.templates[o]=s?Ha(s,i):ji(i),Na(r.templates,o,i)}if(N(t.services))for(let[o,i]of Object.entries(t.services)){let s=e.services[o];e.services[o]=s?Ha(s,i):ji(i),Na(r.services,o,i)}if(N(t.tasks))for(let[o,i]of Object.entries(t.tasks))e.tasks[o]=e.tasks[o]?nR(e.tasks[o],i):Rh(i);if(N(t.proxy))zd(e.proxy,t.proxy);if(N(t.logs)){if(t.logs.max_memory_events!==void 0)e.logs.max_memory_events=_e(t.logs.max_memory_events);if(N(t.logs.persistence)){let o=t.logs.persistence;if(o.enabled!==void 0)e.logs.persistence.enabled=ot(o.enabled);if(o.directory!==void 0)e.logs.persistence.directory=G(o.directory);if(o.retention_days!==void 0)e.logs.persistence.retention_days=_e(o.retention_days);if(o.max_session_logs!==void 0)e.logs.persistence.max_session_logs=_e(o.max_session_logs)}}if(N(t.auth)&&t.auth.refresh_threshold_seconds!==void 0)e.auth.refresh_threshold_seconds=_e(t.auth.refresh_threshold_seconds);if(N(t.shutdown)){if(t.shutdown.stop_services_on_exit!==void 0)e.shutdown.stop_services_on_exit=ot(t.shutdown.stop_services_on_exit);if(t.shutdown.grace_seconds!==void 0)e.shutdown.grace_seconds=_e(t.shutdown.grace_seconds)}if(N(t.ui)){if(t.ui.theme!==void 0)e.ui.theme=G(t.ui.theme);if(t.ui.keymap!==void 0)e.ui.keymap=rn(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,...rn(t.environment.secrets)}}}function zd(e,t){if(t.enabled!==void 0)e.enabled=ot(t.enabled);if(t.gateway!==void 0)e.gateway=ot(t.gateway);if(t.credentials!==void 0)e.credentials=G(t.credentials);if(N(t.listen)){if(t.listen.host!==void 0)e.listen.host=G(t.listen.host);if(t.listen.port!==void 0)e.listen.port=_e(t.listen.port)}if(N(t.token_endpoint)){let r=t.token_endpoint;if(r.enabled!==void 0)e.token_endpoint.enabled=ot(r.enabled);if(r.host!==void 0)e.token_endpoint.host=G(r.host);if(r.port!==void 0)e.token_endpoint.port=_e(r.port)}if(Array.isArray(t.routes))e.routes=t.routes.map((r)=>Xn(r))}function rR(e,t){if(!N(t))return e;return{services:t.services!==void 0?Rt(t.services):e.services,environment:t.environment!==void 0?{...e.environment,...rn(t.environment)}:e.environment}}function nR(e,t){if(!N(t))return e;return{command:t.command!==void 0?Pr(t.command):e.command,shell:t.shell!==void 0?ot(t.shell):e.shell,working_dir:t.working_dir!==void 0?G(t.working_dir):e.working_dir,dependencies:t.dependencies!==void 0?Rt(t.dependencies):e.dependencies,environment:Dh(e.environment,t.environment)}}function Ha(e,t){if(!N(t))return e;let r=Ud(t),n={...e,environment:Dh(e.environment,t.environment),health:sR(e.health,t.health),identity:aR(e.identity,t.identity),logs:cR(e.logs,t.logs),restart:lR(e.restart,t.restart),startup:dR(e.startup,t.startup),container:uR(e.container,t.container),watch:oR(e.watch,t.watch),hooks:iR(e.hooks,t.hooks)};if(r.has("extends"))n.extends=G(t.extends);if(r.has("description"))n.description=G(t.description);if(r.has("command"))n.command=Pr(t.command);if(r.has("shell"))n.shell=ot(t.shell);if(r.has("working_dir"))n.working_dir=G(t.working_dir);if(r.has("dependencies"))n.dependencies=Wd(t.dependencies);if(r.has("ports"))n.ports=Gd(t.ports);if(r.has("capabilities"))n.capabilities=Rt(t.capabilities);if(r.has("proxy"))n.proxy=Vd(t.proxy);if(r.has("expose"))n.expose=qd(t.expose);return n}function oR(e,t){if(!N(t))return e;return{enabled:t.enabled!==void 0?ot(t.enabled):e.enabled,paths:t.paths!==void 0?Rt(t.paths):e.paths,debounce_ms:t.debounce_ms!==void 0?Pa(_e(t.debounce_ms)):e.debounce_ms,ignore:t.ignore!==void 0?Rt(t.ignore):e.ignore}}function iR(e,t){if(!N(t))return e;return{pre_start:t.pre_start!==void 0?Pr(t.pre_start):e.pre_start,post_start:t.post_start!==void 0?Pr(t.post_start):e.post_start}}function Dh(e,t){if(!N(t))return e;let r={...e.vars},n={...e.defaults},o=e.required;for(let[i,s]of Object.entries(t))if(i==="required")o=Rt(s);else if(i==="defaults")Object.assign(n,rn(s));else r[i]=G(s);return{vars:r,required:o,defaults:n}}function sR(e,t){if(!N(t))return e;return{type:t.type!==void 0?G(t.type):e.type,url:t.url!==void 0?G(t.url):e.url,address:t.address!==void 0?G(t.address):e.address,command:t.command!==void 0?Pr(t.command):e.command,interval_seconds:t.interval_seconds!==void 0?_e(t.interval_seconds):e.interval_seconds,timeout_seconds:t.timeout_seconds!==void 0?_e(t.timeout_seconds):e.timeout_seconds,start_period_seconds:t.start_period_seconds!==void 0?_e(t.start_period_seconds):e.start_period_seconds,unhealthy_threshold:t.unhealthy_threshold!==void 0?_e(t.unhealthy_threshold):e.unhealthy_threshold,healthy_reset_threshold:t.healthy_reset_threshold!==void 0?_e(t.healthy_reset_threshold):e.healthy_reset_threshold}}function aR(e,t){if(!N(t))return e;return{type:t.type!==void 0?G(t.type):e.type,mode:t.mode!==void 0?G(t.mode):e.mode,service_account:t.service_account!==void 0?G(t.service_account):e.service_account,config:t.config!==void 0&&N(t.config)?{...e.config??{},...t.config}:e.config}}function cR(e,t){if(!N(t))return e;return{stdout:t.stdout!==void 0?ot(t.stdout):e.stdout,stderr:t.stderr!==void 0?ot(t.stderr):e.stderr}}function lR(e,t){if(!N(t))return e;return{enabled:t.enabled!==void 0?ot(t.enabled):e.enabled,policy:t.policy!==void 0?G(t.policy):e.policy,max_retries:t.max_retries!==void 0?_e(t.max_retries):e.max_retries,backoff_seconds:t.backoff_seconds!==void 0?_e(t.backoff_seconds):e.backoff_seconds}}function dR(e,t){if(!N(t))return e;return{wait_for_healthy:t.wait_for_healthy!==void 0?ot(t.wait_for_healthy):e.wait_for_healthy,timeout_seconds:t.timeout_seconds!==void 0?_e(t.timeout_seconds):e.timeout_seconds}}function uR(e,t){if(!N(t))return e;let r=Kd(t);if(!r)return e;return{image:t.image!==void 0?r.image:e?.image??"",runtime:t.runtime!==void 0?r.runtime:e?.runtime??"",ports:t.ports!==void 0?{...e?.ports??{},...r.ports}:e?.ports??{},env:t.env!==void 0?{...e?.env??{},...r.env}:e?.env??{},volumes:t.volumes!==void 0?r.volumes:e?.volumes??[]}}function Ih(e,t){let r=Object.keys(e.services).sort();for(let n of r){let o=e.services[n]?.proxy??[];if(o.length===0)continue;o.forEach((i,s)=>{let c={name:o.length===1?n:`${n}-${s+1}`,match:i.match,upstream:i.upstream,auth:i.auth},l=e.proxy.routes.length;if(e.proxy.routes.push(c),t)nn(t,c,`synthesized from services.${n}.proxy`,"synthesized",`proxy.routes.${l}`)})}pR(e,t)}function Mh(e){let t=e.repoRoot||process.cwd(),r=e.proxy.credentials.trim()===""?"":_i(e.proxy.credentials.trim(),t);e.proxy.credentials=r;for(let n of e.proxy.routes){if(n.auth.client_id.trim()==="")continue;let o=(n.auth.credentials??"").trim();n.auth.credentials=o===""?r:_i(o,t)}}function pR(e,t){if(!e.proxy.enabled)return;let r=new Set(e.proxy.routes.map((n)=>n.name));for(let n of Object.keys(e.services).sort()){let o=e.services[n];if(!o)continue;if(!(o.expose.enabled!==!1&&(o.expose.enabled===!0||e.proxy.gateway&&Jn(o.ports,"http")!==void 0))||r.has(n))continue;let a={name:n,match:{host:o.expose.host||`${n}.local`,path:""},upstream:{url:"",service:n,port:o.expose.port||"http"},auth:Fi()},c=e.proxy.routes.length;if(e.proxy.routes.push(a),r.add(n),t){let l=o.expose.enabled===!0?`services.${n}.expose`:"proxy.gateway";nn(t,a,`synthesized from ${l}`,"synthesized",`proxy.routes.${c}`)}}}function Oh(e,t=ja()){if(Object.keys(e.templates).length===0)return;let r={};for(let[n,o]of Object.entries(e.services))r[n]=$h(e,o,t.services[n]??new Set,t,{});e.services=r}function $h(e,t,r,n,o){if(t.extends==="")return t;if(o[t.extends])throw Error(`template cycle involving "${t.extends}"`);let i=e.templates[t.extends]??yn();if(!e.templates[t.extends])throw Error(`service extends unknown template "${t.extends}"`);let s={...o,[t.extends]:!0},a=n.templates[t.extends]??new Set,c=$h(e,i,a,n,s),l=fR(c,t,r);return l.extends=t.extends,l}function fR(e,t,r){let n={...e};if(r.has("extends"))n.extends=t.extends;if(r.has("description"))n.description=t.description;if(r.has("command"))n.command=t.command;if(r.has("shell"))n.shell=t.shell;if(r.has("working_dir"))n.working_dir=t.working_dir;if(r.has("dependencies"))n.dependencies=t.dependencies;if(r.has("ports"))n.ports=t.ports;if(r.has("capabilities"))n.capabilities=t.capabilities;if(r.has("proxy"))n.proxy=t.proxy;if(r.has("container")){let o=t.container,i=e.container;n.container=o&&i?{image:r.has("container.image")?o.image:i.image,runtime:r.has("container.runtime")?o.runtime:i.runtime,ports:r.has("container.ports")?o.ports:i.ports,env:r.has("container.env")?o.env:i.env,volumes:r.has("container.volumes")?o.volumes:i.volumes}:o??i}if(r.has("watch"))n.watch=t.watch;return n.hooks={pre_start:r.has("hooks.pre_start")?t.hooks.pre_start:e.hooks.pre_start,post_start:r.has("hooks.post_start")?t.hooks.post_start:e.hooks.post_start},n.health={type:r.has("health.type")?t.health.type:e.health.type,url:r.has("health.url")?t.health.url:e.health.url,address:r.has("health.address")?t.health.address:e.health.address,command:r.has("health.command")?t.health.command:e.health.command,interval_seconds:r.has("health.interval_seconds")?t.health.interval_seconds:e.health.interval_seconds,timeout_seconds:r.has("health.timeout_seconds")?t.health.timeout_seconds:e.health.timeout_seconds,start_period_seconds:r.has("health.start_period_seconds")?t.health.start_period_seconds:e.health.start_period_seconds,unhealthy_threshold:r.has("health.unhealthy_threshold")?t.health.unhealthy_threshold:e.health.unhealthy_threshold,healthy_reset_threshold:r.has("health.healthy_reset_threshold")?t.health.healthy_reset_threshold:e.health.healthy_reset_threshold},n.identity={type:r.has("identity.type")?t.identity.type:e.identity.type,mode:r.has("identity.mode")?t.identity.mode:e.identity.mode,service_account:r.has("identity.service_account")?t.identity.service_account:e.identity.service_account,config:r.has("identity.config")?t.identity.config:e.identity.config},n.logs={stdout:r.has("logs.stdout")?t.logs.stdout:e.logs.stdout,stderr:r.has("logs.stderr")?t.logs.stderr:e.logs.stderr},n.restart={enabled:r.has("restart.enabled")?t.restart.enabled:e.restart.enabled,policy:r.has("restart.policy")?t.restart.policy:e.restart.policy,max_retries:r.has("restart.max_retries")?t.restart.max_retries:e.restart.max_retries,backoff_seconds:r.has("restart.backoff_seconds")?t.restart.backoff_seconds:e.restart.backoff_seconds},n.startup={wait_for_healthy:r.has("startup.wait_for_healthy")?t.startup.wait_for_healthy:e.startup.wait_for_healthy,timeout_seconds:r.has("startup.timeout_seconds")?t.startup.timeout_seconds:e.startup.timeout_seconds},n.environment={vars:{...e.environment.vars,...t.environment.vars},defaults:{...e.environment.defaults,...t.environment.defaults},required:r.has("environment.required")?t.environment.required:e.environment.required},n}te();ht();function Fh(e){if(e.version===0)throw E(Y,"version is required");if(e.version===Uo)return e;throw E(Y,`unsupported config version ${e.version} (expected ${Uo}); no migration is available`)}var Nh=["version","project","google","profiles","templates","services","tasks","proxy","logs","auth","shutdown","ui","secrets","doctor","plugins","environment"],Hh=["extends","description","command","shell","working_dir","dependencies","ports","environment","health","identity","logs","restart","startup","capabilities","proxy","expose","container","watch","hooks"],jh=["type","url","address","command","interval_seconds","timeout_seconds","start_period_seconds","unhealthy_threshold","healthy_reset_threshold"],Bh=["service","condition"],Uh=["type","mode","service_account","config"],Wh=["enabled","policy","max_retries","backoff_seconds"],Gh=["wait_for_healthy","timeout_seconds"],Kh=["enabled","host","port"],qh=["enabled","gateway","credentials","listen","token_endpoint","routes"],Vh=["host","port"],Yd=["name","match","upstream","auth"],Jd=["host","path"],Xd=["url","service","port"],Qd=["type","identity","audience","service_account","client_id","client_secret","credentials","headers"],zh=["max_memory_events","persistence"],Yh=["enabled","directory","retention_days","max_session_logs"],Jh=["refresh_threshold_seconds"],Xh=["stop_services_on_exit","grace_seconds"],Qh=["theme","keymap"],Zh=["name"],eg=["project_id","region"],tg=["services","environment"],Zd=["required","defaults"],rg=["stdout","stderr"],ng=["extra_markers","extra_patterns"],og=["tools"],ig=["name","command"],sg=["enabled","host","port"],ag=["path"],cg=["sources","secrets"],lg=["image","runtime","ports","env","volumes"],dg=["enabled","paths","debounce_ms","ignore"],ug=["pre_start","post_start"],pg=["command","shell","working_dir","dependencies","environment"],fg=["google","google_api","iap","network","service_identity","local_http"],mg=["|","||","&&",";",">",">>","<","&"];var mR=2;function kn(e,t){if(Array.isArray(e))return e.flatMap((o,i)=>kn(o,hg(t,String(i))));if(!wR(e))return[];let r=gR(t),n=[];for(let o of Object.keys(e)){let i=hg(t,o);if(r.length>0&&!r.includes(o)&&!hR(t))n.push(i);n.push(...kn(e[o],i))}return n}function hR(e){if(e==="services"||e==="profiles"||e==="templates"||e==="tasks")return!0;if(e.endsWith(".environment")||e.endsWith(".defaults")||e.endsWith(".keymap"))return!0;if(e.endsWith(".container.ports")||e.endsWith(".container.env"))return!0;if(e.endsWith(".auth.headers"))return!0;if(e.endsWith(".identity.config")||e.includes(".identity.config."))return!0;return e.includes(".environment.")&&!e.endsWith(".environment")}function gR(e){switch(e){case"":return Nh;case"project":return Zh;case"google":return eg;case"proxy":return qh;case"proxy.listen":return Vh;case"proxy.token_endpoint":return sg;case"logs":return zh;case"logs.persistence":return Yh;case"auth":return Jh;case"shutdown":return Xh;case"ui":return Qh;case"secrets":return ng;case"doctor":return og;case"environment":return cg;default:return vR(e)}}function vR(e){if(e==="services"||e==="profiles"||e==="templates"||e==="tasks")return[];if(e.startsWith("services.")||e.startsWith("templates.")){if(e.includes(".dependencies."))return Bh;return yR(e)}if(e.startsWith("profiles.")){if(e.split(".").length===2)return tg}if(e.startsWith("tasks.")){let t=e.split(".");if(t.length===2)return pg;if(t[2]==="environment")return Zd}if(e.includes("routes"))return xR(e);if(e.startsWith("doctor.tools")&&e.split(".").length>=3)return ig;if(e==="plugins")return[];if(e.startsWith("plugins.")&&e.split(".").length===2)return ag;return[]}function yR(e){let t=e.split(".");if(t.length===2)return Hh;if(t.length>=3)switch(t[2]){case"health":return jh;case"identity":return Uh;case"restart":return Wh;case"startup":return Gh;case"logs":return rg;case"environment":return Zd;case"proxy":return bR(t);case"expose":return Kh;case"container":return lg;case"watch":return dg;case"hooks":return ug;default:return[]}return[]}function bR(e){let t=e.slice(3),r=t[0]!==void 0&&/^\d+$/.test(t[0])?1:0,n=t[r]??"";if(n==="match")return Jd;if(n==="upstream")return Xd;if(n==="auth")return Qd;return Yd}function xR(e){if(e==="proxy.routes")return[];if(e.endsWith(".match"))return Jd;if(e.endsWith(".upstream"))return Xd;if(e.endsWith(".auth"))return Qd;if(e.includes("proxy.routes")&&e.split(".").length===mR+1)return Yd;return[]}function hg(e,t){if(e==="")return t;return`${e}.${t}`}function wR(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}function Wo(e){return`unknown fields: ${e.join(", ")}`}ht();function Cn(e){let t=e.trim().toLowerCase();if(t==="")return!0;if(t==="localhost"||t==="::1")return!0;if(gg(t))return!0;return SR(t)}function gg(e){let t=e.split(".");if(t.length!==4)return!1;let r=t.map((n)=>Number(n));if(r.some((n)=>!Number.isInteger(n)||n<0||n>255))return!1;return r[0]===127}function SR(e){if(!e.startsWith("::ffff:"))return!1;return gg(e.slice(7))}import{existsSync as PR}from"fs";import{isAbsolute as _R,resolve as RR}from"path";import{fileURLToPath as ER}from"url";ht();function vg(e,t){if(t){if(t.http!==void 0)return t.http;let n=Object.values(t)[0];if(n!==void 0)return n}let r=Sh(e.ports);if(r&&!r.auto)return r.value;return}function kR(e,t,r,n=""){let o=e,i="";for(;;){let s=o.indexOf("${");if(s<0)return i+o;i+=o.slice(0,s);let a=o.slice(s).indexOf("}");if(a<0)throw Error(`unclosed environment reference in "${e}"`);let c=o.slice(s+2,s+a);i+=CR(c,t,r,n),o=o.slice(s+a+1)}}function CR(e,t,r,n){let o=e.split(".");if(o[0]==="identity"){if(o.length===2&&o[1]==="user")return n;throw Error(`unsupported reference \${${e}}`)}if(o.length<3||o[0]!=="services")throw Error(`unsupported reference \${${e}}`);let i=o[1]??"",s=t.services[i];if(!s)throw Error(`reference \${${e}}: unknown service`);let a=r[i];if(o[2]==="host"||o[2]==="url"){let c=t.proxy.enabled?t.proxy.routes.find((d)=>d.upstream.service===i):void 0;if(o[2]==="host")return c?c.match.host||"127.0.0.1":"127.0.0.1";if(c)return`http://${c.match.host||"127.0.0.1"}:${t.proxy.listen.port}${c.match.path}`;let l=vg(s,a);if(l===void 0)throw Error(`unresolvable reference \${${e}}`);return`http://127.0.0.1:${l}`}if(o[2]==="port"){let c=vg(s,a);if(c!==void 0)return String(c)}if(o[2]==="ports"){let c=o[3];if(!c)throw Error(`reference \${${e}}: missing port name`);if(a&&a[c]!==void 0)return String(a[c]);let l=Jn(s.ports,c);if(l&&!l.auto)return String(l.value);let d=Number.parseInt(c,10);if(!Number.isNaN(d)&&d>=0&&d<s.ports.length){let u=s.ports[d];if(u&&!u.auto)return String(u.value)}}throw Error(`unresolvable reference \${${e}}`)}function eu(e,t,r,n=""){let o={};for(let[i,s]of Object.entries(e))o[i]=kR(s,t,r,n);return o}function yg(e){let t=[],r=e;for(;;){let n=r.indexOf("${");if(n<0)return t;let o=r.slice(n).indexOf("}");if(o<0)return t;t.push(r.slice(n+2,n+o)),r=r.slice(n+o+1)}}function bg(e,t){let r=e.split(".");if(r.length<2)return!1;if(r[0]==="identity")return r.length===2&&r[1]==="user";if(r[0]!=="services"||r.length<3)return!1;let n=t.services[r[1]??""];if(!n)return!1;if(r[2]==="port"||r[2]==="ports"){if(r.length===3)return n.ports.length>0;if(r.length>=4){let o=r[3]??"";if(!Number.isNaN(Number.parseInt(o,10)))return!0;return Jn(n.ports,o)!==void 0}}return!0}ht();var Wa=65535,Ga=1,Pg=["http","tcp","process","command"];function ru(e){let t=[];for(let[r,n]of Object.entries(e.services)){let o=n.health.type;if(o!==""&&!Pg.includes(o.toLowerCase()))t.push({service:r,type:o})}return t}var TR=0,kg=1,Cg=2;function Zn(e){let t=[];if(e.version===0)t.push("version is required");else if(e.version!==Uo)t.push(`unsupported config version ${e.version} (expected ${Uo}); no migration is available`);if(Object.keys(e.services).length===0)t.push("at least one service must be defined");t.push(...LR(e)),t.push(...AR(e)),t.push(...OR(e)),t.push(...$R(e)),t.push(...FR(e));for(let[r,n]of e.plugins.entries())if(n.path==="")t.push(`plugins.${r}.path is required`);else try{let o=n.path.startsWith("file:")?ER(n.path):_R(n.path)?n.path:RR(e.repoRoot,n.path);if(!PR(o))t.push(`plugins.${r}.path does not exist: ${n.path}`)}catch{t.push(`plugins.${r}.path is invalid: ${n.path}`)}if(e.logs.max_memory_events<0)t.push("logs.max_memory_events must be >= 0");return t}function AR(e){let t=[];for(let[r,n]of Object.entries(e.tasks)){let o=`tasks.${r}`;if(bn(n.command))t.push(`${o}.command is required`);t.push(...Ua(o,n.command,n.shell));for(let i of n.dependencies)if(!e.services[i])t.push(`${o}.dependencies: unknown service "${i}"`);t.push(..._g(`${o}.environment`,n.environment,e))}return t}function LR(e){let t=[],r={};for(let[n,o]of Object.entries(e.services)){let i=`services.${n}`;if(bn(o.command)&&!o.container)t.push(`${i}.command is required`);t.push(...Ua(i,o.command,o.shell)),t.push(...Ua(`${i}.hooks.pre_start`,o.hooks.pre_start,o.shell)),t.push(...Ua(`${i}.hooks.post_start`,o.hooks.post_start,o.shell)),t.push(...UR(i,o.capabilities));for(let c of o.dependencies){let l=zt(c);if(!e.services[l])t.push(`${i}.dependencies: unknown service "${l}"`);if(l===n)t.push(`${i}.dependencies: service cannot depend on itself`);if(!["service_started","service_healthy"].includes(vn(c)))t.push(`${i}.dependencies: condition must be service_started or service_healthy`);if(vn(c)==="service_healthy"&&e.services[l]?.health.type==="")t.push(`${i}.dependencies: service_healthy requires ${l} to define a health check`)}if(o.extends!==""&&!e.templates[o.extends])t.push(`${i}.extends: unknown template "${o.extends}"`);for(let c of o.ports.filter((l)=>!l.auto))if(c.value<Ga||c.value>Wa)t.push(`${i}.ports.${c.name}: invalid port ${c.value}`);else if(r[c.value])t.push(`duplicate port ${c.value} used by ${r[c.value]} and ${n}`);else r[c.value]=n;let s=MR(`${i}.identity`,o.identity,e.plugins.length>0);if(s!=="")t.push(s);if(t.push(...DR(i,o,e.plugins.length>0)),o.health.start_period_seconds<0)t.push(`${i}.health.start_period_seconds must be >= 0`);if(o.health.unhealthy_threshold<1)t.push(`${i}.health.unhealthy_threshold must be >= 1`);if(o.health.healthy_reset_threshold<1)t.push(`${i}.health.healthy_reset_threshold must be >= 1`);let a=Ea(o.restart);if(a!==Sa&&a!==ka&&a!==Ca)t.push(`${i}.restart.policy must be never, on_failure, or always`);if(t.push(..._g(`${i}.environment`,o.environment,e)),o.container){if(o.container.image==="")t.push(`${i}.container.image is required`);if(o.container.runtime!==""&&o.container.runtime!=="docker"&&o.container.runtime!=="podman")t.push(`${i}.container.runtime must be docker or podman`);for(let[c,l]of Object.entries(o.container.ports)){if(!o.ports.some((d)=>d.name===c))t.push(`${i}.container.ports.${c}: no matching service port`);if(l<Ga||l>Wa)t.push(`${i}.container.ports.${c}: invalid container port ${l}`)}}}return t}function DR(e,t,r){let n=[];if(t.health.type==="")return n;let o=t.health.type.toLowerCase();if(!Pg.includes(o)&&!r)n.push(`${e}.health.type must be http, tcp, process, or command`);if(o==="http"&&t.health.url==="")n.push(`${e}.health.url is required for http health checks`);if(o==="tcp"&&t.health.address===""&&t.ports.length===0)n.push(`${e}.health.address is required for tcp health checks without ports`);if(o==="command"&&bn({args:t.health.command.args,shell:!1}))n.push(`${e}.health.command is required for command health checks`);return n}var IR=["","user","service","service_account"];function MR(e,t,r){let n=Hi(t).toLowerCase();if(n===""||n==="user")return"";if(n==="service"||n==="service_account"){if(t.service_account==="")return`${e}.service_account is required for service identity`;if(!t.service_account.includes("@"))return`${e}.service_account must be an email`;return""}if(r)return"";return`${e}.type must be user or service_account`}function nu(e){let t=[];for(let[r,n]of Object.entries(e.services)){let o=Hi(n.identity).toLowerCase();if(!IR.includes(o))t.push({service:r,type:o})}return t}function OR(e){let t={},r=[],n=[],o=(i)=>{let s=t[i]??TR;if(s===Cg)return;if(s===kg){r.push(`dependency cycle: ${[...n,i].join(" \u2192 ")}`);return}t[i]=kg,n.push(i);let a=e.services[i];if(a)for(let c of a.dependencies){let l=zt(c);if(e.services[l])o(l)}n.pop(),t[i]=Cg};for(let i of Object.keys(e.services))o(i);return r}function _g(e,t,r){let n=[],o=(i,s)=>{for(let a of yg(s))if(!bg(a,r))n.push(`${e}.${i}: unresolvable reference \${${a}}`)};for(let[i,s]of Object.entries(t.vars))o(i,s);for(let[i,s]of Object.entries(t.defaults))o(i,s);return n}function $R(e){let t=[];for(let[r,n]of Object.entries(e.profiles))for(let o of n.services)if(!e.services[o])t.push(`profiles.${r} references unknown service "${o}"`);return t}function FR(e){let t=[];if(e.proxy.listen.host!==""){if(!WR(e.proxy.listen.host))t.push("proxy.listen.host must be an IP address or localhost");else if(!Cn(e.proxy.listen.host))t.push("proxy.listen.host must be a loopback address")}if(e.proxy.enabled&&e.proxy.listen.port===0)t.push("proxy.listen.port is required when proxy.enabled is true");if(e.proxy.listen.port!==0&&(e.proxy.listen.port<Ga||e.proxy.listen.port>Wa))t.push("proxy.listen.port is invalid");let r={};return e.proxy.routes.forEach((n,o)=>{let i=`proxy.routes[${o}]`;if(n.name==="")t.push(`${i}.name is required`);else if(r[n.name])t.push(`${i}: duplicate route name ${n.name}`);r[n.name]=!0,t.push(...NR(n,i,e)),t.push(...HR(n,i))}),t.push(...BR(e)),t}function NR(e,t,r){let n=[],o=e.upstream.url!=="",i=e.upstream.service??"";if(i===""){if(!o)n.push(`${t}.upstream requires either url or service`);return n}let s=r.services[i];if(!s)return n.push(`${t}.upstream.service references unknown service ${i}`),n;let a=e.upstream.port||"http";if(Jn(s.ports,a)===void 0)n.push(`${t}.upstream: service ${i} has no port named ${a}`);return n}function HR(e,t){let r=[];if(e.auth.type.toLowerCase()==="iap"){if(e.auth.audience.trim()==="")r.push(`${t}.auth.audience is required when auth.type is iap`);if(e.auth.identity.type.trim()==="")r.push(`${t}.auth.identity.type is required when auth.type is iap`)}r.push(...jR(e.auth,t));let n=e.auth.identity.type.toLowerCase();if(n==="service"||n==="service_account"||en({type:n,mode:"",service_account:""})){if((e.auth.identity.service_account||e.auth.service_account)==="")r.push(`${t}.auth.identity.service_account is required`)}return r}function jR(e,t){let r=(e.client_id??"").trim(),n=(e.client_secret??"").trim(),o=(e.credentials??"").trim();if(r===""&&n===""&&o==="")return[];if(e.type.toLowerCase()!=="iap")return[`${t}.auth.${r!==""?"client_id":n!==""?"client_secret":"credentials"} is only valid when auth.type is iap`];let i=[];if(r===""){if(n!=="")i.push(`${t}.auth.client_id is required when client_secret is set`);if(o!=="")i.push(`${t}.auth.credentials requires auth.client_id`);return i}if(n===""&&o==="")i.push(`${t}.auth.client_secret is required when client_id is set`);else if(n!==""&&tu(n).length>0&&!Sg(n))i.push(`${t}.auth.client_secret must be a literal or a single \${NAME} / \${env.NAME} reference`);let s=e.identity.type.toLowerCase();if(s==="service"||s==="service_account")i.push(`${t}.auth.client_id is only valid with identity.type user`);return i}function BR(e){let t=[],r=e.proxy.token_endpoint;if(!r.enabled)return t;let n=r.host||_a;if(!Cn(n))t.push("proxy.token_endpoint.host must be a loopback address");if(r.port!==0&&(r.port<Ga||r.port>Wa))t.push("proxy.token_endpoint.port is invalid");return t}function Ua(e,t,r){if(t.shell||r)return[];for(let n of t.args)if(mg.includes(n)||n.includes("|")||n.includes(";")||n.includes("&&"))return[`${e}.command contains shell metacharacters; set shell: true to run via a shell`];return[]}function UR(e,t){let r=[];for(let n of t)if(!fg.includes(n))r.push(`${e}.capabilities: unknown capability "${n}"`);return r}function WR(e){if(e==="localhost")return!0;return GR(e)}function GR(e){if(/^(\d{1,3}\.){3}\d{1,3}$/.test(e))return e.split(".").every((r)=>{let n=Number(r);return n>=0&&n<=255});return e.includes(":")}function Ui(e,t,r){try{return Wi(e,t,{candidateText:r}),[]}catch(n){if(n instanceof Ve){let o=`${n.kind}: `;return[n.message.startsWith(o)?n.message.slice(o.length):n.message]}return[n instanceof Error?n.message:String(n)]}}function on(e,t){let{repoRoot:r,configPath:n}=Qn(e,t);return Wi(r,n)}function to(e,t){try{return on(e,t)}catch(r){if(!ba(r,qn))throw r;let n=e===""?process.cwd():YR(e),o=Yn();return o.repoRoot=n,o.configPath=eo(n,Nt,lr),o}}function Wi(e,t,r){let n=Yn(),o=ja();Tg(t,n,o,r?.candidateText,"main"),n.repoRoot=e,n.configPath=t;let i=VR(t);if(Eg(i)===Nt)QR(i,n,o);n=XR(n,e,t,o),n=Fh(n);try{Oh(n,o)}catch(a){throw Pe(Y,"template merge failed",a)}Ih(n,o.provenance),Mh(n),n.provenance=o.provenance;let s=Zn(n);if(s.length>0)throw E(Y,s.join(`
|
|
2242
|
-
`));return n}function
|
|
2243
|
-
`)}async delete(e){let t=
|
|
2244
|
-
`)}function
|
|
2245
|
-
`)}function
|
|
2246
|
-
`)}function HE(e){return{identity:e.identity,audience:e.audience,scopes:e.scopes,accessToken:e.accessToken,tokenType:e.tokenType,expiresAt:e.expiresAt.toISOString()}}function jE(e){return{accessToken:e.accessToken,tokenType:e.tokenType||"Bearer",expiresAt:new Date(e.expiresAt),audience:e.audience,identity:e.identity,scopes:e.scopes}}function _n(){return[WE(),UE(),BE()]}function BE(){return{name:"user",accepts:(e,t)=>!e.startsWith("sa:")&&t==="",fetch:async(e,t,r)=>{if(e.startsWith("sa:"))throw E(Fr,"user provider cannot mint service-account tokens");return zE(e,t,r)}}}function UE(){return{name:"service_account",accepts:(e,t)=>e.startsWith("sa:")&&t==="",fetch:async(e,t,r)=>{if(!e.startsWith("sa:"))throw E(Fr,"service-account provider cannot mint user tokens");return GE(e,t,r)}}}function WE(){return{name:"iap",accepts:(e,t)=>t!=="",fetch:async(e,t,r,n)=>{if(t==="")throw E(Fr,"IAP audience is required");if(e.startsWith("sa:"))return KE(e,t,r);return qE(e,t,r,n)}}}async function GE(e,t,r){let n=e.slice(3);try{qo();let i=await new Vi({scopes:r.length>0?r:[Za]}).getClient(),s=new EE({sourceClient:i,targetPrincipal:n,targetScopes:r.length>0?r:[Za],lifetime:3600}),a=await s.getAccessToken(),c=typeof a==="string"?a:a?.token;if(!c)throw E(Fr,"empty impersonated token");return{accessToken:c,tokenType:"Bearer",expiresAt:zi(c,s),audience:t,identity:e,scopes:r}}catch(o){throw Vo(o)}}async function KE(e,t,r){let n=e.slice(3);try{qo();let i=await new Vi({scopes:[Za]}).getClient(),s=`https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/${encodeURIComponent(n)}:generateIdToken`,c=(await i.request({url:s,method:"POST",data:{audience:t,includeEmail:!0}})).data.token;if(!c)throw E(Fr,"empty impersonated IAP id token");return{accessToken:c,tokenType:"Bearer",expiresAt:zi(c),audience:t,identity:e,scopes:r}}catch(o){throw Vo(o)}}async function qE(e,t,r,n){try{if(qo(),n)return await VE(e,t,r,n);let s=await(await new Vi({scopes:r.length>0?r:[zg]}).getIdTokenClient(t)).idTokenProvider.fetchIdToken(t);return{accessToken:s,tokenType:"Bearer",expiresAt:zi(s),audience:t,identity:e,scopes:r}}catch(o){if(o instanceof Ve)throw o;throw Vo(o)}}async function VE(e,t,r,n){let o=n.refreshToken;if(!o){if(o=(await new Vi({scopes:r.length>0?r:[zg]}).getClient()).credentials.refresh_token??void 0,!o)throw Te(gn,"ADC has no refresh token for a custom IAP OAuth client","set auth.credentials (or proxy.credentials) to an authorized_user file whose refresh_token was issued by this client_id, or run `gcloud auth application-default login` with that client")}let s=await new TE({clientId:n.clientId,clientSecret:n.clientSecret,refreshToken:o}).fetchIdToken(t);if(!s)throw E(Fr,"empty IAP id token");return{accessToken:s,tokenType:"Bearer",expiresAt:zi(s),audience:t,identity:e,scopes:r}}async function zE(e,t,r){try{qo();let o=await new Vi({scopes:r.length>0?r:[Za]}).getClient(),i=await o.getAccessToken();if(!i.token)throw E(Fr,"empty access token");return{accessToken:i.token,tokenType:"Bearer",expiresAt:YE(o,i.token),audience:t,identity:e,scopes:r}}catch(n){throw Vo(n)}}function zi(e,t){if(t?.credentials?.expiry_date)return new Date(t.credentials.expiry_date);return JE(e)??new Date(Date.now()+AE)}function YE(e,t){return zi(t,e)}function JE(e){let t=e.split(".");if(t.length<2||!t[1])return;try{let r=JSON.parse(Buffer.from(t[1],"base64url").toString("utf8"));if(typeof r.exp==="number")return new Date(r.exp*1000)}catch{return}return}function XE(e){if(e instanceof Ve)return e.kind!==Y&&e.kind!==Vn;let t=e instanceof Error?e.message.toLowerCase():String(e).toLowerCase();return t.includes("network")||t.includes("econnreset")||t.includes("etimedout")||t.includes("503")||t.includes("429")}te();jr();var Ji=4000,QE=8000;function Yo(e){let t=e?.tokens,r=()=>(t??=new ao(60000,_n(),void 0,Ja("file")),t);return{detectGoogle:io,hasCommand:uu,portAvailable:jo,hasLocalAdc:Ki,adcQuotaProject:du,mintToken:async(n,o,i)=>{await r().get(n,o,[],i)},probeServiceUsage:async(n,o)=>{let i=await Xi(r().get("user","",[]),Ji),s=`https://serviceusage.googleapis.com/v1/projects/${n}/services/${o}`,a=await fetch(s,{headers:{Authorization:`Bearer ${i.accessToken}`},signal:AbortSignal.timeout(Ji)});if(!a.ok)return!1;return(await a.json().catch(()=>{return}))?.state==="ENABLED"},containerRuntimeAvailable:async(n)=>{try{return await Bun.spawn({cmd:[n,"info"],stdout:"ignore",stderr:"ignore",stdin:"ignore"}).exited===0}catch{return!1}}}}var tc=Yo();function rc(e=Yo()){return{run:(t,r,n)=>ZE(t,e,r,n)}}async function ZE(e,t=tc,r,n){let o={checks:[],issues:0},i="Preparing diagnostics",s=()=>r?.({active:i,checks:[...o.checks]}),a=(p)=>{i=p,s()},c=(p)=>{if(o.checks.push(p),p.severity==="error")o.issues+=1;s()},l=[...new Set(Object.values(e.services).flatMap((p)=>p.container?[p.container.runtime||"docker"]:[]))];for(let p of l){a(`${p} container runtime`);let m=await t.hasCommand(p),h=m&&await(t.containerRuntimeAvailable??tc.containerRuntimeAvailable)(p);c(h?{name:`${p} container runtime`,severity:"ok",message:`${p} daemon reachable`}:{name:`${p} container runtime`,severity:"error",message:m?`${p} daemon is not reachable`:`${p} not found`,hint:`install and start ${p}`})}if(a("Google CLI installed"),await t.hasCommand("gcloud"))c({name:"Google CLI installed",severity:"ok",message:"gcloud found"});else c({name:"Google CLI installed",severity:Gi(e)?"error":"warn",message:"gcloud not installed",hint:"install the Google Cloud CLI from https://cloud.google.com/sdk/docs/install"});a("Google environment");let d=await t.detectGoogle(e.google.project_id);if(d.adcAvailable)c({name:"Google authentication available",severity:"ok",message:"Application Default Credentials found"});else c({name:"Google authentication available",severity:Gi(e)?"error":"warn",message:"ADC unavailable",hint:"run `gcloud auth application-default login`"});if(d.projectID!=="")c({name:"Project configured",severity:"ok",message:`${d.projectID} (source: ${d.projectSource})`});else c({name:"Project configured",severity:Gi(e)?"error":"warn",message:"no Google project configured",hint:"set google.project_id in .devctl/config.yaml"});if(Gi(e)||Object.values(e.services).some((p)=>p.capabilities.includes("google")||p.identity.type!=="")){a("Live Google access");let p=!0,m=(h)=>{if(p)c(h)};try{await Xi((async()=>{await tT(e,m,t,d.userEmail),await eT(e,m,t)})(),t.liveDeadlineMs??QE)}catch(h){m({name:"Live Google probes",severity:"warn",message:L(h),hint:"network or timeout \u2014 retry when online"})}finally{p=!1}}for(let p of e.doctor.tools){let m=p.command||p.name;if(a(`${p.name} installed`),await t.hasCommand(m))c({name:`${p.name} installed`,severity:"ok",message:`${m} found`});else c({name:`${p.name} installed`,severity:"error",message:`${m} not found`,hint:`install ${p.name} and ensure it is on PATH`})}if(a("Repository configuration"),n?.repositoryConfigError!==void 0)if(n.repositoryConfigError==="")c({name:"Repository configuration",severity:"ok",message:"valid"});else c({name:"Repository configuration",severity:"error",message:n.repositoryConfigError});else try{let p=Zn(e);if(p.length>0)c({name:"Repository configuration",severity:"error",message:p.join("; ")});else c({name:"Repository configuration",severity:"ok",message:"valid"})}catch(p){c({name:"Repository configuration",severity:"error",message:L(p)})}let f={};for(let[p,m]of Object.entries(e.services))for(let h of m.ports){if(h.auto)continue;let v=`Port ${h.value}`;if(a(v),f[h.value])c({name:v,severity:"error",message:`configured on both ${f[h.value]} and ${p}`});else{f[h.value]=p;let b=n?.services?.[p],g=Boolean(m.container&&b&&["STARTING","RUNNING","HEALTHY","UNHEALTHY"].includes(b.state??""));if(Boolean(b&&(b.pid>0||g)&&Object.values(b.ports).includes(h.value)))c({name:v,severity:"ok",message:`in use by running service ${p}`});else if(await t.portAvailable(h.value))c({name:v,severity:"ok",message:"available"});else c(await Jg(v,h.value,`services.${p}.ports`))}}if(e.proxy.listen.port>0){let p=`Port ${e.proxy.listen.port}`;if(a(p),n?.proxyRunning)c({name:p,severity:"ok",message:"in use by the running proxy"});else if(await t.portAvailable(e.proxy.listen.port))c({name:p,severity:"ok",message:"proxy listen port available"});else c(await Jg(p,e.proxy.listen.port,"proxy.listen.port"))}return i="Diagnostics complete",s(),o}async function eT(e,t,r){let n=e.google.project_id,o=r.hasLocalAdc??Ki;if(n===""||!o())return;let i=r.probeServiceUsage??tc.probeServiceUsage,s=[{name:"IAM Credentials API",service:"iamcredentials.googleapis.com"},{name:"Resource Manager API",service:"cloudresourcemanager.googleapis.com"},{name:"IAP API",service:"iap.googleapis.com"}];(await Promise.allSettled(s.map((c)=>Xi(i(n,c.service),Ji)))).forEach((c,l)=>{let d=s[l];if(!d)return;if(c.status==="fulfilled"){t({name:d.name,severity:c.value?"ok":"warn",message:c.value?"enabled":"not enabled or unreachable",hint:c.value?void 0:`enable ${d.service} in the Google Cloud console \u2014 doctor never auto-enables APIs`});return}t({name:d.name,severity:"warn",message:L(c.reason),hint:`enable ${d.service} in the Google Cloud console \u2014 doctor never auto-enables APIs`})})}async function tT(e,t,r,n){let o=Hr(e),i=e.proxy.routes.filter((u)=>u.auth.type.toLowerCase()==="iap"),s=i.filter((u)=>u.auth.audience.trim()!=="");for(let u of i)if(u.auth.audience.trim()==="")t({name:`IAP audience ${u.name}`,severity:"error",message:"missing audience",hint:"set auth.audience to the IAP OAuth client ID"});if(o.length===0&&s.length===0)return;if(!(r.hasLocalAdc??Ki)())return;let c=r.mintToken??tc.mintToken,l=o.map(async(u)=>{try{await Xi(c(`sa:${u}`,""),Ji),t({name:`Impersonate ${u}`,severity:"ok",message:"token minted"})}catch(f){let p=(r.adcQuotaProject??du)(),m=n||"the ADC principal",h=p===""?"unknown":p,v=Yg(`Impersonate ${u}`,f,`grant roles/iam.serviceAccountTokenCreator to ${m} on ${u}; ADC quota project: ${h}`);if(p!==""&&v.message.toLowerCase().includes("api is not enabled"))v.message=`IAM Credentials API is disabled in ADC quota project ${p}`,v.hint=`enable iamcredentials.googleapis.com in ${p}, or set the ADC quota project to ${e.google.project_id}`;else if(v.message.toLowerCase()==="google authentication failed")v.message=`access-token mint failed: ${m} \u2192 ${u}`,v.hint=`verify roles/iam.serviceAccountTokenCreator for ${m} on this service account; ADC quota project: ${h}`;t(v)}}),d=s.map(async(u)=>{let f=no(u.auth).kind===ro?`sa:${no(u.auth).serviceAccount}`:"user";try{let p=ec(u.auth);await Xi(c(f,u.auth.audience,p),Ji),t({name:`IAP ${u.name}`,severity:"ok",message:"id token minted"})}catch(p){t(Yg(`IAP ${u.name}`,p,"check IAP OAuth client ID and ADC"))}});await Promise.all([...l,...d])}function Yg(e,t,r){let n=L(t),o=n.toLowerCase();if(o.includes("timeout")||o.includes("network")||o.includes("econnrefused")||o.includes("enotfound"))return{name:e,severity:"warn",message:n,hint:"network or timeout \u2014 retry when online"};if(t instanceof Ve&&t.hint!==""){let i=`${t.kind}: `,s=t.message.startsWith(i)?t.message.slice(i.length):t.message;return{name:e,severity:"error",message:s,hint:t.hint}}return{name:e,severity:"error",message:n,hint:r}}function Xi(e,t){return new Promise((r,n)=>{let o=setTimeout(()=>n(Error("timeout")),t);e.then((i)=>{clearTimeout(o),r(i)},(i)=>{clearTimeout(o),n(i)})})}async function Jg(e,t,r){let n=await zn(t);if(!n)return{name:e,severity:"error",message:"already in use",hint:`stop the process using port ${t} or change ${r}`};if(n.pid===process.pid)return{name:e,severity:"error",message:`in use by this TUI (${n.command} pid ${n.pid})`,hint:"stop the proxy from the proxy screen (x), then rerun doctor"};return{name:e,severity:"error",message:`in use by ${n.command} (pid ${n.pid})`,hint:"enter stop that process after a confirmation",action:{kind:"free-port",holder:n}}}function Xg(e){let t=[`${er()} doctor`,""];for(let r of e.checks){let n=r.severity==="error"?"\u2717":r.severity==="warn"?"!":"\u2713";if(t.push(`${n} ${r.name}`),r.severity!=="ok"){if(t.push(` ${r.message}`),r.hint)t.push(` \u2192 ${r.hint}`)}}return t.push("",`${e.issues} issue(s) found.`),t.join(`
|
|
2238
|
+
`,{mode:384})}function uh(e){let t=Hi(e)&&Ed(e).isDirectory()?e:ch(e);if(Nr(t),process.platform==="darwin"){let r=Hi(e)&&Ed(e).isFile()?["-R",e]:[t];Rd("open",r,{detached:!0,stdio:"ignore"}).unref();return}if(process.platform==="win32"){Rd("explorer",[t],{detached:!0,stdio:"ignore"}).unref();return}Rd("xdg-open",[t],{detached:!0,stdio:"ignore"}).unref()}function ph(e=oa()){if(!Hi(e))return[];return Td(e).filter((t)=>t.startsWith(Ad)).sort().reverse()}function mh(e,t=oa()){let r=jo(t,e);if(!Hi(r))return[];let n=[];for(let i of Td(r)){if(!i.endsWith(".log"))continue;let s=SR(jo(r,i),"utf8");for(let a of s.split(`
|
|
2239
|
+
`)){if(a.trim()==="")continue;let c=a.split(" "),l=c.slice(3).join(" "),d=wd(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 ER(e){let t=e.replace(/[^A-Za-z0-9._-]+/g,"_");return t===""?"service":t}function TR(e,t,r){if(!Hi(e))return;let n=Td(e).filter((i)=>i.startsWith(Ad)).map((i)=>{let s=jo(e,i),a=Ed(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)kR(i.path,{recursive:!0,force:!0})})}te();var{spawn:Od}=globalThis.Bun;import{createServer as xh}from"net";var AR=65535,Md=1;async function wh(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>=Md?l:c.value;if(l===void 0&&c.auto)d=await LR();if(d<Md||d>AR)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 Uo(d))throw await DR(i,c.name,d);n[d]=i,a[c.name]=d}o[i]=a}return o}function LR(){return new Promise((e,t)=>{let r=xh();r.listen(0,"127.0.0.1",()=>{let n=r.address();r.close(()=>{if(n&&typeof n==="object")e(n.port);else t(Se(Y,"unable to allocate dynamic port",Error("no address")))})}),r.on("error",(n)=>t(Se(Y,"unable to allocate dynamic port",n)))})}async function Ra(e){let t=e.ports.filter((n)=>!n.auto&&n.value>=Md);if(t.length===0)return;let r={};for(let n of t){if(await Uo(n.value))return;r[n.name]=n.value}return r}function Uo(e){return new Promise((t)=>{let r=xh();r.listen(e,"127.0.0.1",()=>{r.close(()=>t(!0))}),r.on("error",()=>t(!1))})}async function DR(e,t,r){let n=await Jn(r);return IR(e,t,r,n)}function IR(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 MR(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 Jn(e){if(process.platform==="win32")return NR(e);let t=await OR(e);if(t)return t;return $R(e)}async function OR(e){try{let t=Od({cmd:["lsof","-nP",`-iTCP:${e}`,"-sTCP:LISTEN"],stdout:"pipe",stderr:"ignore"}),r=await new Response(t.stdout).text();return await t.exited,MR(r,e)}catch{return}}async function $R(e){let t;try{let o=Od({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 FR(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 NR(e){let t=Od({cmd:["netstat","-ano"],stdout:"pipe",stderr:"ignore"}),r=t.stdout?await new Response(t.stdout).text():"";return await t.exited,FR(r,e)}var HR=400;async function _a(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 Se(He,`could not stop pid ${e.pid} on port ${e.port}`,t)}await jR(HR);try{process.kill(e.pid,0),process.kill(e.pid,"SIGKILL")}catch{return}}function jR(e){return new Promise((t)=>{setTimeout(t,e)})}ja();te();import{readdirSync as X_,readFileSync as Q_}from"fs";import{basename as Mg,dirname as Z_,extname as eE,join as ro,resolve as tE}from"path";import{parse as rE}from"yaml";dt();function N(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}function qd(e){return N(e)?new Set(Object.keys(e)):new Set}function U(e){if(typeof e==="string")return e;if(typeof e==="number"||typeof e==="boolean")return String(e);return""}function Ce(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 _t(e){if(!Array.isArray(e))return[];return e.map((t)=>U(t))}function Vd(e){if(!Array.isArray(e))return[];return e.map((t)=>N(t)?{service:U(t.service),condition:U(t.condition)||"service_started"}:U(t))}function nn(e){if(!N(e))return{};let t={};for(let[r,n]of Object.entries(e))t[r]=U(n);return t}function _r(e){if(typeof e==="string")return{args:e.split(/\s+/).filter((t)=>t!==""),shell:!1};if(Array.isArray(e))return{args:e.map((t)=>U(t)),shell:!1};return Wo()}function zd(e){if(e===void 0||e===null)return[];if(typeof e==="string"||typeof e==="number")return[Kd(e,"http")];if(Array.isArray(e))return e.map((t,r)=>Kd(t,`port_${r}`));if(N(e))return Object.entries(e).map(([t,r])=>Kd(r,t));return[]}function Kd(e,t){if(typeof e==="string"&&e.toLowerCase()==="auto")return{name:t,value:0,auto:!0};return{name:t,value:Ce(e),auto:!1}}function Dh(e){if(!N(e))return Nd();let t={},r=[],n={};for(let[o,i]of Object.entries(e))if(o==="required")r=_t(i);else if(o==="defaults")n=nn(i);else t[o]=U(i);return{vars:t,required:r,defaults:n}}function JR(e){if(!N(e))return jd();return{type:U(e.type),mode:U(e.mode),service_account:U(e.service_account),config:N(e.config)?{...e.config}:{}}}function XR(e){if(!N(e))return Hd();return{type:U(e.type),url:U(e.url),address:U(e.address),command:_r(e.command),interval_seconds:Ce(e.interval_seconds),timeout_seconds:Ce(e.timeout_seconds),start_period_seconds:Ce(e.start_period_seconds),unhealthy_threshold:e.unhealthy_threshold===void 0?3:Ce(e.unhealthy_threshold),healthy_reset_threshold:e.healthy_reset_threshold===void 0?10:Ce(e.healthy_reset_threshold)}}function QR(e){if(!N(e))return{policy:"",max_retries:0,backoff_seconds:0};return{enabled:e.enabled===void 0?void 0:ot(e.enabled),policy:U(e.policy),max_retries:Ce(e.max_retries),backoff_seconds:Ce(e.backoff_seconds)}}function ZR(e){if(!N(e))return{wait_for_healthy:!1,timeout_seconds:0};return{wait_for_healthy:ot(e.wait_for_healthy),timeout_seconds:Ce(e.timeout_seconds)}}function Gi(e){if(!N(e))return xn();let t=N(e.logs)?e.logs:{};return{extends:U(e.extends),description:U(e.description),command:_r(e.command),shell:ot(e.shell),working_dir:U(e.working_dir),dependencies:Vd(e.dependencies),ports:zd(e.ports),environment:Dh(e.environment),health:XR(e.health),identity:JR(e.identity),logs:{stdout:ot(t.stdout),stderr:ot(t.stderr)},restart:QR(e.restart),startup:ZR(e.startup),capabilities:_t(e.capabilities),proxy:Xd(e.proxy),expose:Jd(e.expose),container:Yd(e.container),watch:e_(e.watch),hooks:t_(e.hooks)}}function e_(e){if(!N(e))return $d();return{enabled:ot(e.enabled),paths:_t(e.paths),debounce_ms:La(Ce(e.debounce_ms)),ignore:e.ignore===void 0?[...ji]:_t(e.ignore)}}function t_(e){let t=N(e)?e:{};return{pre_start:_r(t.pre_start),post_start:_r(t.post_start)}}function Ih(e){let t=N(e)?e:{};return{command:_r(t.command),shell:ot(t.shell),working_dir:U(t.working_dir),dependencies:_t(t.dependencies),environment:Dh(t.environment)}}function Yd(e){if(!N(e))return;let t={};if(N(e.ports))for(let[r,n]of Object.entries(e.ports))t[r]=Ce(n);return{image:U(e.image),runtime:U(e.runtime),ports:t,env:nn(e.env),volumes:_t(e.volumes)}}function Jd(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:U(e.host),port:U(e.port)};return Fd()}function Xd(e){if(e===void 0||e===null)return[];if(Array.isArray(e))return e.map((t)=>Zn(t));if(N(e))return[Zn(e)];return[]}function Ba(e){if(!N(e))return{services:[],environment:{}};return{services:_t(e.services),environment:nn(e.environment)}}function r_(e){if(typeof e==="string")return{type:e,service_account:""};if(!N(e))return{type:"",service_account:""};return{type:U(e.type),service_account:U(e.service_account)}}function n_(e){if(!N(e))return Bi();return{type:U(e.type),identity:r_(e.identity),audience:U(e.audience),service_account:U(e.service_account),client_id:U(e.client_id),client_secret:U(e.client_secret),credentials:U(e.credentials),headers:nn(e.headers)}}function Zn(e){if(!N(e))return{name:"",match:{host:"",path:""},upstream:{url:""},auth:Bi()};let t=N(e.match)?e.match:{},r=N(e.upstream)?e.upstream:{};return{name:U(e.name),transport:U(e.transport),match:{host:U(t.host),path:U(t.path)},upstream:{url:U(r.url),service:U(r.service),port:U(r.port)},auth:n_(e.auth),listen:N(e.listen)?{host:U(e.listen.host),port:Ce(e.listen.port)}:void 0}}te();import{existsSync as o_,statSync as Oh}from"fs";import{basename as Mh,dirname as Ua,isAbsolute as i_,join as Wa,resolve as $h}from"path";function eo(e,t){if(t!=="")return s_(t);let r=e===""?process.cwd():e;r=i_(r)?r:$h(r);for(;;){let n=Wa(r,Nt,dr);if(Ki(n))return{repoRoot:r,configPath:n};let o=Wa(r,"devctl.yaml");if(Ki(o))return{repoRoot:r,configPath:o};let i=Ua(r);if(i===r)break;r=i}throw Te(zn,"no devctl configuration found","run `devctl setup` or create a .devctl/config.yaml in the repository root")}function s_(e){let t=$h(e),r;try{r=Oh(t)}catch(o){throw Se(zn,"config path not found",o)}if(r.isDirectory()){if(Mh(t)===Nt)return{repoRoot:Ua(t),configPath:Wa(t,dr)};return{repoRoot:t,configPath:Wa(t,dr)}}let n=Ua(t);if(Mh(n)===Nt)n=Ua(n);return{repoRoot:n,configPath:t}}function Ki(e){return o_(e)&&!Oh(e).isDirectory()}dt();function qa(){return{services:{},templates:{},provenance:{}}}function on(e,t,r,n,o=""){if(N(t)&&Object.keys(t).length>0){for(let[i,s]of Object.entries(t))on(e,s,r,n,o===""?i:`${o}.${i}`);return}if(o==="")return;(e[o]??=[]).push({source:r,layer:n})}var a_=["environment","restart","startup","health","logs","identity","container","watch","hooks"];function Ga(e,t,r){let n=qd(r);if(N(r))for(let i of a_){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 Fh(e,t,r=qa(),n={source:"unknown",layer:"unknown"}){if(on(r.provenance,t,n.source,n.layer),t.version!==void 0)e.version=Ce(t.version);if(N(t.project)&&t.project.name!==void 0)e.project.name=U(t.project.name);if(N(t.google)){if(t.google.project_id!==void 0)e.google.project_id=U(t.google.project_id);if(t.google.region!==void 0)e.google.region=U(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?c_(s,i):Ba(i)}if(N(t.templates))for(let[o,i]of Object.entries(t.templates)){let s=e.templates[o];e.templates[o]=s?Ka(s,i):Gi(i),Ga(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?Ka(s,i):Gi(i),Ga(r.services,o,i)}if(N(t.tasks))for(let[o,i]of Object.entries(t.tasks))e.tasks[o]=e.tasks[o]?l_(e.tasks[o],i):Ih(i);if(N(t.proxy))Qd(e.proxy,t.proxy);if(N(t.logs)){if(t.logs.max_memory_events!==void 0)e.logs.max_memory_events=Ce(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=U(o.directory);if(o.retention_days!==void 0)e.logs.persistence.retention_days=Ce(o.retention_days);if(o.max_session_logs!==void 0)e.logs.persistence.max_session_logs=Ce(o.max_session_logs)}}if(N(t.auth)&&t.auth.refresh_threshold_seconds!==void 0)e.auth.refresh_threshold_seconds=Ce(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=Ce(t.shutdown.grace_seconds)}if(N(t.ui)){if(t.ui.theme!==void 0)e.ui.theme=U(t.ui.theme);if(t.ui.keymap!==void 0)e.ui.keymap=nn(t.ui.keymap)}if(N(t.secrets)){if(t.secrets.extra_markers!==void 0)e.secrets.extra_markers=_t(t.secrets.extra_markers);if(t.secrets.extra_patterns!==void 0)e.secrets.extra_patterns=_t(t.secrets.extra_patterns)}if(N(t.doctor)&&Array.isArray(t.doctor.tools))e.doctor.tools=t.doctor.tools.filter(N).map((o)=>({name:U(o.name),command:U(o.command)}));if(Array.isArray(t.plugins))e.plugins=t.plugins.filter(N).map((o)=>({path:U(o.path)})).filter((o)=>o.path!=="");if(N(t.environment)){if(Array.isArray(t.environment.sources))e.environment.sources=_t(t.environment.sources);if(N(t.environment.secrets))e.environment.secrets={...e.environment.secrets,...nn(t.environment.secrets)}}}function Qd(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=U(t.credentials);if(N(t.listen)){if(t.listen.host!==void 0)e.listen.host=U(t.listen.host);if(t.listen.port!==void 0)e.listen.port=Ce(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=U(r.host);if(r.port!==void 0)e.token_endpoint.port=Ce(r.port)}if(Array.isArray(t.routes))e.routes=t.routes.map((r)=>Zn(r))}function c_(e,t){if(!N(t))return e;return{services:t.services!==void 0?_t(t.services):e.services,environment:t.environment!==void 0?{...e.environment,...nn(t.environment)}:e.environment}}function l_(e,t){if(!N(t))return e;return{command:t.command!==void 0?_r(t.command):e.command,shell:t.shell!==void 0?ot(t.shell):e.shell,working_dir:t.working_dir!==void 0?U(t.working_dir):e.working_dir,dependencies:t.dependencies!==void 0?_t(t.dependencies):e.dependencies,environment:Nh(e.environment,t.environment)}}function Ka(e,t){if(!N(t))return e;let r=qd(t),n={...e,environment:Nh(e.environment,t.environment),health:p_(e.health,t.health),identity:m_(e.identity,t.identity),logs:f_(e.logs,t.logs),restart:h_(e.restart,t.restart),startup:g_(e.startup,t.startup),container:v_(e.container,t.container),watch:d_(e.watch,t.watch),hooks:u_(e.hooks,t.hooks)};if(r.has("extends"))n.extends=U(t.extends);if(r.has("description"))n.description=U(t.description);if(r.has("command"))n.command=_r(t.command);if(r.has("shell"))n.shell=ot(t.shell);if(r.has("working_dir"))n.working_dir=U(t.working_dir);if(r.has("dependencies"))n.dependencies=Vd(t.dependencies);if(r.has("ports"))n.ports=zd(t.ports);if(r.has("capabilities"))n.capabilities=_t(t.capabilities);if(r.has("proxy"))n.proxy=Xd(t.proxy);if(r.has("expose"))n.expose=Jd(t.expose);return n}function d_(e,t){if(!N(t))return e;return{enabled:t.enabled!==void 0?ot(t.enabled):e.enabled,paths:t.paths!==void 0?_t(t.paths):e.paths,debounce_ms:t.debounce_ms!==void 0?La(Ce(t.debounce_ms)):e.debounce_ms,ignore:t.ignore!==void 0?_t(t.ignore):e.ignore}}function u_(e,t){if(!N(t))return e;return{pre_start:t.pre_start!==void 0?_r(t.pre_start):e.pre_start,post_start:t.post_start!==void 0?_r(t.post_start):e.post_start}}function Nh(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=_t(s);else if(i==="defaults")Object.assign(n,nn(s));else r[i]=U(s);return{vars:r,required:o,defaults:n}}function p_(e,t){if(!N(t))return e;return{type:t.type!==void 0?U(t.type):e.type,url:t.url!==void 0?U(t.url):e.url,address:t.address!==void 0?U(t.address):e.address,command:t.command!==void 0?_r(t.command):e.command,interval_seconds:t.interval_seconds!==void 0?Ce(t.interval_seconds):e.interval_seconds,timeout_seconds:t.timeout_seconds!==void 0?Ce(t.timeout_seconds):e.timeout_seconds,start_period_seconds:t.start_period_seconds!==void 0?Ce(t.start_period_seconds):e.start_period_seconds,unhealthy_threshold:t.unhealthy_threshold!==void 0?Ce(t.unhealthy_threshold):e.unhealthy_threshold,healthy_reset_threshold:t.healthy_reset_threshold!==void 0?Ce(t.healthy_reset_threshold):e.healthy_reset_threshold}}function m_(e,t){if(!N(t))return e;return{type:t.type!==void 0?U(t.type):e.type,mode:t.mode!==void 0?U(t.mode):e.mode,service_account:t.service_account!==void 0?U(t.service_account):e.service_account,config:t.config!==void 0&&N(t.config)?{...e.config??{},...t.config}:e.config}}function f_(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 h_(e,t){if(!N(t))return e;return{enabled:t.enabled!==void 0?ot(t.enabled):e.enabled,policy:t.policy!==void 0?U(t.policy):e.policy,max_retries:t.max_retries!==void 0?Ce(t.max_retries):e.max_retries,backoff_seconds:t.backoff_seconds!==void 0?Ce(t.backoff_seconds):e.backoff_seconds}}function g_(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?Ce(t.timeout_seconds):e.timeout_seconds}}function v_(e,t){if(!N(t))return e;let r=Yd(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 Hh(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)on(t,c,`synthesized from services.${n}.proxy`,"synthesized",`proxy.routes.${l}`)})}y_(e,t)}function jh(e){let t=e.repoRoot||process.cwd(),r=e.proxy.credentials.trim()===""?"":Ti(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:Ti(o,t)}}function y_(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&&Qn(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:Bi()},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";on(t,a,`synthesized from ${l}`,"synthesized",`proxy.routes.${c}`)}}}function Bh(e,t=qa()){if(Object.keys(e.templates).length===0)return;let r={};for(let[n,o]of Object.entries(e.services))r[n]=Uh(e,o,t.services[n]??new Set,t,{});e.services=r}function Uh(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]??xn();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=Uh(e,i,a,n,s),l=b_(c,t,r);return l.extends=t.extends,l}function b_(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();dt();function Wh(e){if(e.version===0)throw P(Y,"version is required");if(e.version===Go)return e;throw P(Y,`unsupported config version ${e.version} (expected ${Go}); no migration is available`)}var Gh=["version","project","google","profiles","templates","services","tasks","proxy","logs","auth","shutdown","ui","secrets","doctor","plugins","environment"],Kh=["extends","description","command","shell","working_dir","dependencies","ports","environment","health","identity","logs","restart","startup","capabilities","proxy","expose","container","watch","hooks"],qh=["type","url","address","command","interval_seconds","timeout_seconds","start_period_seconds","unhealthy_threshold","healthy_reset_threshold"],Vh=["service","condition"],zh=["type","mode","service_account","config"],Yh=["enabled","policy","max_retries","backoff_seconds"],Jh=["wait_for_healthy","timeout_seconds"],Xh=["enabled","host","port"],Qh=["enabled","gateway","credentials","listen","token_endpoint","routes"],Va=["host","port"],Zd=["name","transport","listen","match","upstream","auth"],eu=["host","path"],tu=["url","service","port"],ru=["type","identity","audience","service_account","client_id","client_secret","credentials","headers"],Zh=["max_memory_events","persistence"],eg=["enabled","directory","retention_days","max_session_logs"],tg=["refresh_threshold_seconds"],rg=["stop_services_on_exit","grace_seconds"],ng=["theme","keymap"],og=["name"],ig=["project_id","region"],sg=["services","environment"],nu=["required","defaults"],ag=["stdout","stderr"],cg=["extra_markers","extra_patterns"],lg=["tools"],dg=["name","command"],ug=["enabled","host","port"],pg=["path"],mg=["sources","secrets"],fg=["image","runtime","ports","env","volumes"],hg=["enabled","paths","debounce_ms","ignore"],gg=["pre_start","post_start"],vg=["command","shell","working_dir","dependencies","environment"],yg=["google","google_api","iap","network","service_identity","local_http"],bg=["|","||","&&",";",">",">>","<","&"];var x_=2;function Pn(e,t){if(Array.isArray(e))return e.flatMap((o,i)=>Pn(o,xg(t,String(i))));if(!__(e))return[];let r=S_(t),n=[];for(let o of Object.keys(e)){let i=xg(t,o);if(r.length>0&&!r.includes(o)&&!w_(t))n.push(i);n.push(...Pn(e[o],i))}return n}function w_(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(".auth.headers"))return!0;if(e.endsWith(".identity.config")||e.includes(".identity.config."))return!0;return e.includes(".environment.")&&!e.endsWith(".environment")}function S_(e){switch(e){case"":return Gh;case"project":return og;case"google":return ig;case"proxy":return Qh;case"proxy.listen":return Va;case"proxy.token_endpoint":return ug;case"logs":return Zh;case"logs.persistence":return eg;case"auth":return tg;case"shutdown":return rg;case"ui":return ng;case"secrets":return cg;case"doctor":return lg;case"environment":return mg;default:return k_(e)}}function k_(e){if(e==="services"||e==="profiles"||e==="templates"||e==="tasks")return[];if(e.startsWith("services.")||e.startsWith("templates.")){if(e.includes(".dependencies."))return Vh;return C_(e)}if(e.startsWith("profiles.")){if(e.split(".").length===2)return sg}if(e.startsWith("tasks.")){let t=e.split(".");if(t.length===2)return vg;if(t[2]==="environment")return nu}if(e.includes("routes"))return R_(e);if(e.startsWith("doctor.tools")&&e.split(".").length>=3)return dg;if(e==="plugins")return[];if(e.startsWith("plugins.")&&e.split(".").length===2)return pg;return[]}function C_(e){let t=e.split(".");if(t.length===2)return Kh;if(t.length>=3)switch(t[2]){case"health":return qh;case"identity":return zh;case"restart":return Yh;case"startup":return Jh;case"logs":return ag;case"environment":return nu;case"proxy":return P_(t);case"expose":return Xh;case"container":return fg;case"watch":return hg;case"hooks":return gg;default:return[]}return[]}function P_(e){let t=e.slice(3),r=t[0]!==void 0&&/^\d+$/.test(t[0])?1:0,n=t[r]??"";if(n==="match")return eu;if(n==="upstream")return tu;if(n==="auth")return ru;if(n==="listen")return Va;return Zd}function R_(e){if(e==="proxy.routes")return[];if(e.endsWith(".match"))return eu;if(e.endsWith(".upstream"))return tu;if(e.endsWith(".auth"))return ru;if(e.endsWith(".listen"))return Va;if(e.includes("proxy.routes")&&e.split(".").length===x_+1)return Zd;return[]}function xg(e,t){if(e==="")return t;return`${e}.${t}`}function __(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}function qo(e){return`unknown fields: ${e.join(", ")}`}dt();function Er(e){let t=e.trim().toLowerCase();if(t==="")return!0;if(t==="localhost"||t==="::1")return!0;if(wg(t))return!0;return E_(t)}function wg(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 E_(e){if(!e.startsWith("::ffff:"))return!1;return wg(e.slice(7))}import{existsSync as L_}from"fs";import{isAbsolute as D_,resolve as I_}from"path";import{fileURLToPath as M_}from"url";dt();function Sg(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=Eh(e.ports);if(r&&!r.auto)return r.value;return}function T_(e,t,r,n=""){let o=e,i="";for(;;){let s=o.indexOf("${");if(s<0)return i+o;i+=o.slice(0,s);let a=o.slice(s).indexOf("}");if(a<0)throw Error(`unclosed environment reference in "${e}"`);let c=o.slice(s+2,s+a);i+=A_(c,t,r,n),o=o.slice(s+a+1)}}function A_(e,t,r,n){let o=e.split(".");if(o[0]==="identity"){if(o.length===2&&o[1]==="user")return n;throw Error(`unsupported reference \${${e}}`)}if(o.length<3||o[0]!=="services")throw Error(`unsupported reference \${${e}}`);let i=o[1]??"",s=t.services[i];if(!s)throw Error(`reference \${${e}}: unknown service`);let a=r[i];if(o[2]==="host"||o[2]==="url"){let c=t.proxy.enabled?t.proxy.routes.find((d)=>d.upstream.service===i):void 0;if(o[2]==="host")return c?c.match.host||"127.0.0.1":"127.0.0.1";if(c)return`http://${c.match.host||"127.0.0.1"}:${t.proxy.listen.port}${c.match.path}`;let l=Sg(s,a);if(l===void 0)throw Error(`unresolvable reference \${${e}}`);return`http://127.0.0.1:${l}`}if(o[2]==="port"){let c=Sg(s,a);if(c!==void 0)return String(c)}if(o[2]==="ports"){let c=o[3];if(!c)throw Error(`reference \${${e}}: missing port name`);if(a&&a[c]!==void 0)return String(a[c]);let l=Qn(s.ports,c);if(l&&!l.auto)return String(l.value);let d=Number.parseInt(c,10);if(!Number.isNaN(d)&&d>=0&&d<s.ports.length){let u=s.ports[d];if(u&&!u.auto)return String(u.value)}}throw Error(`unresolvable reference \${${e}}`)}function ou(e,t,r,n=""){let o={};for(let[i,s]of Object.entries(e))o[i]=T_(s,t,r,n);return o}function kg(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 Cg(e,t){let r=e.split(".");if(r.length<2)return!1;if(r[0]==="identity")return r.length===2&&r[1]==="user";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 Qn(n.ports,o)!==void 0}}return!0}dt();var qi=65535,Vi=1,Ag=["http","tcp","process","command"];function su(e){let t=[];for(let[r,n]of Object.entries(e.services)){let o=n.health.type;if(o!==""&&!Ag.includes(o.toLowerCase()))t.push({service:r,type:o})}return t}var O_=0,Eg=1,Tg=2;function to(e){let t=[];if(e.version===0)t.push("version is required");else if(e.version!==Go)t.push(`unsupported config version ${e.version} (expected ${Go}); no migration is available`);if(Object.keys(e.services).length===0)t.push("at least one service must be defined");t.push(...F_(e)),t.push(...$_(e)),t.push(...B_(e)),t.push(...U_(e)),t.push(...W_(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:")?M_(n.path):D_(n.path)?n.path:I_(e.repoRoot,n.path);if(!L_(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(wn(n.command))t.push(`${o}.command is required`);t.push(...Ya(o,n.command,n.shell));for(let i of n.dependencies)if(!e.services[i])t.push(`${o}.dependencies: unknown service "${i}"`);t.push(...Lg(`${o}.environment`,n.environment,e))}return t}function F_(e){let t=[],r={};for(let[n,o]of Object.entries(e.services)){let i=`services.${n}`;if(wn(o.command)&&!o.container)t.push(`${i}.command is required`);t.push(...Ya(i,o.command,o.shell)),t.push(...Ya(`${i}.hooks.pre_start`,o.hooks.pre_start,o.shell)),t.push(...Ya(`${i}.hooks.post_start`,o.hooks.post_start,o.shell)),t.push(...Y_(i,o.capabilities));for(let c of o.dependencies){let l=Yt(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(bn(c)))t.push(`${i}.dependencies: condition must be service_started or service_healthy`);if(bn(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<Vi||c.value>qi)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=j_(`${i}.identity`,o.identity,e.plugins.length>0);if(s!=="")t.push(s);if(t.push(...N_(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=Ma(o.restart);if(a!==Ea&&a!==Ta&&a!==Aa)t.push(`${i}.restart.policy must be never, on_failure, or always`);if(t.push(...Lg(`${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<Vi||l>qi)t.push(`${i}.container.ports.${c}: invalid container port ${l}`)}}}return t}function N_(e,t,r){let n=[];if(t.health.type==="")return n;let o=t.health.type.toLowerCase();if(!Ag.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"&&wn({args:t.health.command.args,shell:!1}))n.push(`${e}.health.command is required for command health checks`);return n}var H_=["","user","service","service_account"];function j_(e,t,r){let n=Wi(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 au(e){let t=[];for(let[r,n]of Object.entries(e.services)){let o=Wi(n.identity).toLowerCase();if(!H_.includes(o))t.push({service:r,type:o})}return t}function B_(e){let t={},r=[],n=[],o=(i)=>{let s=t[i]??O_;if(s===Tg)return;if(s===Eg){r.push(`dependency cycle: ${[...n,i].join(" \u2192 ")}`);return}t[i]=Eg,n.push(i);let a=e.services[i];if(a)for(let c of a.dependencies){let l=Yt(c);if(e.services[l])o(l)}n.pop(),t[i]=Tg};for(let i of Object.keys(e.services))o(i);return r}function Lg(e,t,r){let n=[],o=(i,s)=>{for(let a of kg(s))if(!Cg(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 U_(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 W_(e){let t=[];if(e.proxy.listen.host!==""){if(!Dg(e.proxy.listen.host))t.push("proxy.listen.host must be an IP address or localhost");else if(!Er(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<Vi||e.proxy.listen.port>qi))t.push("proxy.listen.port is invalid");let r={},n=new Set;return e.proxy.routes.forEach((o,i)=>{let s=`proxy.routes[${i}]`;if(o.name==="")t.push(`${s}.name is required`);else if(r[o.name])t.push(`${s}: duplicate route name ${o.name}`);r[o.name]=!0;let a=(o.transport??"").toLowerCase();if(a!==""&&a!=="http"&&a!=="grpc")t.push(`${s}.transport must be "http" or "grpc"`);if(t.push(...K_(o,s,e)),t.push(...q_(o,s)),Ko(o))t.push(...G_(o,s,n,e))}),t.push(...z_(e)),t}function G_(e,t,r,n){let o=[],i=e.listen;if(!i||i.port===0)o.push(`${t}.listen.port is required for a grpc route`);else{if(i.port<Vi||i.port>qi)o.push(`${t}.listen.port is invalid`);if(r.has(i.port))o.push(`${t}.listen.port ${i.port} is already used by another grpc route`);if(r.add(i.port),i.port===n.proxy.listen.port)o.push(`${t}.listen.port must differ from proxy.listen.port`);if(n.proxy.token_endpoint.enabled&&i.port===n.proxy.token_endpoint.port)o.push(`${t}.listen.port must differ from proxy.token_endpoint.port`);if(i.host!==""&&(!Dg(i.host)||!Er(i.host)))o.push(`${t}.listen.host must be a loopback address`)}if(!(e.upstream.url??"").trim().toLowerCase().startsWith("https://"))o.push(`${t}.upstream.url must be an https:// address for a grpc route`);if(e.match.host!==""||e.match.path!=="")o.push(`${t}.match is not supported on a grpc route`);return o}function K_(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(Qn(s.ports,a)===void 0)n.push(`${t}.upstream: service ${i} has no port named ${a}`);return n}function q_(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(...V_(e.auth,t));let n=e.auth.identity.type.toLowerCase();if(n==="service"||n==="service_account"||tn({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 V_(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!==""&&iu(n).length>0&&!_g(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 z_(e){let t=[],r=e.proxy.token_endpoint;if(!r.enabled)return t;let n=r.host||Da;if(!Er(n))t.push("proxy.token_endpoint.host must be a loopback address");if(r.port!==0&&(r.port<Vi||r.port>qi))t.push("proxy.token_endpoint.port is invalid");return t}function Ya(e,t,r){if(t.shell||r)return[];for(let n of t.args)if(bg.includes(n)||n.includes("|")||n.includes(";")||n.includes("&&"))return[`${e}.command contains shell metacharacters; set shell: true to run via a shell`];return[]}function Y_(e,t){let r=[];for(let n of t)if(!yg.includes(n))r.push(`${e}.capabilities: unknown capability "${n}"`);return r}function Dg(e){if(e==="localhost")return!0;return J_(e)}function J_(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 zi(e,t,r){try{return Yi(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 sn(e,t){let{repoRoot:r,configPath:n}=eo(e,t);return Yi(r,n)}function no(e,t){try{return sn(e,t)}catch(r){if(!Pa(r,zn))throw r;let n=e===""?process.cwd():tE(e),o=Xn();return o.repoRoot=n,o.configPath=ro(n,Nt,dr),o}}function Yi(e,t,r){let n=Xn(),o=qa();Og(t,n,o,r?.candidateText,"main"),n.repoRoot=e,n.configPath=t;let i=Z_(t);if(Mg(i)===Nt)oE(i,n,o);n=nE(n,e,t,o),n=Wh(n);try{Bh(n,o)}catch(a){throw Se(Y,"template merge failed",a)}Hh(n,o.provenance),jh(n),n.provenance=o.provenance;let s=to(n);if(s.length>0)throw P(Y,s.join(`
|
|
2242
|
+
`));return n}function nE(e,t,r,n){let o=ro(lr(),"config.local.yaml"),i=ro(t,Nt,"config.local.yaml");for(let s of[o,i]){if(s===r||!Ki(s))continue;Og(s,e,n,void 0,s===o?"home_local":"repo_local")}return e}function Og(e,t,r,n,o="main"){let i=n!==void 0?$g(n,e):cu(e),s=Pn(i,"");if(s.length>0)throw P(Y,qo(s));Fh(t,i,r,{source:e,layer:o})}function oE(e,t,r){Ig(ro(e,"services"),(o,i,s)=>{let a=Pn(i,`services.${o}`);if(a.length>0)throw P(Y,qo(a));let c=t.services[o];t.services[o]=c?Ka(c,i):Gi(i),Ga(r.services,o,i),on(r.provenance,i,s,"modular_service",`services.${o}`)}),Ig(ro(e,"profiles"),(o,i,s)=>{let a=Pn(i,`profiles.${o}`);if(a.length>0)throw P(Y,qo(a));t.profiles[o]=Ba(i),on(r.provenance,i,s,"modular_profile",`profiles.${o}`)});let n=ro(e,"proxy","routes.yaml");if(Ki(n))iE(n,t,r)}function iE(e,t,r){let n=cu(e);if(N(n.proxy))on(r.provenance,n.proxy,e,"modular_proxy","proxy");if(Array.isArray(n.routes))on(r.provenance,n.routes,e,"modular_proxy","proxy.routes");if(N(n.proxy)){let o=Pn({proxy:n.proxy},"");if(o.length>0)throw P(Y,qo(o))}if(Array.isArray(n.routes))n.routes.forEach((o,i)=>{let s=Pn(o,`proxy.routes.${i}`);if(s.length>0)throw P(Y,qo(s))});if(N(n.proxy)){let o={...n.proxy};if(delete o.routes,Qd(t.proxy,o),Array.isArray(n.proxy.routes))t.proxy.routes.push(...n.proxy.routes.map((i)=>Zn(i)))}if(Array.isArray(n.routes))t.proxy.routes.push(...n.routes.map((o)=>Zn(o)))}function Ig(e,t){let r;try{r=X_(e)}catch(n){if(sE(n))return;throw n}for(let n of r.sort()){let o=eE(n);if(o!==".yaml"&&o!==".yml")continue;let i=ro(e,n),s=cu(i);t(Mg(n,o),s,i)}}function cu(e){let t;try{t=Q_(e,"utf8")}catch(r){throw Se(Y,"unable to read config",r)}return $g(t,e)}function $g(e,t){let r;try{r=rE(e)}catch(n){throw Se(Y,`invalid YAML in ${t}`,n)}if(r===null||r===void 0)return{};if(!N(r))throw P(Y,`unable to decode config: ${t} is not a mapping`);return r}function sE(e){return typeof e==="object"&&e!==null&&"code"in e&&e.code==="ENOENT"}dt();te();Tr();import{existsSync as Qa,readFileSync as cE}from"fs";import{homedir as Bg}from"os";import{join as pu}from"path";var{spawn:mu}=globalThis.Bun;var lE="https://www.googleapis.com/auth/cloud-platform",Ug=1500,dE=2000;function zo(){let e=globalThis;if(e.window&&typeof e.window.fetch!=="function")e.window.fetch=fetch}function fu(){let e=process.env.GOOGLE_APPLICATION_CREDENTIALS||pu(Bg(),".config","gcloud","application_default_credentials.json");if(!Qa(e))return"";try{let t=JSON.parse(cE(e,"utf8"));return typeof t.quota_project_id==="string"?t.quota_project_id:""}catch{return""}}async function so(e){let t={gcloudInstalled:await hu("gcloud"),adcAvailable:!1,userEmail:"",projectID:"",projectSource:""};if(e!=="")t.projectID=e,t.projectSource="configuration";else{let r=hE("GOOGLE_CLOUD_PROJECT","GCLOUD_PROJECT","GOOGLE_PROJECT");if(r!=="")t.projectID=r,t.projectSource="environment variable";else if(t.gcloudInstalled){let n=await jg("core/project");if(n!=="")t.projectID=n,t.projectSource="gcloud configuration"}}if(Xi())try{await Vg(uE(t),dE)}catch(r){t.adcAvailable=!1,t.error=Yo(r)}if(t.userEmail===""&&t.gcloudInstalled)t.userEmail=await jg("core/account");return t}async function uE(e){zo();let{GoogleAuth:t}=await import("google-auth-library"),r=new t({scopes:[lE]});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 pE(r);if(o!=="")e.userEmail=o}async function pE(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=mE(r);if(n!=="")return n}}catch{return""}return""}function mE(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 Wg(e){let t=await so(e);return ur({kind:"user",email:t.userEmail,project:t.projectID,projectSource:t.projectSource,adcAvailable:t.adcAvailable})}async function Gg(){if(await mu({cmd:["gcloud","auth","application-default","login"],stdin:"inherit",stdout:"inherit",stderr:"inherit"}).exited!==0)throw Te(yn,"application-default login failed","run `gcloud auth application-default login`")}async function Kg(){if(await mu({cmd:["gcloud","auth","application-default","revoke","--quiet"],stdout:"ignore",stderr:"pipe"}).exited!==0)throw Te(yn,"failed to revoke application-default credentials","run `gcloud auth application-default revoke`")}function Yo(e){let t=fE(e).toLowerCase();if(io(t,["unauthorized_client"]))return Te(Ca,"IAP OAuth client does not match the ADC refresh token","omit client_id to use ADC's default client, or login with an OAuth client that matches auth.client_id");if(io(t,["unauth","invalid_grant","token has been expired","expired"]))return Te(yn,"credential expired or invalid","run `devctl auth login` or `gcloud auth application-default login`");if(io(t,["api has not been used","has not been enabled","access not configured","service_disabled"]))return Te(Yn,"required Google API is not enabled","enable the API in the target service account project; devctl will not enable APIs automatically");if(io(t,["permission","forbidden","iam"])){if(io(t,["serviceaccounttoken","iamcredentials","token creator","getaccesstoken"]))return Te(ka,"cannot impersonate service account","ask an administrator to grant roles/iam.serviceAccountTokenCreator on the target service account");return Te(Yn,"authorization failure","verify IAM permissions for the current user on this project")}if(t.includes("audience"))return Te(Ca,"IAP audience is incorrect","set auth.audience on the proxy route to the IAP OAuth client ID");if(t.includes("iap"))return Se(Ca,"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(io(t,["timeout","network","connection refused","no such host"]))return Se(lt,"network problem reaching Google Cloud",e);if(io(t,["adc","default credentials","could not find"]))return Te(yn,"application default credentials unavailable","run `gcloud auth application-default login`");return Se(yn,"Google authentication failed",e)}function fE(e){let t=[],r=new Set,n=(o,i)=>{if(o===null||o===void 0||i>8||r.has(o))return;if(typeof o==="string"){t.push(o);return}if(typeof o!=="object")return;r.add(o);let s=o;for(let a of["message","reason","status","code","error_description","permission","service"])n(s[a],i+1);for(let a of["error","errors","details","response","data","cause","metadata"]){let c=s[a];if(Array.isArray(c))c.forEach((l)=>n(l,i+1));else n(c,i+1)}};return n(e,0),t.length>0?t.join(" "):String(e)}function io(e,t){return t.some((r)=>e.includes(r))}function hE(...e){for(let t of e){let r=process.env[t];if(r)return r}return""}async function hu(e){try{return(await qg(process.platform==="win32"?["where",e]:["which",e],Ug)).code===0}catch{return!1}}function Xi(){let e=process.env.GOOGLE_APPLICATION_CREDENTIALS;if(e&&Qa(e))return!0;if(Qa(pu(Bg(),".config","gcloud","application_default_credentials.json")))return!0;let t=process.env.APPDATA;return Boolean(t&&Qa(pu(t,"gcloud","application_default_credentials.json")))}async function jg(e){try{let t=await qg(["gcloud","config","get-value",e],Ug);if(t.code!==0)return"";let r=t.stdout.trim();if(r===""||r==="(unset)")return"";return r}catch{return""}}async function qg(e,t){let r=mu({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 Vg(Promise.all([n,r.exited]),t);return{code:i??1,stdout:o}}catch{return r.kill(),{code:1,stdout:""}}}function Vg(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 LE}from"crypto";import{readFileSync as DE}from"fs";import{join as IE}from"path";import{GoogleAuth as Zi,Impersonated as ME}from"google-auth-library";var{spawn:bu}=globalThis.Bun;import{existsSync as ec,readdirSync as gE,readFileSync as zg,unlinkSync as vE}from"fs";import{join as Qi}from"path";var xu="keychain-index.json",ao="devctl.credentials",vu=".cred.json";function yu(e){return Qi(Cr(),`${rc(e)}${vu}`)}function tc(e){if(e==="file"||process.env.DEVCTL_HOME&&e!=="keychain")return new Za;if(e==="keychain"||process.platform==="darwin"||process.platform==="win32"||process.platform==="linux")return new Yg(new Za);return new Za}class Za{backend="file";async get(e){let t=wE(e);if(!t||t.accessToken==="")return;return t}async set(e,t){Pr(yu(e),`${JSON.stringify({...xE(t),key:e})}
|
|
2243
|
+
`)}async delete(e){let t=yu(e);if(ec(t))vE(t)}async list(){let e=Cr();if(!ec(e))return[];let t=[];for(let r of gE(e)){if(!r.endsWith(vu))continue;let n=r.slice(0,-vu.length),o=Jg(Qi(Cr(),r));if(!o)continue;t.push(Xg(o.key??n,o.record))}return t.sort((r,n)=>r.identity.localeCompare(n.identity))}}class Yg{backend="keychain";fallback;constructor(e){this.fallback=e}async get(e){let t=await SE(e);if(t)return t;return this.fallback.get(e)}async set(e,t){if(!await kE(e,t))await this.fallback.set(e,t);yE(e)}async delete(e){await CE(e),await this.fallback.delete(e),bE(e)}async list(){let e=new Set(wu()),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,Xg(n,o))}return[...r.values()].sort((n,o)=>n.identity.localeCompare(o.identity))}}function yE(e){let t=wu();if(!t.includes(e))Pr(Qi(Cr(),xu),`${JSON.stringify([...t,e])}
|
|
2244
|
+
`)}function bE(e){let t=wu(),r=t.filter((n)=>n!==e);if(r.length!==t.length)Pr(Qi(Cr(),xu),`${JSON.stringify(r)}
|
|
2245
|
+
`)}function wu(){let e=Qi(Cr(),xu);if(!ec(e))return[];try{let t=JSON.parse(zg(e,"utf8"));return Array.isArray(t)?t.filter((r)=>typeof r==="string"):[]}catch{return[]}}function xE(e){return{...e,accessToken:""}}function wE(e){return Jg(yu(e))?.record}function Jg(e){if(!ec(e))return;try{let t=JSON.parse(zg(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 Xg(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 rc(e){return e.replace(/[^A-Za-z0-9._-]+/g,"_").slice(0,80)||"token"}async function SE(e){let t=await PE(e);if(!t)return;try{return JSON.parse(t)}catch{return}}async function kE(e,t){let r=JSON.stringify(t);if(process.platform==="darwin")return await Jo(["security","delete-generic-password","-s",ao,"-a",e]),await Jo(["security","add-generic-password","-s",ao,"-a",e,"-w",r,"-U"])===0;if(process.platform==="linux")return await RE(["secret-tool","store","--label",`devctl ${e}`,"service",ao,"account",e],r)===0;if(process.platform==="win32"){let o=`Add-Type -AssemblyName System.Security; $bytes = [Convert]::FromBase64String('${Buffer.from(r,"utf8").toString("base64")}'); $enc = [System.Security.Cryptography.ProtectedData]::Protect($bytes, $null, 'CurrentUser'); [IO.File]::WriteAllBytes($env:LOCALAPPDATA + '\\devctl-cred-${rc(e)}.bin', $enc)`;return await Jo(["powershell","-NoProfile","-Command",o])===0}return!1}async function CE(e){if(process.platform==="darwin"){await Jo(["security","delete-generic-password","-s",ao,"-a",e]);return}if(process.platform==="linux"){await Jo(["secret-tool","clear","service",ao,"account",e]);return}if(process.platform==="win32")await Jo(["powershell","-NoProfile","-Command",`Remove-Item -ErrorAction SilentlyContinue ($env:LOCALAPPDATA + '\\devctl-cred-${rc(e)}.bin')`])}async function PE(e){if(process.platform==="darwin"){let t=await gu(["security","find-generic-password","-s",ao,"-a",e,"-w"]);return t.code===0&&t.stdout!==""?t.stdout:void 0}if(process.platform==="linux"){let t=await gu(["secret-tool","lookup","service",ao,"account",e]);return t.code===0&&t.stdout!==""?t.stdout:void 0}if(process.platform==="win32"){let t=`$p = $env:LOCALAPPDATA + '\\devctl-cred-${rc(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 gu(["powershell","-NoProfile","-Command",t]);return r.code===0&&r.stdout!==""?r.stdout:void 0}return}var Su=3000;async function Jo(e){try{return await bu({cmd:e,stdout:"ignore",stderr:"ignore",timeout:Su}).exited}catch{return 1}}async function RE(e,t){try{return await bu({cmd:e,stdin:new TextEncoder().encode(t),stdout:"ignore",stderr:"ignore",timeout:Su}).exited}catch{return 1}}async function gu(e){try{let t=bu({cmd:e,stdout:"pipe",stderr:"ignore",timeout:Su}),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 _E=3,EE=200;async function Qg(e,t={}){let r=t.attempts??_E,n=t.backoffMs??EE,o=t.retry??TE,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 AE(n*(s+1))}throw i}function TE(e){if(e instanceof Ve)return e.kind!==Y&&e.kind!==Yn;return!0}function AE(e){return new Promise((t)=>{setTimeout(t,e)})}var nc={now:()=>new Date,isoNow:()=>new Date().toISOString(),unixMs:()=>Date.now()};var ku=300000,OE=3000000,$E=3,FE=200,oc="https://www.googleapis.com/auth/cloud-platform",ev="https://www.googleapis.com/auth/userinfo.email",NE="https://oauth2.googleapis.com/token";function Zg(e,t,r,n){let o=n??"",i=`${e}|${t}|${r.join(",")}`;return o===""?i:`${i}|oauth:${o}`}function HE(e){let t;try{t=DE(e,"utf8")}catch{throw P(Y,`IAP credentials file not found: ${e}`)}let r;try{r=JSON.parse(t)}catch{throw P(Y,`IAP credentials file is not valid JSON: ${e}`)}if(typeof r!=="object"||r===null)throw P(Y,`IAP credentials file is malformed: ${e}`);let n=r,o=typeof n.refresh_token==="string"?n.refresh_token:"";if(o==="")throw P(Y,`IAP credentials file has no refresh_token: ${e}`);let i=typeof n.client_id==="string"?n.client_id:"";if(i==="")throw P(Y,`IAP credentials file has no client_id: ${e}`);return{clientId:i,clientSecret:typeof n.client_secret==="string"?n.client_secret:"",refreshToken:o}}function jE(e,t=process.env){let r=(e.client_id??"").trim();if(r==="")return;let n=(e.client_secret??"").trim(),{value:o,missing:i}=Rg(n,t),s=(e.credentials??"").trim();if(s!==""){let a=HE(s);if(a.clientId!==r)throw P(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 P(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 P(Y,a?`IAP client_secret env ${a} is empty`:`IAP client_id ${r} requires client_secret`)}return{clientId:r,clientSecret:o}}function ic(e,t=process.env){let r=(e.client_id??"").trim();if(r==="")return;return{clientId:r,resolve:()=>{let n=jE(e,t);if(!n)throw P(Y,`IAP client_id ${r} requires client_secret`);return n}}}function BE(e,t=ku,r=Date.now()){if(e.accessToken==="")return!1;return e.expiresAt.getTime()-r>=t}function UE(e,t=ku,r=Date.now()){if(e.accessToken==="")return!0;let n=e.expiresAt.getTime()-r;return n>0&&n<t}class co{cache=new Map;inflight=new Map;providers;thresholdMs;bus;store;clock;constructor(e,t,r,n,o=nc){this.thresholdMs=e>0?e:ku,this.providers=[...t],this.bus=r,this.store=n??tc(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 BE(e,this.thresholdMs,this.clock.unixMs())}expiresSoon(e){return UE(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=Zg(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=Zg(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 P(Rr,`no token provider accepts identity ${t}`);let a=P(Rr,"no token provider available");for(let c of s)try{let l=await Qg(()=>c.fetch(t,r,n,i),{attempts:$E,backoffMs:FE,retry:oT});return this.cache.set(e,l),GE(e,l),await this.store.set(e,KE(l)),this.bus?.publish(ye($o,"",{identity:l.identity,audience:l.audience})),l}catch(l){a=l instanceof Error?l:Error(String(l))}throw this.bus?.publish(ye(qn,"",{identity:t,audience:r,error:L(a)})),a}async loadStored(e){let t=await this.store.get(e);if(!t||t.accessToken==="")return;return qE(t)}async clearStore(){let e=await this.store.list();await Promise.all(e.map((t)=>this.store.delete(t.key)))}}function WE(e){let t=LE("sha256").update(e).digest("hex").slice(0,16);return IE(Cr(),`${t}.json`)}function GE(e,t){let r={identity:t.identity,audience:t.audience,expires_at:t.expiresAt.toISOString(),scopes:t.scopes};Pr(WE(e),`${JSON.stringify(r)}
|
|
2246
|
+
`)}function KE(e){return{identity:e.identity,audience:e.audience,scopes:e.scopes,accessToken:e.accessToken,tokenType:e.tokenType,expiresAt:e.expiresAt.toISOString()}}function qE(e){return{accessToken:e.accessToken,tokenType:e.tokenType||"Bearer",expiresAt:new Date(e.expiresAt),audience:e.audience,identity:e.identity,scopes:e.scopes}}function En(){return[YE(),zE(),VE()]}function VE(){return{name:"user",accepts:(e,t)=>!e.startsWith("sa:")&&t==="",fetch:async(e,t,r)=>{if(e.startsWith("sa:"))throw P(Rr,"user provider cannot mint service-account tokens");return tT(e,t,r)}}}function zE(){return{name:"service_account",accepts:(e,t)=>e.startsWith("sa:")&&t==="",fetch:async(e,t,r)=>{if(!e.startsWith("sa:"))throw P(Rr,"service-account provider cannot mint user tokens");return JE(e,t,r)}}}function YE(){return{name:"iap",accepts:(e,t)=>t!=="",fetch:async(e,t,r,n)=>{if(t==="")throw P(Rr,"IAP audience is required");if(e.startsWith("sa:"))return XE(e,t,r);return QE(e,t,r,n)}}}async function JE(e,t,r){let n=e.slice(3);try{zo();let i=await new Zi({scopes:r.length>0?r:[oc]}).getClient(),s=new ME({sourceClient:i,targetPrincipal:n,targetScopes:r.length>0?r:[oc],lifetime:3600}),a=await s.getAccessToken(),c=typeof a==="string"?a:a?.token;if(!c)throw P(Rr,"empty impersonated token");return{accessToken:c,tokenType:"Bearer",expiresAt:es(c,s),audience:t,identity:e,scopes:r}}catch(o){throw Yo(o)}}async function XE(e,t,r){let n=e.slice(3);try{zo();let i=await new Zi({scopes:[oc]}).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 P(Rr,"empty impersonated IAP id token");return{accessToken:c,tokenType:"Bearer",expiresAt:es(c),audience:t,identity:e,scopes:r}}catch(o){throw Yo(o)}}async function QE(e,t,r,n){try{if(zo(),n)return await ZE(e,t,r,n);let s=await(await new Zi({scopes:r.length>0?r:[ev]}).getIdTokenClient(t)).idTokenProvider.fetchIdToken(t);return{accessToken:s,tokenType:"Bearer",expiresAt:es(s),audience:t,identity:e,scopes:r}}catch(o){if(o instanceof Ve)throw o;throw Yo(o)}}async function ZE(e,t,r,n){let o=n.refreshToken;if(!o){if(o=(await new Zi({scopes:r.length>0?r:[ev]}).getClient()).credentials.refresh_token??void 0,!o)throw Te(yn,"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 i=await fetch(NE,{method:"POST",headers:{"content-type":"application/x-www-form-urlencoded"},body:eT(n,o,t)}),s=await i.text();if(!i.ok)throw Error(`IAP token exchange failed (${i.status}): ${s}`);let a;try{a=JSON.parse(s)}catch{throw P(Rr,"IAP token endpoint returned a non-JSON response")}let c=a.id_token;if(!c)throw P(Rr,"empty IAP id token");return{accessToken:c,tokenType:"Bearer",expiresAt:es(c),audience:t,identity:e,scopes:r}}function eT(e,t,r){return new URLSearchParams({grant_type:"refresh_token",client_id:e.clientId,client_secret:e.clientSecret,refresh_token:t,audience:r})}async function tT(e,t,r){try{zo();let o=await new Zi({scopes:r.length>0?r:[oc]}).getClient(),i=await o.getAccessToken();if(!i.token)throw P(Rr,"empty access token");return{accessToken:i.token,tokenType:"Bearer",expiresAt:rT(o,i.token),audience:t,identity:e,scopes:r}}catch(n){throw Yo(n)}}function es(e,t){if(t?.credentials?.expiry_date)return new Date(t.credentials.expiry_date);return nT(e)??new Date(Date.now()+OE)}function rT(e,t){return es(t,e)}function nT(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 oT(e){if(e instanceof Ve)return e.kind!==Y&&e.kind!==Yn;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();Tr();var rs=4000,iT=8000;function Xo(e){let t=e?.tokens,r=()=>(t??=new co(60000,En(),void 0,tc("file")),t);return{detectGoogle:so,hasCommand:hu,portAvailable:Uo,hasLocalAdc:Xi,adcQuotaProject:fu,mintToken:async(n,o,i)=>{await r().get(n,o,[],i)},probeServiceUsage:async(n,o)=>{let i=await ns(r().get("user","",[]),rs),s=`https://serviceusage.googleapis.com/v1/projects/${n}/services/${o}`,a=await fetch(s,{headers:{Authorization:`Bearer ${i.accessToken}`},signal:AbortSignal.timeout(rs)});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 sc=Xo();function ac(e=Xo()){return{run:(t,r,n)=>sT(t,e,r,n)}}async function sT(e,t=sc,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 f=await t.hasCommand(p),h=f&&await(t.containerRuntimeAvailable??sc.containerRuntimeAvailable)(p);c(h?{name:`${p} container runtime`,severity:"ok",message:`${p} daemon reachable`}:{name:`${p} container runtime`,severity:"error",message:f?`${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:Ji(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:Ji(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:Ji(e)?"error":"warn",message:"no Google project configured",hint:"set google.project_id in .devctl/config.yaml"});if(Ji(e)||Object.values(e.services).some((p)=>p.capabilities.includes("google")||p.identity.type!=="")){a("Live Google access");let p=!0,f=(h)=>{if(p)c(h)};try{await ns((async()=>{await cT(e,f,t,d.userEmail),await aT(e,f,t)})(),t.liveDeadlineMs??iT)}catch(h){f({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 f=p.command||p.name;if(a(`${p.name} installed`),await t.hasCommand(f))c({name:`${p.name} installed`,severity:"ok",message:`${f} found`});else c({name:`${p.name} installed`,severity:"error",message:`${f} 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=to(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 m={};for(let[p,f]of Object.entries(e.services))for(let h of f.ports){if(h.auto)continue;let v=`Port ${h.value}`;if(a(v),m[h.value])c({name:v,severity:"error",message:`configured on both ${m[h.value]} and ${p}`});else{m[h.value]=p;let b=n?.services?.[p],g=Boolean(f.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 rv(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 rv(p,e.proxy.listen.port,"proxy.listen.port"))}return i="Diagnostics complete",s(),o}async function aT(e,t,r){let n=e.google.project_id,o=r.hasLocalAdc??Xi;if(n===""||!o())return;let i=r.probeServiceUsage??sc.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)=>ns(i(n,c.service),rs)))).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 cT(e,t,r,n){let o=Br(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??Xi)())return;let c=r.mintToken??sc.mintToken,l=o.map(async(u)=>{try{await ns(c(`sa:${u}`,""),rs),t({name:`Impersonate ${u}`,severity:"ok",message:"token minted"})}catch(m){let p=(r.adcQuotaProject??fu)(),f=n||"the ADC principal",h=p===""?"unknown":p,v=tv(`Impersonate ${u}`,m,`grant roles/iam.serviceAccountTokenCreator to ${f} 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: ${f} \u2192 ${u}`,v.hint=`verify roles/iam.serviceAccountTokenCreator for ${f} on this service account; ADC quota project: ${h}`;t(v)}}),d=s.map(async(u)=>{let m=an(u.auth).kind===oo?`sa:${an(u.auth).serviceAccount}`:"user";try{let p=ic(u.auth);await ns(c(m,u.auth.audience,p),rs),t({name:`IAP ${u.name}`,severity:"ok",message:"id token minted"})}catch(p){t(tv(`IAP ${u.name}`,p,"check IAP OAuth client ID and ADC"))}});await Promise.all([...l,...d])}function tv(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 ns(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 rv(e,t,r){let n=await Jn(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 nv(e){let t=[`${tr()} 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(`
|
|
2247
2247
|
`)+`
|
|
2248
|
-
`}At();class
|
|
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(
|
|
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
|
|
2251
|
-
`)}async function
|
|
2252
|
-
`).find((i)=>i.startsWith("n"));if(r&&r.length>1)return r.slice(1).trim();let n=await
|
|
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
|
|
2254
|
-
`);i=a.pop()??"";for(let c of a)r(t,c.replace(/\r$/,""))}if(i!=="")r(t,i)}var
|
|
2248
|
+
`}At();class dv{start;constructor(e){this.start=e}execute(e){return this.start({...e,profile:e.profile?pr(e.profile):void 0,services:e.services?.map((t)=>Pu(t))})}}class uv{stop;constructor(e){this.stop=e}execute(e){return this.stop(e.map((t)=>Pu(t)))}}class pv{restart;constructor(e){this.restart=e}execute(e,t){return this.restart(e.map((r)=>Pu(r)),t)}}class mv{start;constructor(e){this.start=e}execute(e,t){return this.start.execute({profile:e,client_env:t})}}class fv{host;constructor(e){this.host=e}execute(){return this.host.startProxy()}}class hv{host;constructor(e){this.host=e}execute(){return this.host.stopProxy()}}class gv{host;constructor(e){this.host=e}execute(){return this.host.reload()}}class vv{host;constructor(e){this.host=e}execute(){return this.host.refreshIdentity({probeServiceAccounts:!0})}}class mc{runner;constructor(e){this.runner=e}execute(e,t,r){return this.runner.run(e,t,r)}}class fc{execute(e,t,r){return cc(e,t,r)}}class hc{execute(e,t){return lc(e,t)}}class yv{host;constructor(e){this.host=e}execute(){return this.host.snapshot()}}class gc{execute(e,t){return pc(e,t)}}function Pu(e){return ov(e)}function bv(e,t,r){let n=r??e,o=new dv((i)=>n.start(i));return{startService:o,stopService:new uv((i)=>n.stop(i)),restartService:new pv((i,s)=>n.restart(i,s)),startProfile:new mv(o),startProxy:new fv(e),stopProxy:new hv(e),reloadConfig:new gv(e),refreshIdentity:new vv(e),runDoctor:new mc(t),getServiceStatus:new yv(e),getStartupPlan:new fc,getShutdownPlan:new hc,resolveStart:new gc}}import{createConnection as kT}from"net";var{spawn:CT}=globalThis.Bun;import{readdirSync as uT}from"fs";import{join as xv,resolve as Ru,sep as _u}from"path";function pT(e){let t=xv(lr(),"state"),r;try{r=uT(t)}catch{return}let n=wv(Ru(e)),o;for(let i of r){let s=cd(xv(t,i,"state.json"));if(!s)continue;let a=wv(Ru(s.repo_root));if(!mT(a,n))continue;if(!o||a.length>o.length)o=a}return o}function vc(e,t,r=""){if(t!=="")return{repoRoot:Ru(t),source:"explicit"};let n=e===""?process.cwd():e;try{let{repoRoot:i}=eo(n,r);return{repoRoot:i,source:"config"}}catch{if(r!=="")return}let o=pT(n);return o?{repoRoot:o,source:"state-scan"}:void 0}function wv(e){let t=e.length>1&&e.endsWith(_u)?e.slice(0,-1):e;return process.platform==="win32"?t.toLowerCase():t}function mT(e,t){return t===e||t.startsWith(e.endsWith(_u)?e:`${e}${_u}`)}import{existsSync as kv,readFileSync as Cv}from"fs";import{isAbsolute as fT,join as Eu}from"path";import{parse as hT}from"dotenv";te();var ss=["process","profile","dotenv","generated","keychain","secret_manager","defaults","vars","runtime"],gT=/^projects\/[^/]+\/secrets\/[^/]+(?:\/versions\/[^/]+)?$/,vT=["process","defaults","vars","runtime"];function yT(){return{name:"dotenv",load:(e)=>{let t=Sv(e.repoRoot,e.profile);if(e.workDir!=="")Object.assign(t,Sv(e.workDir,e.profile));return t}}}function bT(e){let t=e?.environment.sources??[];if(t.length===0)return[...ss];let r=new Set([...vT,...t]),n=new Set(ss),o=t.filter((s)=>!n.has(s)),i=[];for(let s of ss){if(s==="defaults")i.push(...o);if(r.has(s))i.push(s)}return i}async function as(e,t){let r={},n=t.serviceCfg.working_dir;if(n!==""&&!fT(n))n=Eu(e,n);let o={repoRoot:e,profile:t.profile,service:t.service,serviceCfg:t.serviceCfg,workDir:n,cfg:t.cfg},i=xT(t),s=t.userEmail??"",a={process:t.includeProcess===!1?{}:t.clientEnv??Zo(),profile:is(t.profileEnv,t.cfg,i,s),dotenv:is(await yT().load(o),t.cfg,i,s),generated:{},keychain:t.sourceValues?.keychain??wT(o),secret_manager:t.sourceValues?.secret_manager??await ST(o,t.fetchSecret),defaults:is(t.serviceCfg.environment.defaults,t.cfg,i,s),vars:is(t.serviceCfg.environment.vars,t.cfg,i,s),runtime:t.runtime};for(let c of bT(t.cfg)){if(a[c]!==void 0){Object.assign(r,a[c]);continue}let l=t.pluginSources?.find((d)=>d.name===c);if(l)Object.assign(r,is(await l.load(o),t.cfg,i,s))}for(let c of t.serviceCfg.environment.required)if((r[c]??"").trim()==="")throw P(Y,`service ${t.service} missing required environment variable ${c}`);return r}function xT(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 is(e,t,r,n=""){if(!t||Object.keys(e).length===0)return e;return ou(e,t,r,n)}function wT(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=Eu(Cr(),"env",o);if(!kv(i))continue;try{r[o]=Cv(i,"utf8").replace(/\n$/,"")}catch(s){throw Se(Y,`unable to read keychain env ${o}`,s)}}return r}async function ST(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(!gT.test(s))throw P(Y,`environment.secrets.${i} is not a Secret Manager resource`);if(!t)throw P(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 Sv(e,t){let r={},n=[".env",".env.development",".env.local"];if(t!=="")n.push(`.env.${t}`);for(let o of n){let i=Eu(e,o);if(!kv(i))continue;try{let s=hT(Cv(i));Object.assign(r,s)}catch(s){throw Se(Y,`unable to read ${i}`,s)}}return r}function Zo(){let e={};for(let[t,r]of Object.entries(process.env))if(r!==void 0)e[t]=r;return e}function cs(e,t,r,n,o,i=""){let s={DEVCTL_SERVICE_NAME:e,DEVCTL_ENVIRONMENT:o,SERVICE_HOST:t};if(n!=="")s.DEVCTL_PROXY_URL=n;if(i!=="")s.DEVCTL_USER_EMAIL=i;if(r.http!==void 0)s.SERVICE_PORT=String(r.http);else{let a=Object.values(r)[0];if(a!==void 0)s.SERVICE_PORT=String(a)}for(let[a,c]of Object.entries(r))s[`${a.toUpperCase()}_PORT`]=String(c);return s}function ei(e){return{...e}}te();var PT=50,RT=8000,_T=15000,ET=200,TT=30000,Pv=86400000,AT=new Set(["logs","logs_page","logs_stats"]);function LT(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 ${ts}`}function DT(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 Tu(e,t){if(!e.compat.compatible&&!AT.has(t))throw Te(lt,LT(e.compat),"run `devctl down` to stop it, then start again")}class Rv{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(bh({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=TT){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 _v(e,t){let r=sa(e),n=Date.now()+t;return new Promise((o,i)=>{let s=()=>{let a=kT(r);a.once("connect",()=>{let c=new Rv(a);IT(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,PT)})};s()})}async function IT(e){let t;try{t=await e.call("ping",null,RT)}catch{return}let r=MT(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===ts,legacy:!1,daemonVersion:o,daemonProtocol:n}}function MT(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}async function po(e){try{return await _v(e,ET)}catch{return}}function OT(e,t,r,n){return r?[e,...n]:[e,t,...n]}async function Ev(e,t){let r=await po(e);if(r)return r;Df(e);let n=Ai(e),o=OT(process.execPath,process.argv[1]??"",Bun.isStandaloneExecutable===!0,["--config",t,"_supervisor","--repo",e]),i=CT({cmd:o,env:{...process.env},stdout:"ignore",stderr:Bun.file(n),stdin:"ignore",detached:!0});try{let s=await FT(e,_T,i.exited);return i.unref(),s}catch{throw await NT(i),Te(lt,"supervisor failed to start",`see ${n} for details`)}}var $T=1000;function FT(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`)))}),_v(e,t).then((a)=>s(()=>n(a)),(a)=>s(()=>o(a)))})}async function NT(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,$T)})])}class yc{cfg;client;previousPersisted;constructor(e){this.cfg=e}async start(e){return await this.call("start",{...e,client_env:Zo()})}async stop(e){await this.call("stop",{services:e})}async restart(e,t){await this.call("restart",{services:e,cascade:t===!0,client_env:Zo()})}async runTask(e){return await this.call("run_task",{name:e,client_env:Zo()},Pv)}async execService(e,t,r=!1){return await this.call("exec",{service:e,command:t,print_env:r,client_env:Zo()},Pv)}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 Se(lt,"supervisor is not running",Error("no client"));return Tu(this.client,e),this.client.call(e,t,r)}}function ls(e){let t=e&&DT(e.compat);if(t)process.stderr.write(`warning: ${t}
|
|
2251
|
+
`)}async function Tv(e,t,r=""){let n=vc(e,t,r);if(!n)throw Te(lt,"no devctl configuration found","run `devctl setup`, create a .devctl/config.yaml in the repository root, or pass --repo");let o=await po(n.repoRoot);return ls(o),{repoRoot:n.repoRoot,client:o}}async function Av(e,t,r,n){let o=n?.allowMissingConfig===!0?no(e,t):sn(e,t),i=new yc(o);if(!r)return i.client=await po(o.repoRoot),ls(i.client),i;return i.client=await Ev(o.repoRoot,o.configPath),ls(i.client),i}async function Lv(e){let t=new yc(Xn());return t.client=e,ls(t.client),t.cfg=await t.configSnapshot(),t}async function Dv(e,t){let r=vc(e,"",t),n=r?await po(r.repoRoot):void 0;if(!n)throw Te(lt,"supervisor is not running","run `devctl start` before `devctl attach`");return Lv(n)}async function Iv(e,t){let r=vc(e,"",t),n=r?await po(r.repoRoot):void 0;if(n)return Lv(n);let o=sn(e,t),i=new yc(o),s=gn(o.repoRoot);if(i.client=await Ev(o.repoRoot,o.configPath),ls(i.client),i.cfg=await i.configSnapshot(),s&&s.processes.length>0)i.previousPersisted=s;return i}import{realpathSync as jT}from"fs";var BT="https://api.github.com/repos/amr-m-abdelgawad/devctl/releases/latest";function UT(){let e=Bun.isStandaloneExecutable===!0,t=e?"":jv(process.argv[1]??"");return Fv({scriptPath:t,execPath:jv(process.execPath),standalone:e})}async function WT(e=fetch,t=UT()){try{let r=await e(BT,{headers:{Accept:"application/vnd.github+json"}});if(!r.ok)return Hv(t);let o=((await r.json()).tag_name??"").replace(/^v/,"");return{current:jt,latest:o,newer:o!==""&&Nv(o,jt)>0,hint:t.hint,kind:t.kind,command:t.command}}catch{return Hv(t)}}async function GT(e,t=Bv){return t(e)}async function Bv(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 Uv(e=fetch){return{check:()=>WT(e),apply:(t,r)=>r===!0?Bv(t,!0):GT(t)}}function Hv(e){return{current:jt,latest:"",newer:!1,hint:e.hint,kind:e.kind,command:e.command}}function jv(e){if(e==="")return"";try{return jT(e)}catch{return e}}function Wv(e){let t=e?.tokens??new co(60000,En(),void 0),r=e?.doctorHost??Xo({tokens:t}),n=Uv(),o={loadTuiConfig:Mo,saveTuiPreferences:Hf,resolveTuiOverridePath:ma,userTuiConfigPath:pa,listSessions:ph,loadSessionEvents:mh,loadPath:Yi,validateConfigText:zi,discover:eo,configDiff:Rn,openTui:Iv,findDaemon:Tv,tryDial:po,assertMethodAllowed:Tu,readPersistedState:gn,bootstrapLogPath:Ai,exportsDir:ia,resolveExportPath:dh,writeLogExport:Dd,openInFileManager:uh,freePort:_a,createStarterConfig:Ah,runSetup:(i,s,a)=>Lh(o,i,s,a),readTextFile:(i)=>qT(i,"utf8"),writeTextFile:(i,s)=>VT(i,s),fileExists:KT,load:sn,loadOrEmpty:no,validate:to,detectGoogle:so,loginGoogle:Gg,logoutGoogle:Kg,refreshUserToken:async(i="user")=>t.refresh(i,"",[]),runDoctor:new mc(e?.doctorRunner??ac(r)),getStartupPlan:new fc,getShutdownPlan:new hc,resolveStart:new gc,formatDoctor:nv,openController:Av,openAttach:Dv,checkUpdate:()=>n.check(),applyUpdate:(i,s)=>n.apply(i,s),formatUpdateStatus:$v};return o}import{existsSync as xL,unlinkSync as wL}from"fs";import{connect as zT}from"net";At();var YT=2000,JT=200,XT=300;function Gv(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)=>QT({...o,type:t},i.pid,i.ports,i.workDir,i.env,e)}:void 0}}}async function QT(e,t,r,n,o,i=[]){let s=e.timeout_seconds>0?e.timeout_seconds*1000:YT,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 ZT(e.url,s);if(a==="tcp")return t0(e0(e.address,r),s);if(a==="command")return r0(e.command.args,e.command.shell,n,o,s);if(a==="process"||a===""){if(t>0&&Zt(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 ZT(e,t){let r=new AbortController,n=setTimeout(()=>r.abort(),t);try{let o=await fetch(e,{signal:r.signal});if(o.status>=JT&&o.status<XT)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 e0(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 t0(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=zT({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 r0(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:oy}=globalThis.Bun;var{spawn:n0}=globalThis.Bun;async function Kv(e,t){if(e<=0)return;try{process.kill(-e,t);return}catch{try{process.kill(e,t)}catch{return}}}async function qv(e){if(!Zt(e))return;let t=await ds(["ps","-p",String(e),"-o","command="]),r=Date.now(),n=o0(await ds(["ps","-p",String(e),"-o","etime="])),o=await i0(e);return{pid:e,command:t.trim(),cwd:o,startTime:n===void 0?void 0:new Date(r-n).toISOString()}}function o0(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 i0(e){let r=(await ds(["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 ds(["pwdx",String(e)]),o=n.indexOf(":");if(o>=0)return n.slice(o+1).trim();return""}async function ds(e){try{let t=n0({cmd:e,stdout:"pipe",stderr:"ignore"}),r=t.stdout?await new Response(t.stdout).text():"";return await t.exited,r}catch{return""}}async function Vv(e){let t=new Map;if(e.length===0)return t;let r=await ds(["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 zv(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:Yv}=globalThis.Bun;var s0=1024,a0=3000;async function Jv(e,t){if(e<=0)return;try{await Yv({cmd:["taskkill","/PID",String(e),"/T","/F"],stdout:"ignore",stderr:"ignore"}).exited}catch{return}}async function Xv(e){let t=c0(ld(e),e);if(t)return t;if(!Zt(e))return;let r=await Au(["powershell","-NoProfile","-Command",`Get-CimInstance Win32_Process -Filter "ProcessId=${e}" | Select-Object CommandLine,ExecutablePath,CreationDate | ConvertTo-Json -Compress`]),n=d0(r);if(n&&n.command!=="")return{pid:e,command:n.command,cwd:n.cwd,startTime:n.startTime};let o=await Au(["powershell","-NoProfile","-Command",`Get-Process -Id ${e} -ErrorAction SilentlyContinue | Select-Object Path,StartTime | ConvertTo-Json -Compress`]),i=l0(o);if(!i&&!n)return;return{pid:e,command:n?.command||i?.command||"",cwd:n?.cwd||i?.cwd||"",startTime:n?.startTime||i?.startTime}}function c0(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 l0(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:wc(n),startTime:r.StartTime}}catch{let r=xc(t,"Path");if(r==="")return;return{command:r,cwd:wc(r)}}}function d0(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=wc(o);return{command:n,cwd:i,startTime:r.CreationDate||void 0}}catch{return{command:xc(t,"CommandLine")||xc(t,"ExecutablePath"),cwd:wc(xc(t,"ExecutablePath"))}}}async function Qv(e){let t=new Map;if(e.length===0)return t;let r=e.filter((o)=>o>0).join(",");if(r==="")return t;let n=await Au(["powershell","-NoProfile","-Command",`${r}.Split(',') | ForEach-Object { Get-Process -Id $_ -ErrorAction SilentlyContinue } | Select-Object Id,CPU,WorkingSet64 | ConvertTo-Json -Compress`]);return u0(n)}function u0(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)/s0;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 wc(e){if(e==="")return"";let t=e.replace(/\//g,"\\"),r=t.lastIndexOf("\\");return r>0?t.slice(0,r):""}function xc(e,t){let r=`${t}=`,n=e.split(/\r?\n/).find((o)=>o.startsWith(r));return n?n.slice(r.length).trim():""}async function Au(e){let t,r=setTimeout(()=>{try{t?.kill()}catch{}},a0);try{t=Yv({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:Lu}=globalThis.Bun;async function ey(e){await f0(e.runtime,e.containerName);let t=p0(e),r=await ti(e.runtime,t,e.env);if(r.code!==0||r.stdout.trim()==="")throw P(He,`failed to start ${e.containerName}: ${r.stderr.trim()||"container runtime returned no id"}`);let n=r.stdout.trim().split(/\s+/)[0]??"";return ny(e.runtime,e.containerName,n,e.onLine,e.onExit)}function p0(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 m0=new Set(["PATH","HOME","HOSTNAME","PWD","OLDPWD","SHLVL","_","TMPDIR"]);function ty(e){return Object.fromEntries(Object.entries(e).filter(([t])=>!m0.has(t)))}async function ry(e,t,r,n){let o=await ti(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 ny(e,t,i,r,n)}function ny(e,t,r,n,o){let i=!0,s=Lu({cmd:[e,"logs","--follow",r],stdout:"pipe",stderr:"pipe",stdin:"ignore"});Zv(s.stdout,"stdout",n),Zv(s.stderr,"stderr",n);let a=Lu({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),m=Number.isFinite(u)?u:d??1,p=m===0?void 0:Error(`container exited with code ${m}`);return await ti(e,["rm","--force",r],process.env),o?.(m,p),{code:m,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 ti(e,["stop","--time",String(d),r],process.env),await c}}}async function f0(e,t){let r=await ti(e,["inspect","--format","{{.State.Running}}",t],process.env);if(r.code===0&&r.stdout.trim()==="false")await ti(e,["rm","--force",t],process.env)}async function ti(e,t,r){try{let n=Lu({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 Se(He,`unable to run ${e}`,n)}}async function Zv(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 h0=1e4,g0=2000,v0=500;class Du{isRunning(e){let t=this.running.get(e);return t!==void 0&&us(t)}running=new Map;async start(e){let t=this.running.get(e.name);if(t&&us(t))return t;if(this.running.delete(e.name),e.args.length===0)throw P(He,"empty command");let r=e.shell?ay(e.args):e.args,n;try{n=oy({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 Se(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 Sc(n.stdout,"stdout",e.onLine),Sc(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 P(He,"empty command");let t;try{t=oy({cmd:e.shell?ay(e.args):e.args,cwd:e.workDir||void 0,env:e.env,stdout:"pipe",stderr:"pipe",stdin:"ignore"})}catch(a){throw Se(He,`failed to run ${e.name}`,a)}let r="",n="",o=(a,c)=>{if(a==="stdout")r+=`${c}
|
|
2255
2255
|
`;else n+=`${c}
|
|
2256
|
-
`;e.onLine?.(a,c)},i=[
|
|
2257
|
-
`);i=c.pop()??"";for(let l of c)r(t,l.replace(/\r$/,""))}}async function ny(e,t){return Promise.race([e.then(()=>!0),new Promise((r)=>{setTimeout(()=>r(!1),t)})])}function f0(e){return new Promise((t)=>{setTimeout(t,e)})}import{existsSync as m0,readFileSync as h0,writeFileSync as g0}from"fs";var sy={exists:(e)=>m0(e),readText:(e)=>h0(e,"utf8"),writeText:(e,t)=>g0(e,t)};ht();var Tu=3,v0=3,y0=10,as={shouldRestart(e){if(!e.enabled||e.policy===Sa)return!1;if(e.retryCount>=e.maxRetries)return!1;if(e.policy===Ca)return!0;return e.policy===ka&&e.exitCode!==0},fromService(e,t,r){return{policy:Ea(e.restart),enabled:e.restart.enabled!==!1,exitCode:r,retryCount:t,maxRetries:e.restart.max_retries>0?e.restart.max_retries:Tu}}},Au={shouldRestartUnhealthy(e,t=v0){return e>=t},shouldResetRestartBudget(e,t=y0){return e>=t}},yc={timeoutMs(e,t){let r=e.startup.timeout_seconds;return r>0?r*1000:t},waitForHealthy(e){return e.startup.wait_for_healthy}};At();te();var b0=2,x0=2000,w0=3,S0=10;class Lu{host;processes;clock;actions;generation=new Map;healthTimers=new Map;restarts=new Map;unhealthyStreak=new Map;healthyStreak=new Map;restartTimers=new Map;constructor(e,t,r,n){this.host=e;this.processes=t;this.clock=r;this.actions=n}forget(e){this.clearHealthWatch(e),this.clearRestartTimer(e),this.restarts.delete(e),this.bumpGeneration(e),this.unhealthyStreak.delete(e),this.healthyStreak.delete(e)}dispose(){this.clearAllRestartTimers();for(let e of this.generation.keys())this.bumpGeneration(e);for(let e of this.healthTimers.keys())this.clearHealthWatch(e)}onExit(e,t,r,n){if(!this.isCurrentGeneration(e,t))return;this.clearHealthWatch(e);let o=this.host().runtimes.get(e);if(o?.state===dt)return;if(o&&(o.state===_r||o.state===Et)){this.host().setState(e,Et,Tt,0,""),this.host().bus.publish(Se(ca,e,{code:r}));return}let i=this.host().cfg.services[e],s=n?.message??`exited with code ${r}`,a=this.restarts.get(e)??0;if((i?as.shouldRestart(as.fromService(i,a,r)):!1)&&i){this.host().setState(e,fr,Tt,0,s),this.bumpRestartCount(e,a+1),this.host().persistState(),this.armRestart(e,i,t,a,()=>{let l=this.host().serviceProfile.get(e)??this.host().profile,d=this.host().serviceProfileEnv.get(e)??this.host().profileEnv;this.actions.startOne(e,l,d,!1).catch((u)=>this.host().log(e,"ERROR",L(u)))});return}this.host().fail(e,E("process_start",s))}startHealth(e,t,r,n,o,i,s){let a=this.healthTimers.get(e);if(a)clearInterval(a);if(t.health.type===""){this.setHealth(e,Bt,"no health check");return}let c=t.health.interval_seconds>0?t.health.interval_seconds*1000:x0,l=Date.parse(this.host().runtimes.get(e)?.startTime??"")||this.clock.unixMs(),d=()=>{(t.container&&t.health.type.toLowerCase()==="process"?Promise.resolve(this.processes.isRunning(e)?{status:Bt,message:"container running"}:{status:Oe,message:"container not running"}):Promise.resolve().then(()=>this.host().healthCheckers.lookup(t.health.type)?.check(t.health,{pid:r,ports:n,workDir:o,env:i})??{status:Oe,message:`unknown health type ${t.health.type}`})).catch((f)=>({status:Oe,message:L(f)})).then((f)=>{if(!this.isCurrentGeneration(e,s))return;let p=this.host().runtimes.get(e);if(p&&ay(p.state))return;if(f.status===Oe&&this.clock.unixMs()-l<t.health.start_period_seconds*1000){this.host().logs.append({timestamp:this.clock.isoNow(),service:e,source:"health",level:"INFO",message:`health check still in start period: ${f.message}`,pid:r});return}this.setHealth(e,f.status,f.message),this.host().logs.append({timestamp:this.clock.isoNow(),service:e,source:"health",level:k0(f.status),message:`health ${f.status} ${f.message}`,pid:r}),this.maybeRestartUnhealthy(e,t,f.status,s)})};d(),this.healthTimers.set(e,setInterval(d,c))}bumpRestartCount(e,t){this.restarts.set(e,t);let r=this.host().runtimes.get(e);if(r)r.restarts=t}resetRestartCount(e){this.healthyStreak.set(e,0),this.bumpRestartCount(e,0)}bumpGeneration(e){let t=(this.generation.get(e)??0)+1;return this.generation.set(e,t),this.healthyStreak.set(e,0),t}isCurrentGeneration(e,t){return this.generation.get(e)===t}scheduleRestart(e,t,r){this.clearRestartTimer(e);let n=setTimeout(()=>{this.restartTimers.delete(e),r()},t);this.restartTimers.set(e,n)}armRestart(e,t,r,n,o){let i=t.restart.backoff_seconds>0?t.restart.backoff_seconds:b0;this.scheduleRestart(e,i*2**n*1000,()=>{if(this.isCurrentGeneration(e,r))o()})}clearRestartTimer(e){let t=this.restartTimers.get(e);if(t)clearTimeout(t),this.restartTimers.delete(e)}clearAllRestartTimers(){for(let e of this.restartTimers.values())clearTimeout(e);this.restartTimers.clear()}setHealth(e,t,r){let n=this.host().runtimes.get(e);if(!n||ay(n.state))return;if(n.health=t,n.state===je||n.state===pr||n.state===Br)n.state=t===Bt?pr:t===Oe?Br:n.state;this.host().bus.publish(Se($m,e,{health:t,message:r}))}maybeRestartUnhealthy(e,t,r,n){if(r!==Oe){if(this.unhealthyStreak.set(e,0),r===Bt)this.maybeForgiveRestarts(e,t);return}this.healthyStreak.set(e,0);let o=t.restart.policy||(t.restart.enabled?"on_failure":"never");if(o!=="on_failure"&&o!=="always")return;if(this.restartTimers.has(e))return;let i=(this.unhealthyStreak.get(e)??0)+1;this.unhealthyStreak.set(e,i);let s=t.health.unhealthy_threshold>0?t.health.unhealthy_threshold:w0;if(!Au.shouldRestartUnhealthy(i,s))return;this.unhealthyStreak.set(e,0);let a=this.restarts.get(e)??0,c=t.restart.max_retries>0?t.restart.max_retries:Tu;if(a>=c){this.host().log(e,"WARN",`unhealthy after ${i} consecutive checks but the restart limit (${c}) has already been reached; not restarting`);return}this.bumpRestartCount(e,a+1),this.host().log(e,"WARN",`restarting after ${i} consecutive unhealthy checks (attempt ${a+1}/${c})`),this.armRestart(e,t,n,a,()=>{this.actions.restart([e],{auto:!0}).catch((l)=>this.host().log(e,"ERROR",L(l)))})}maybeForgiveRestarts(e,t){let r=(this.healthyStreak.get(e)??0)+1;this.healthyStreak.set(e,r);let n=t.health.healthy_reset_threshold>0?t.health.healthy_reset_threshold:S0;if(Au.shouldResetRestartBudget(r,n)&&(this.restarts.get(e)??0)>0)this.host().log(e,"INFO",`restart count reset after ${r} consecutive healthy checks`),this.bumpRestartCount(e,0)}clearHealthWatch(e){let t=this.healthTimers.get(e);if(t)clearInterval(t),this.healthTimers.delete(e)}}function ay(e){return e===fr||e===_r||e===Et||e===dt}function k0(e){return e===Oe?"WARN":e===Bt?"INFO":"DEBUG"}ht();te();jr();te();At();var Du=[_r,dt,fr,je,Et],C0={[ev]:[Et,sn,je,dt,fr,_r],[Et]:[sn,je,dt],[sn]:[je,dt,Et,_r],[je]:Du,[pr]:Du,[Br]:Du,[_r]:[Et,dt],[dt]:[sn,Et,je],[fr]:[sn,je,dt,Et]};function cs(e,t){if(e===t)return!0;return(C0[e]??[]).includes(t)}function cy(e,t){if(!cs(e,t))throw E(Y,`illegal service lifecycle transition ${e} \u2192 ${t}`);return t}At();var P0=100,ly=30000;class Iu{processes;clock;session;health;constructor(e,t){this.processes=e;this.clock=t;this.health=new Lu(()=>this.host(),e,t,{startOne:(r,n,o,i)=>this.startOne(r,n,o,i),restart:(r,n)=>this.restart(r,n)})}bind(e){this.session=e}host(){if(!this.session)throw E(He,"service orchestrator is not bound");return this.session}planStart(e,t,r){return nc(e,t,r)}planStop(e,t,r=!1){return r?ic(e,t):oc(e,t)}applyLifecycle(e,t){return cy(e,t)}isLegalLifecycle(e,t){return cs(e,t)}async start(e){let t=this.host();if(e.detach===!0)t.detached=!0;let r=ac(t.cfg,{services:e.services,profile:e.profile?ur(e.profile):void 0,activeProfile:ur(t.profile)});if(r.profile)t.profile=r.profile,t.profileEnv=r.env;if(e.client_env)for(let a of r.services)t.clientEnv.set(a,e.client_env);if(e.auto!==!0)for(let a of r.services)this.health.resetRestartCount(a);let n=this.planStart(t.cfg,r.services,r.profile),o=await t.detectGoogle(t.cfg.google.project_id);n.blockers=Dg(t.cfg,n.waves.flat(),o.adcAvailable);let i=new Set(n.blockers.map((a)=>a.name));for(let a of n.blockers)await t.fail(a.name,E(He,a.message));if(t.cfg.proxy.enabled&&!t.proxySuppressed)await t.startProxy().catch((a)=>t.log("devctl","ERROR",L(a)));let s=[];for(let a of n.waves.flat()){if(i.has(a)||await t.claimIfAlreadyUp(a))continue;s.push(a)}for(let a of s)if(e.profile||!t.serviceProfile.has(a))t.serviceProfile.set(a,r.profile);if(s.length>0)await t.assignPendingPorts(s);for(let a of n.waves){let c=a.filter((l)=>s.includes(l));if(c.length>0){let l=await Promise.allSettled(c.map((u)=>this.startOne(u,r.profile,r.env,e.auto!==!0))),d=!1;for(let u of l)if(u.status==="rejected")d=!0,t.log("devctl","ERROR",L(u.reason));if(d)throw E(He,"one or more services failed to start")}try{await this.awaitWaveHealth(a,n.waves.flat())}catch(l){throw t.log("devctl","ERROR",L(l)),l}}return t.persistState(),n}async stop(e){let t=this.host(),r=e;if(r.length===0)r=[...t.runtimes.entries()].filter(([,s])=>s.state!==Et).map(([s])=>s);if(r.length===0)return;let n=r.filter((s)=>!t.cfg.services[s]&&t.runtimes.has(s)),o=r.filter((s)=>!t.cfg.services[s]&&!t.runtimes.has(s));if(o.length>0)throw E(Ho,`unknown service "${o[0]}"`);let i=r.filter((s)=>t.cfg.services[s]!==void 0);if(i.length>0)await this.runStopPlan(this.planStop(t.cfg,i),{resetRestartCounts:!0});if(n.length>0){await this.runStopPlan({profile:t.profile,steps:[],waves:n.map((s)=>[s])},{resetRestartCounts:!0});for(let s of n)t.forgetService(s)}}async restart(e,t){let r=this.host(),n=t?.cascade===!0,o=n?co(r.cfg,e):e,i=n?this.planStop(r.cfg,e):this.planStop(r.cfg,e,!0),s=t?.auto!==!0;await this.runStopPlan(i,{resetRestartCounts:s}),await this.start({services:o,client_env:t?.clientEnv,auto:t?.auto})}async runStopPlan(e,t){let r=this.host();for(let i of e.waves.flat())if(this.health.clearRestartTimer(i),this.health.bumpGeneration(i),t?.resetRestartCounts)this.health.resetRestartCount(i);let n=wn(r.cfg.shutdown)*1000,o=[];for(let i of e.waves){let s=await Promise.allSettled(i.map(async(a)=>{r.setState(a,_r,Tt,0,""),this.health.clearHealthWatch(a);try{await this.processes.stop(a,n)}catch(c){throw r.setState(a,dt,Tt,0,L(c)),r.bus.publish(Se(la,a,{error:L(c)})),c}try{await r.releasePorts(a)}catch(c){r.log(a,"WARN",L(c))}r.setState(a,Et,Tt,0,""),r.bus.publish(Se(ca,a,{}))}));for(let a=0;a<s.length;a+=1){let c=s[a];if(c?.status==="rejected"){let l=i[a]??"";o.push(l),r.log(l||"devctl","ERROR",L(c.reason))}}}if(r.persistState(),o.length>0)throw E(He,`failed to stop: ${o.join(", ")}`)}serviceIsActive(e){if(this.processes.isRunning(e))return!0;let t=this.host().runtimes.get(e);if(!t)return!1;if(t.state===fr||t.state===_r||t.state===Et||t.state===dt)return!1;if(t.health===Bt||t.state===pr)return!0;return t.state===sn||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,sn,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:wn(o.cfg.shutdown)*1000,captureStdout:xh(i),captureStderr:wh(i),onLine:m,onExit:h})}catch(p){if(this.health.isCurrentGeneration(e,s))await o.fail(e,p);throw p}if(!this.health.isCurrentGeneration(e,s)){if(this.processes.get(e)===f)await this.processes.stop(e,wn(o.cfg.shutdown)*1000).catch(()=>{});return}if(o.processMeta.set(e,{command:[...i.command.args],cwd:u,startTime:f.startTime}),o.setState(e,je,Tt,f.pid,""),o.bus.publish(Se(Mm,e,{pid:f.pid})),n)try{await this.runTransient(`${e}:post_start`,i.hooks.post_start,i.shell,u,d)}catch(p){throw await o.fail(e,p),p}if(!this.health.isCurrentGeneration(e,s))return;if(this.health.startHealth(e,i,f.pid,l,u,d,s),o.persistState(),o.clearRestartRequired([e]),yc.waitForHealthy(i)){let p=yc.timeoutMs(i,ly);try{await this.waitHealthy(e,p)}catch(m){throw await o.fail(e,m),m}}}async awaitWaveHealth(e,t){for(let r of e){let n=this.host().cfg.services[r],o=t.some((s)=>(this.host().cfg.services[s]?.dependencies??[]).some((a)=>zt(a)===r&&vn(a)==="service_healthy"));if(!n||n.health.type===""||!o)continue;let i=yc.timeoutMs(n,ly);try{await this.waitHealthy(r,i)}catch(s){throw await this.host().fail(r,s),s}}}async waitHealthy(e,t){let r=this.clock.unixMs()+t;while(this.clock.unixMs()<r){let n=this.host().runtimes.get(e);if(n?.health===Bt)return;if(n?.state===dt)throw E(Td,`service ${e} failed while waiting for healthy`);await _0(P0)}throw E(Td,`service ${e} did not become healthy in time`)}async runTransient(e,t,r,n,o){if(bn(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:wn(this.host().cfg.shutdown)*1000,onLine:(s,a)=>this.host().logs.append({timestamp:this.clock.isoNow(),service:e,source:s,stream:s,level:"",message:a,pid:0})});if(i.code!==0)throw E(He,`${e} exited with code ${i.code}`);return i}}function _0(e){return new Promise((t)=>setTimeout(t,e))}te();function ls(e){return async(t)=>{let r=t.includes("/versions/")?t:`${t}/versions/latest`,n=await e(),o=await fetch(`https://secretmanager.googleapis.com/v1/${r}:access`,{headers:{Authorization:`Bearer ${n}`}});if(!o.ok)throw E(Y,`secret manager request failed for ${t}: HTTP ${o.status}`);let i=await o.json();if(!i.payload?.data)throw E(Y,`secret manager response for ${t} had no payload`);return Buffer.from(i.payload.data,"base64").toString("utf8")}}At();te();async function bc(e,t,r,n){let o="";if(e.proxy?.isRunning())o=`http://${e.proxy.address()}`;else if(e.cfg.proxy.enabled)o=`http://${xn(e.cfg.proxy.listen)}`;let i=rs(t,"127.0.0.1",n,o,e.cfg.project.name);if(i.DEVCTL_INTERNAL_TOKEN=e.internalTok,e.cfg.proxy.token_endpoint.enabled)i.DEVCTL_TOKEN_URL=e.boundTokenURL||`http://127.0.0.1:${e.tokenEP?.listenPort()||e.cfg.proxy.token_endpoint.port}/token`;try{let s=await ts(e.cfg.repoRoot,{service:t,profile:e.serviceProfile.get(t)??e.profile,serviceCfg:r,profileEnv:e.serviceProfileEnv.get(t)??e.profileEnv,assignedPorts:n,runtime:i,cfg:e.cfg,fetchSecret:ls(async()=>(await e.tokens.get("user","",[])).accessToken),pluginSources:e.registry?.environmentSources});return Qo(s)}catch(s){return e.log(t,"WARN",`could not fully reconstruct environment for adopted service's health check (${L(s)}); using a baseline environment`),Qo(i)}}function dy(e,t,r,n,o,i){if(e.procs.get(t)&&e.processAliveFn(e.procs.get(t)?.pid??0))return;if(!e.processAliveFn(r)||r===process.pid)return;let s=e.orchestrator.health.bumpGeneration(t);try{e.procs.adopt({name:t,pid:r,args:n,workDir:o,startTime:i,onExit:(a,c)=>{e.orchestrator.health.onExit(t,s,a,c)}})}catch(a){let c=a instanceof Error?a.message:String(a);e.log(t,"WARN",`adopt pid ${r} failed (${c}); tracking leftover in snapshot only`)}return e.processMeta.set(t,{command:n,cwd:o,startTime:i}),e.persistState(),s}async function uy(e,t){let r=e.cfg.services[t];if(e.orchestrator.serviceIsActive(t)){if(r&&!e.ports.has(t)){let a=await xa(r);if(a)e.ports.set(t,a)}return!0}if(!r)return!1;if(r.container){let a=e.orchestrator.health.bumpGeneration(t),c=r.container.runtime==="podman"?"podman":"docker",l=e.serviceWorkDir(r),d=await e.procs.adoptContainer({name:t,runtime:c,containerName:`devctl-${ar(e.cfg.repoRoot)}-${t.replace(/[^a-zA-Z0-9_.-]/g,"-")}`,workDir:l,onLine:(p,m)=>e.logs.append({timestamp:e.clock.isoNow(),service:t,source:p,stream:p,level:"",message:m,pid:0}),onExit:(p,m)=>e.orchestrator.health.onExit(t,a,p,m)});if(!d)return!1;let u=e.ports.get(t)??Object.fromEntries(r.ports.filter((p)=>!p.auto).map((p)=>[p.name,p.value]));e.ports.set(t,u),e.processMeta.set(t,{command:[...r.command.args],cwd:l,startTime:d.startTime}),xc(e,t,wc(e,mn(e.cfg.repoRoot)?.processes.find((p)=>p.name===t))),e.setState(t,je,Tt,0,"");let f=await bc(e,t,r,u);return e.orchestrator.health.startHealth(t,r,0,u,l,f,a),e.persistState(),e.log(t,"INFO",`claimed running ${c} container ${d.container?.id??""}`),!0}let n=await xa(r);if(!n)return!1;let o=Object.values(n)[0],s=(o===void 0?void 0:await zn(o))?.pid??0;if(s>0&&s!==process.pid){let a=mn(e.cfg.repoRoot)?.processes.find((d)=>d.name===t&&d.pid===s);if(!a)return e.log(t,"WARN",`port ${o} is held by pid ${s} with no persisted record for ${t}; not adopting`),!1;let c=await e.inspectProcessFn(s);if(c!==void 0&&c.command!==""&&ss({args:[...r.command.args],workDir:e.serviceWorkDir(r),startTime:new Date(a.startTime)},c)){e.ports.set(t,n);let d=dy(e,t,s,[...r.command.args],e.serviceWorkDir(r),new Date(a.startTime))??e.orchestrator.health.bumpGeneration(t);xc(e,t,wc(e,a)),e.setState(t,je,Tt,s,""),e.log(t,"INFO",`already listening on ${Object.values(n).join(", ")}; not starting again`);let u=e.serviceWorkDir(r),f=await bc(e,t,r,n);return e.orchestrator.health.startHealth(t,r,s,n,u,f,d),!0}e.log(t,"WARN",`port ${o} is in use by an unrelated process (pid ${s}); not adopting`)}return!1}async function py(e){let t=mn(e.cfg.repoRoot);if(!t)return;R0(e,t.profile);let r=[];for(let[n,o]of Object.entries(e.cfg.services)){if(!o.container)continue;let i=t.processes.find((u)=>u.name===n),s=e.orchestrator.health.bumpGeneration(n),a=o.container.runtime==="podman"?"podman":"docker",c=await e.procs.adoptContainer({name:n,runtime:a,containerName:`devctl-${ar(e.cfg.repoRoot)}-${n.replace(/[^a-zA-Z0-9_.-]/g,"-")}`,workDir:e.serviceWorkDir(o),onLine:(u,f)=>e.logs.append({timestamp:e.clock.isoNow(),service:n,source:u,stream:u,level:"",message:f,pid:0}),onExit:(u,f)=>e.orchestrator.health.onExit(n,s,u,f)});if(!c)continue;let l=i?.ports??Object.fromEntries(o.ports.filter((u)=>!u.auto).map((u)=>[u.name,u.value]));e.ports.set(n,l),e.processMeta.set(n,{command:[...o.command.args],cwd:e.serviceWorkDir(o),startTime:i?.startTime?new Date(i.startTime):c.startTime}),xc(e,n,wc(e,i)),e.setState(n,je,Tt,0,"");let d=await bc(e,n,o,l);e.orchestrator.health.startHealth(n,o,0,l,e.serviceWorkDir(o),d,s),e.log(n,"INFO",`adopted ${a} container ${c.container?.id??""}`),r.push(n)}for(let n of t.processes){if(!e.cfg.services[n.name]||e.cfg.services[n.name]?.container||n.pid<=0||n.pid===process.pid||!e.processAliveFn(n.pid))continue;let o=await e.inspectProcessFn(n.pid);if(!(o!==void 0&&o.command!==""&&ss({args:n.command,workDir:n.cwd,startTime:n.startTime?new Date(n.startTime):void 0},o))){if(Object.values(n.ports).length>0&&await xa({ports:Object.entries(n.ports).map(([l,d])=>({name:l,value:d,auto:!1}))})!==void 0)e.log(n.name,"WARN",`pid ${n.pid} does not match stored command; leftover listener not adopted`);else e.log(n.name,"WARN",`pid ${n.pid} is a different process; not adopting`);continue}let s=dy(e,n.name,n.pid,n.command,n.cwd,new Date(n.startTime||e.clock.unixMs()))??e.orchestrator.health.bumpGeneration(n.name);if(Object.keys(n.ports).length>0)e.ports.set(n.name,n.ports);xc(e,n.name,wc(e,n)),e.setState(n.name,je,Tt,n.pid,"");let a=e.cfg.services[n.name];if(a){let c=n.cwd||e.serviceWorkDir(a),l=await bc(e,n.name,a,n.ports);e.orchestrator.health.startHealth(n.name,a,n.pid,n.ports,c,l,s)}e.log(n.name,"INFO","adopted leftover process; stdout/stderr from before adopt are not captured"),r.push(n.name)}if(r.length>0)e.bus.publish(Se(Hm,"",{services:r,session_id:t.session_id})),e.log("devctl","INFO",`recovered session processes: ${r.join(", ")}`)}function R0(e,t){e.profile=t||e.profile,e.profileEnv=lo(e.cfg,e.profile)}function xc(e,t,r){e.serviceProfile.set(t,r),e.serviceProfileEnv.set(t,lo(e.cfg,r))}function wc(e,t){return t?.profile??e.profile}import{statSync as U0,watch as W0}from"fs";import{isAbsolute as G0,join as K0,resolve as q0}from"path";function fy(e,t){return t}function Mu(e,t){let r={},n=new Set,o=new Set([...Object.keys(e.services),...Object.keys(t.services)]);for(let s of o){let a=e.services[s],c=t.services[s],l=[];if(!a||!c)l.push("presence"),n.add(s);else{if(a.command.args.join("\x00")!==c.command.args.join("\x00")||a.command.shell!==c.command.shell||a.shell!==c.shell)l.push("command"),n.add(s);if(a.working_dir!==c.working_dir)l.push("working_dir"),n.add(s);if(JSON.stringify(a.environment)!==JSON.stringify(c.environment))l.push("environment"),n.add(s);if(JSON.stringify(a.ports)!==JSON.stringify(c.ports))l.push("ports"),n.add(s);if(JSON.stringify(a.identity)!==JSON.stringify(c.identity))l.push("identity"),n.add(s);if(JSON.stringify(a.health)!==JSON.stringify(c.health))l.push("health"),n.add(s);if(JSON.stringify(a.restart)!==JSON.stringify(c.restart))l.push("restart"),n.add(s);if(JSON.stringify(a.startup)!==JSON.stringify(c.startup))l.push("startup"),n.add(s);if(JSON.stringify(a.logs)!==JSON.stringify(c.logs))l.push("logs"),n.add(s);if(JSON.stringify(a.container)!==JSON.stringify(c.container))l.push("container"),n.add(s);if(JSON.stringify(a.watch)!==JSON.stringify(c.watch))l.push("watch"),n.add(s)}if(l.length>0)r[s]=l}let i=[];if(JSON.stringify(e.logs)!==JSON.stringify(t.logs))i.push("logs");if(JSON.stringify(e.auth)!==JSON.stringify(t.auth))i.push("auth");return{restart_required:[...n].sort(),changes:r,supervisor_restart_required:i}}At();te();import{pathToFileURL as F0}from"url";import{isAbsolute as N0,resolve as H0}from"path";import{createServer as yy,request as E0}from"http";import{request as T0}from"https";import{randomBytes as my}from"crypto";import{Readable as A0}from"stream";te();jr();var ds="x-devctl-request-id",L0="x-devctl-internal-token",hy=100,Ou=new Set(["connection","keep-alive","proxy-authenticate","proxy-authorization","te","trailers","transfer-encoding","upgrade"]),D0=new Set(["gzip","deflate","br"]);class $u{cfg;tokens;logs;bus;detector;resolvePort;middleware;server;running=!1;addr="";upgradedSockets=new Set;recentRequests=[];constructor(e,t,r,n,o,i=[],s){this.cfg=e,this.tokens=t,this.logs=r,this.bus=n,this.detector=o,this.middleware=i,this.resolvePort=s}upstreamBase(e){let t=e.upstream.service??"";if(t==="")return e.upstream.url;if(!this.resolvePort)throw E(Nr,`route ${e.name} addresses service ${t} but this proxy has no port resolver configured`);let r=this.resolvePort(t,e.upstream.port??"");if(r===void 0)throw E(Nr,`upstream service ${t} is not running`);return`http://127.0.0.1:${r}`}address(){return this.addr||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>hy)this.recentRequests.splice(0,this.recentRequests.length-hy)}start(){let e=this.cfg.listen.host||"127.0.0.1";if(!Cn(e))return Promise.reject(E(Nr,`refusing to bind proxy to ${e}`));if(this.cfg.listen.port===0)return Promise.reject(E(Nr,"proxy.listen.port is required when the proxy is enabled"));let t=this.cfg.listen.port;return new Promise((r,n)=>{this.server=yy((o,i)=>{this.serve(o,i)}),this.server.on("upgrade",(o,i,s)=>{this.serveUpgrade(o,i,s)}),this.server.on("error",(o)=>n(Pe(Nr,`unable to listen on ${e}:${t}`,o))),this.server.listen(t,e,()=>{this.running=!0,this.addr=`${e}:${t}`,this.bus?.publish(Se(Fm,"",{address:this.addr})),r()})})}stop(){let e=this.server;if(!e)return Promise.resolve();for(let t of this.upgradedSockets)t.destroy();return this.upgradedSockets.clear(),new Promise((t)=>{e.close(()=>{this.running=!1,this.bus?.publish(Se(Nm,"",{})),t()})})}async serveUpgrade(e,t,r){let n=Date.now(),o=e.headers[ds]?.toString()||my(8).toString("hex"),i=e.method??"GET",s=e.url??"/",a=this.detector?this.detector.redactText(s):s,c=vy(this.cfg.routes,e);if(!c){t.end(`HTTP/1.1 404 Not Found\r
|
|
2256
|
+
`;e.onLine?.(a,c)},i=[Sc(t.stdout,"stdout",o),Sc(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&&us(t))return t;let r=await ey({...e,env:ty(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&&us(t))return t;let r=await ry(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&&us(t))return t;if(!Zt(e.pid))throw P(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=b0(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:h0;if(r.container){await r.container.stop(n),this.running.delete(e);return}if(await iy(r.pid,"SIGTERM"),!await ly(r.done,n)){if(await iy(r.pid,"SIGKILL"),!await ly(r.done,g0))throw P(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 us(e){if(e.container)return e.container.running();if(e.proc)return e.proc.exitCode===null&&!e.proc.killed;return Zt(e.pid)}async function iy(e,t){if(process.platform==="win32"){await Jv(e,t);return}await Kv(e,t)}async function dy(e){if(process.platform==="win32")return Xv(e);return qv(e)}async function uy(e){if(process.platform==="win32")return Qv(e);return Vv(e)}var y0=2000;function ps(e,t){if(e.workDir!==""&&t.cwd!==""&&cy(e.workDir)!==cy(t.cwd))return!1;if(!zv(e.args,t.command))return!1;let r=sy(e.startTime),n=sy(t.startTime);if(r!==void 0&&n!==void 0)return Math.abs(r-n)<=y0;return!0}function sy(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 ay(e){if(process.platform==="win32")return["cmd.exe","/c",e.join(" ")];return["/bin/sh","-c",e.join(" ")]}function cy(e){return e.replace(/\\/g,"/").replace(/\/+$/,"")}async function b0(e){while(Zt(e))await x0(v0);return 0}async function Sc(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 ly(e,t){return Promise.race([e.then(()=>!0),new Promise((r)=>{setTimeout(()=>r(!1),t)})])}function x0(e){return new Promise((t)=>{setTimeout(t,e)})}import{existsSync as w0,readFileSync as S0,writeFileSync as k0}from"fs";var py={exists:(e)=>w0(e),readText:(e)=>S0(e,"utf8"),writeText:(e,t)=>k0(e,t)};dt();var Iu=3,C0=3,P0=10,ms={shouldRestart(e){if(!e.enabled||e.policy===Ea)return!1;if(e.retryCount>=e.maxRetries)return!1;if(e.policy===Aa)return!0;return e.policy===Ta&&e.exitCode!==0},fromService(e,t,r){return{policy:Ma(e.restart),enabled:e.restart.enabled!==!1,exitCode:r,retryCount:t,maxRetries:e.restart.max_retries>0?e.restart.max_retries:Iu}}},Mu={shouldRestartUnhealthy(e,t=C0){return e>=t},shouldResetRestartBudget(e,t=P0){return e>=t}},kc={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 R0=2,_0=2000,E0=3,T0=10;class Ou{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===ut)return;if(o&&(o.state===Ar||o.state===Et)){this.host().setState(e,Et,Tt,0,""),this.host().bus.publish(ye(fa,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?ms.shouldRestart(ms.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,P("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:_0,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((m)=>({status:Oe,message:L(m)})).then((m)=>{if(!this.isCurrentGeneration(e,s))return;let p=this.host().runtimes.get(e);if(p&&my(p.state))return;if(m.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: ${m.message}`,pid:r});return}this.setHealth(e,m.status,m.message),this.host().logs.append({timestamp:this.clock.isoNow(),service:e,source:"health",level:A0(m.status),message:`health ${m.status} ${m.message}`,pid:r}),this.maybeRestartUnhealthy(e,t,m.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:R0;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||my(n.state))return;if(n.health=t,n.state===je||n.state===mr||n.state===Ur)n.state=t===Bt?mr:t===Oe?Ur:n.state;this.host().bus.publish(ye(Uf,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:E0;if(!Mu.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:Iu;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:T0;if(Mu.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 my(e){return e===fr||e===Ar||e===Et||e===ut}function A0(e){return e===Oe?"WARN":e===Bt?"INFO":"DEBUG"}dt();te();Tr();te();At();var $u=[Ar,ut,fr,je,Et],L0={[sv]:[Et,cn,je,ut,fr,Ar],[Et]:[cn,je,ut],[cn]:[je,ut,Et,Ar],[je]:$u,[mr]:$u,[Ur]:$u,[Ar]:[Et,ut],[ut]:[cn,Et,je],[fr]:[cn,je,ut,Et]};function fs(e,t){if(e===t)return!0;return(L0[e]??[]).includes(t)}function fy(e,t){if(!fs(e,t))throw P(Y,`illegal service lifecycle transition ${e} \u2192 ${t}`);return t}At();var D0=100,hy=30000;class Fu{processes;clock;session;health;constructor(e,t){this.processes=e;this.clock=t;this.health=new Ou(()=>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 P(He,"service orchestrator is not bound");return this.session}planStart(e,t,r){return cc(e,t,r)}planStop(e,t,r=!1){return r?dc(e,t):lc(e,t)}applyLifecycle(e,t){return fy(e,t)}isLegalLifecycle(e,t){return fs(e,t)}async start(e){let t=this.host();if(e.detach===!0)t.detached=!0;let r=pc(t.cfg,{services:e.services,profile:e.profile?pr(e.profile):void 0,activeProfile:pr(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=Ng(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,P(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 P(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 P(Bo,`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=kn(r.cfg.shutdown)*1000,o=[];for(let i of e.waves){let s=await Promise.allSettled(i.map(async(a)=>{r.setState(a,Ar,Tt,0,""),this.health.clearHealthWatch(a);try{await this.processes.stop(a,n)}catch(c){throw r.setState(a,ut,Tt,0,L(c)),r.bus.publish(ye(ha,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(ye(fa,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 P(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===Ar||t.state===Et||t.state===ut)return!1;if(t.health===Bt||t.state===mr)return!0;return t.state===cn||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 P(lt,`unknown service ${e}`);o.setState(e,cn,Tt,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="",m;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 f=(v,b)=>{o.logs.append({timestamp:this.clock.isoNow(),service:e,source:v,stream:v,level:"",message:b,pid:m.pid})},h=(v,b)=>this.health.onExit(e,s,v,b);m=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:f,onExit:h}):await this.processes.start({name:e,args:[...i.command.args],shell:i.shell||i.command.shell,workDir:u,env:d,graceMs:kn(o.cfg.shutdown)*1000,captureStdout:Rh(i),captureStderr:_h(i),onLine:f,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)===m)await this.processes.stop(e,kn(o.cfg.shutdown)*1000).catch(()=>{});return}if(o.processMeta.set(e,{command:[...i.command.args],cwd:u,startTime:m.startTime}),o.setState(e,je,Tt,m.pid,""),o.bus.publish(ye(jf,e,{pid:m.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,m.pid,l,u,d,s),o.persistState(),o.clearRestartRequired([e]),kc.waitForHealthy(i)){let p=kc.timeoutMs(i,hy);try{await this.waitHealthy(e,p)}catch(f){throw await o.fail(e,f),f}}}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)=>Yt(a)===r&&bn(a)==="service_healthy"));if(!n||n.health.type===""||!o)continue;let i=kc.timeoutMs(n,hy);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===ut)throw P(Id,`service ${e} failed while waiting for healthy`);await I0(D0)}throw P(Id,`service ${e} did not become healthy in time`)}async runTransient(e,t,r,n,o){if(wn(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:kn(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 P(He,`${e} exited with code ${i.code}`);return i}}function I0(e){return new Promise((t)=>setTimeout(t,e))}te();function hs(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 P(Y,`secret manager request failed for ${t}: HTTP ${o.status}`);let i=await o.json();if(!i.payload?.data)throw P(Y,`secret manager response for ${t} had no payload`);return Buffer.from(i.payload.data,"base64").toString("utf8")}}At();te();async function Cc(e,t,r,n){let o="";if(e.proxy?.isRunning())o=`http://${e.proxy.address()}`;else if(e.cfg.proxy.enabled)o=`http://${Sn(e.cfg.proxy.listen)}`;let i=cs(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 as(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:hs(async()=>(await e.tokens.get("user","",[])).accessToken),pluginSources:e.registry?.environmentSources});return ei(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`),ei(i)}}function gy(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 vy(e,t){let r=e.cfg.services[t];if(e.orchestrator.serviceIsActive(t)){if(r&&!e.ports.has(t)){let a=await Ra(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-${cr(e.cfg.repoRoot)}-${t.replace(/[^a-zA-Z0-9_.-]/g,"-")}`,workDir:l,onLine:(p,f)=>e.logs.append({timestamp:e.clock.isoNow(),service:t,source:p,stream:p,level:"",message:f,pid:0}),onExit:(p,f)=>e.orchestrator.health.onExit(t,a,p,f)});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}),Pc(e,t,Rc(e,gn(e.cfg.repoRoot)?.processes.find((p)=>p.name===t))),e.setState(t,je,Tt,0,"");let m=await Cc(e,t,r,u);return e.orchestrator.health.startHealth(t,r,0,u,l,m,a),e.persistState(),e.log(t,"INFO",`claimed running ${c} container ${d.container?.id??""}`),!0}let n=await Ra(r);if(!n)return!1;let o=Object.values(n)[0],s=(o===void 0?void 0:await Jn(o))?.pid??0;if(s>0&&s!==process.pid){let a=gn(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!==""&&ps({args:[...r.command.args],workDir:e.serviceWorkDir(r),startTime:new Date(a.startTime)},c)){e.ports.set(t,n);let d=gy(e,t,s,[...r.command.args],e.serviceWorkDir(r),new Date(a.startTime))??e.orchestrator.health.bumpGeneration(t);Pc(e,t,Rc(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),m=await Cc(e,t,r,n);return e.orchestrator.health.startHealth(t,r,s,n,u,m,d),!0}e.log(t,"WARN",`port ${o} is in use by an unrelated process (pid ${s}); not adopting`)}return!1}async function yy(e){let t=gn(e.cfg.repoRoot);if(!t)return;M0(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-${cr(e.cfg.repoRoot)}-${n.replace(/[^a-zA-Z0-9_.-]/g,"-")}`,workDir:e.serviceWorkDir(o),onLine:(u,m)=>e.logs.append({timestamp:e.clock.isoNow(),service:n,source:u,stream:u,level:"",message:m,pid:0}),onExit:(u,m)=>e.orchestrator.health.onExit(n,s,u,m)});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}),Pc(e,n,Rc(e,i)),e.setState(n,je,Tt,0,"");let d=await Cc(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!==""&&ps({args:n.command,workDir:n.cwd,startTime:n.startTime?new Date(n.startTime):void 0},o))){if(Object.values(n.ports).length>0&&await Ra({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=gy(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);Pc(e,n.name,Rc(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 Cc(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(ye(Kf,"",{services:r,session_id:t.session_id})),e.log("devctl","INFO",`recovered session processes: ${r.join(", ")}`)}function M0(e,t){e.profile=t||e.profile,e.profileEnv=uo(e.cfg,e.profile)}function Pc(e,t,r){e.serviceProfile.set(t,r),e.serviceProfileEnv.set(t,uo(e.cfg,r))}function Rc(e,t){return t?.profile??e.profile}import{statSync as J0,watch as X0}from"fs";import{isAbsolute as Q0,join as Z0,resolve as eA}from"path";function by(e,t){return t}function Nu(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 K0}from"url";import{isAbsolute as q0,resolve as V0}from"path";import{createServer as ky,request as O0}from"http";import{request as $0}from"https";import{randomBytes as xy}from"crypto";import{Readable as F0}from"stream";te();Tr();var gs="x-devctl-request-id",N0="x-devctl-internal-token",H0=100;class _c{cap;items=[];constructor(e=H0){this.cap=e}record(e){if(this.items.push(e),this.items.length>this.cap)this.items.splice(0,this.items.length-this.cap)}stats(){let e=this.items.reduce((t,r)=>t+(r.status>=400||r.error?1:0),0);return{total:this.items.length,errors:e,recent:[...this.items].reverse()}}}var Hu=new Set(["connection","keep-alive","proxy-authenticate","proxy-authorization","te","trailers","transfer-encoding","upgrade"]),j0=new Set(["gzip","deflate","br"]);class ju{cfg;tokens;logs;bus;detector;resolvePort;middleware;server;running=!1;addr="";upgradedSockets=new Set;requests=new _c;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 P(zt,`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 P(zt,`upstream service ${t} is not running`);return`http://127.0.0.1:${r}`}address(){return this.addr||Sn(this.cfg.listen)}setMiddleware(e){this.middleware=e}isRunning(){return this.running}stats(){return this.requests.stats()}recordRequest(e){this.requests.record(e)}start(){let e=this.cfg.listen.host||"127.0.0.1";if(!Er(e))return Promise.reject(P(zt,`refusing to bind proxy to ${e}`));if(this.cfg.listen.port===0)return Promise.reject(P(zt,"proxy.listen.port is required when the proxy is enabled"));let t=this.cfg.listen.port;return new Promise((r,n)=>{this.server=ky((o,i)=>{this.serve(o,i)}),this.server.on("upgrade",(o,i,s)=>{this.serveUpgrade(o,i,s)}),this.server.on("error",(o)=>n(Se(zt,`unable to listen on ${e}:${t}`,o))),this.server.listen(t,e,()=>{this.running=!0,this.addr=`${e}:${t}`,this.bus?.publish(ye(Wf,"",{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(ye(Gf,"",{})),t()})})}async serveUpgrade(e,t,r){let n=Date.now(),o=e.headers[gs]?.toString()||xy(8).toString("hex"),i=e.method??"GET",s=e.url??"/",a=this.detector?this.detector.redactText(s):s,c=Sy(this.cfg.routes,e);if(!c){t.end(`HTTP/1.1 404 Not Found\r
|
|
2258
2258
|
Connection: close\r
|
|
2259
2259
|
Content-Length: 0\r
|
|
2260
2260
|
\r
|
|
2261
|
-
`),this.recordRequest({timestamp:new Date().toISOString(),requestId:o,method:i,path:a,route:"",identity:"",status:404,durationMs:Date.now()-n});return}let l=
|
|
2261
|
+
`),this.recordRequest({timestamp:new Date().toISOString(),requestId:o,method:i,path:a,route:"",identity:"",status:404,durationMs:Date.now()-n});return}let l=an(c.auth),d=_n(l),u,m=!1,p=(h,v)=>{if(m)return;m=!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(ye(Di,c.name,{status:h,request_id:o,duration:b,identity:d}))},f=()=>{t.destroy(),u?.destroy()};try{let h={};for(let[S,y]of Object.entries(e.headers)){let x=S.toLowerCase();if(Hu.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[gs]=o,this.middleware.length===0)await ni(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 C=wy(this.upstreamBase(c),s),E=U0(C)(C,{method:i,headers:h});E.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",f),y.once("error",f);let k=`HTTP/${S.httpVersion} ${S.statusCode??101} ${S.statusMessage??"Switching Protocols"}\r
|
|
2262
2262
|
`,D=S.rawHeaders.map((O,F)=>`${F%2===0?O+":":" "+O+`\r
|
|
2263
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)}),
|
|
2264
|
+
`),r.length>0)y.write(r);if(x.length>0)t.write(x);t.pipe(y).pipe(t),p(S.statusCode??101)}),E.on("response",(S)=>{S.resume();let y=S.statusCode??502;t.end(`HTTP/1.1 ${y} ${S.statusMessage??"Bad Gateway"}\r
|
|
2265
2265
|
Connection: close\r
|
|
2266
2266
|
Content-Length: 0\r
|
|
2267
2267
|
\r
|
|
2268
|
-
`),p(y,"upstream refused protocol upgrade")}),
|
|
2268
|
+
`),p(y,"upstream refused protocol upgrade")}),E.on("error",(S)=>{p(502,S.message),t.end(`HTTP/1.1 502 Bad Gateway\r
|
|
2269
2269
|
Connection: close\r
|
|
2270
2270
|
Content-Length: 0\r
|
|
2271
2271
|
\r
|
|
2272
|
-
`)}),t.once("error",()=>
|
|
2272
|
+
`)}),t.once("error",()=>E.destroy()),E.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
|
|
2273
2273
|
Connection: close\r
|
|
2274
2274
|
Content-Length: 0\r
|
|
2275
2275
|
\r
|
|
2276
|
-
`)}}async serve(e,t){let r=Date.now(),n=e.headers[ds]?.toString()||my(8).toString("hex");t.setHeader(ds,n);let o=e.method??"GET",i=e.url??"/",s=this.detector?this.detector.redactText(i):i,a=vy(this.cfg.routes,e);if(!a){ei(t,404,"no matching proxy route"),this.recordRequest({timestamp:new Date().toISOString(),requestId:n,method:o,path:s,route:"",identity:"",status:404,durationMs:Date.now()-r});return}let c=no(a.auth),l=Ko(c),d=0,u;try{let f={};for(let[y,x]of Object.entries(e.headers)){if(Ou.has(y.toLowerCase()))continue;if(typeof x==="string")f[y]=x;else if(Array.isArray(x))f[y]=x.join(",")}let p=e.socket.remoteAddress??"",m=f.host??"",h=f["x-forwarded-for"];if(f["x-forwarded-for"]=h?`${h}, ${p}`:p,f["x-forwarded-host"]=m,f["x-forwarded-proto"]="http",delete f.host,f["accept-encoding"]="gzip, deflate, br",f[ds]=n,this.middleware.length===0)await Sc(a,f,this.tokens);for(let y of this.middleware)await y.apply({route:a,headers:f,tokens:this.tokens});let v=gy(this.upstreamBase(a),i),b=o!=="GET"&&o!=="HEAD"?e:void 0,g=await fetch(v,{method:o,headers:f,body:b,redirect:"manual",duplex:b?"half":void 0});t.statusCode=g.status,d=g.status;let _=(g.headers.get("content-encoding")??"").split(",").map((y)=>y.trim().toLowerCase()).filter((y)=>y!==""),R=_.length>0&&_.every((y)=>D0.has(y));g.headers.forEach((y,x)=>{let w=x.toLowerCase();if(Ou.has(w))return;if(R&&(w==="content-encoding"||w==="content-length"))return;t.setHeader(x,this.detector?this.detector.redactText(y):y)}),await I0(g,t);let S=Date.now()-r;this.logs?.append({timestamp:new Date().toISOString(),service:"proxy",source:"proxy",level:g.status>=400?"WARN":"INFO",message:`${o} ${i} route=${a.name} identity=${l} status=${g.status} duration=${S}ms`,pid:0,request_id:n,identity:l}),this.bus?.publish(Se(dd,a.name,{status:g.status,request_id:n,duration:S,identity:l}))}catch(f){let p=f instanceof Error?f.message:"proxy error";u=p,d=502,this.logs?.append({timestamp:new Date().toISOString(),service:"proxy",source:"proxy",level:"ERROR",message:`${o} ${i} route=${a.name} identity=${l} error=${p}`,pid:0,request_id:n,identity:l}),ei(t,502,"proxy error")}finally{this.recordRequest({timestamp:new Date().toISOString(),requestId:n,method:o,path:s,route:a.name,identity:l,status:d,durationMs:Date.now()-r,error:u})}}}async function Sc(e,t,r){let n=e.auth.type.toLowerCase();if(n===""||n==="none")return;if(!r)throw E(Nr,"token manager unavailable");let o=no(e.auth),i=await r.get(Ko(o),e.auth.audience,[],ec(e.auth));t.authorization=`Bearer ${i.accessToken}`;for(let[s,a]of Object.entries(e.auth.headers??{}))t[s]=a.includes("${token}")?a.replaceAll("${token}",i.accessToken):a}async function I0(e,t){if(!e.body){t.end();return}let r=A0.fromWeb(e.body);await new Promise((n,o)=>{r.on("error",o),t.on("error",o),t.on("finish",n),r.pipe(t)})}function M0(e){return e.protocol==="https:"?T0:E0}function gy(e,t){let r=new URL(e),n=new URL(t||"/",r),o=new URL(r.href);if(o.pathname=O0(n.pathname),o.search=n.search,o.hash="",o.origin!==r.origin)throw E(Nr,"refusing to proxy to a different origin");return o}function O0(e){return(e.startsWith("/")?e:`/${e}`).replace(/^\/+/,"/")}function ei(e,t,r){e.statusCode=t,e.setHeader("content-type","text/plain; charset=utf-8"),e.setHeader("x-content-type-options","nosniff"),e.end(r)}function vy(e,t){let r=(t.headers.host??"").split(":")[0]??"",n=t.url??"/";return e.find((o)=>{let i=o.match.host===""||o.match.host===r,s=o.match.path===""||n.startsWith(o.match.path);return i&&s})}class Fu{host;port;secret;tokens;server;constructor(e,t,r,n){this.host=e;this.port=t;this.secret=r;this.tokens=n}listenPort(){let e=this.server?.address();if(typeof e==="object"&&e)return e.port;return this.port}start(){let e=this.host||"127.0.0.1";if(!Cn(e))return Promise.reject(E(Nr,`refusing to bind token endpoint to ${e}`));return new Promise((t,r)=>{this.server=yy((n,o)=>{this.serve(n,o)}),this.server.on("error",(n)=>r(Pe(Nr,"token endpoint listen failed",n))),this.server.listen(this.port,e,()=>t())})}stop(){let e=this.server;if(!e)return Promise.resolve();return new Promise((t)=>e.close(()=>t()))}async serve(e,t){if(!$0(e.socket.remoteAddress)){ei(t,403,"forbidden");return}if((e.headers[L0]??"")!==this.secret){ei(t,401,"unauthorized");return}if(e.url?.startsWith("/token")!==!0){ei(t,404,"not found");return}let r=new URL(e.url,"http://127.0.0.1"),n=r.searchParams.get("identity")??"user",o=r.searchParams.get("audience")??"";try{let i=await this.tokens.get(n,o,[]);t.setHeader("content-type","application/json"),t.end(JSON.stringify({access_token:i.accessToken,token_type:i.tokenType,expires_at:i.expiresAt.toISOString(),identity:i.identity}))}catch{ei(t,500,"token error")}}}function $0(e){if(!e)return!0;return e==="127.0.0.1"||e==="::1"||e==="::ffff:127.0.0.1"}jr();var by=1;class xy{tokenProviders=[];identityProviders=[];pluginIdentityProviders=[];environmentSources=[];healthChecks=[];logParsers=[];proxyMiddleware=[];loadErrors=[];pluginPaths=[];registerBuiltins(){this.tokenProviders.push(..._n()),this.identityProviders.push(su(),au()),this.logParsers.push(Wm()),this.proxyMiddleware.push(B0())}register(e){j0(e),po(this.tokenProviders,e.tokenProviders),po(this.identityProviders,e.identityProviders),po(this.pluginIdentityProviders,e.identityProviders),po(this.environmentSources,e.environmentSources),po(this.healthChecks,e.healthChecks),po(this.logParsers,e.logParsers),po(this.proxyMiddleware,e.proxyMiddleware)}}async function kc(e,t=process.cwd()){let r=new xy;r.registerBuiltins();for(let n of e){if(n.trim()==="")continue;try{let o=n.startsWith("file:")?n:N0(n)?n:H0(t,n),s=await import(o.startsWith("file:")?o:F0(o).href);if(s.sdkVersion!==by)throw Error(`plugin SDK version ${String(s.sdkVersion??"missing")} is incompatible; expected ${by}`);r.register(s),r.pluginPaths.push(o)}catch(o){r.loadErrors.push({path:n,message:o instanceof Error?o.message:String(o)})}}return r}function j0(e){if(!e||typeof e!=="object")throw Error("plugin module must export an object");ti("tokenProviders",e.tokenProviders,["fetch"]),ti("identityProviders",e.identityProviders,["accepts","resolve"]),ti("environmentSources",e.environmentSources,["load"]),ti("healthChecks",e.healthChecks,["check"]),ti("logParsers",e.logParsers,["parse"]),ti("proxyMiddleware",e.proxyMiddleware,["apply"])}function ti(e,t,r){if(t===void 0)return;if(!Array.isArray(t))throw Error(`${e} must be an array`);for(let[n,o]of t.entries()){if(!o||typeof o!=="object"||typeof o.name!=="string"||o.name==="")throw Error(`${e}[${n}] must have a name`);for(let i of r)if(typeof o[i]!=="function")throw Error(`${e}[${n}].${i} must be a function`)}}function B0(){return{name:"identity_inject",apply:async(e)=>{await Sc(e.route,e.headers,e.tokens)}}}function po(e,t){if(!t)return;e.push(...t)}var V0=200;function Nu(e){if(!e.registry)return;if(e.registry.tokenProviders.length>0)e.tokens.replaceProviders(e.registry.tokenProviders);e.logs.setParsers(e.registry.logParsers,e.registry.pluginPaths),e.proxy?.setMiddleware?.(e.registry.proxyMiddleware)}function Hu(e,t){let r=new Map;for(let n of e){let o=Y0(n,t);try{r.set(o,U0(o).mtimeMs)}catch{}}return r}async function z0(e,t,r){let n=t.plugins.map((a)=>a.path),o=JSON.stringify(r)!==JSON.stringify(n),i=Hu(n,t.repoRoot),s=[];for(let[a,c]of i){let l=e.pluginMtimes.get(a);if(l!==void 0&&l!==c){s.push("plugins");break}}if(o){let a=await kc(n,t.repoRoot);e.registry=a;for(let c of a.loadErrors)e.log("devctl","ERROR",`plugin ${c.path} skipped: ${c.message}`);Nu(e),e.log("devctl","INFO","plugin path list reloaded")}return e.pluginMtimes=i,s}function Y0(e,t){if(e.startsWith("file:"))return e;return G0(e)?e:q0(t,e)}function ju(e,t){let r=ru(t);if(r.length===0)return;let n=new Set((e?.healthChecks??[]).map((i)=>i.name.toLowerCase())),o=r.filter((i)=>!n.has(i.type.toLowerCase()));if(o.length>0)throw E(Y,`unknown health check type(s): ${o.map((i)=>`${i.service}.health.type=${i.type}`).join(", ")}`)}function Bu(e,t){let r=nu(t);if(r.length===0)return;let n=e?.pluginIdentityProviders??[],o=r.filter(({service:i})=>{let s=t.services[i];return!s||!n.some((a)=>a.accepts(s.identity))});if(o.length>0)throw E(Y,`unknown identity type(s): ${o.map((i)=>`${i.service}.identity.type=${i.type}`).join(", ")}`)}function Uu(e,t){let r=new Set(es),n=new Set((e?.environmentSources??[]).map((i)=>i.name)),o=t.environment.sources.filter((i)=>!r.has(i)&&!n.has(i));if(o.length>0)throw E(Y,`unknown environment source(s): ${o.join(", ")}`)}function J0(e,t,r){for(let n of Object.keys(r))if(!t[n]&&!e.runtimes.has(n))e.runtimes.set(n,Qi(n));for(let n of Object.keys(t)){if(r[n])continue;if(e.orchestrator.serviceIsActive(n)){let o=e.runtimes.get(n);if(o)o.orphaned=!0;e.log(n,"WARN","removed from configuration while still running; now orphaned \u2014 stop it explicitly to clean it up");continue}e.forgetService(n)}}function Wu(e){let t=K0(e.cfg.repoRoot,".devctl");if(!e.fs.exists(t))return;try{e.configWatcher=W0(t,{recursive:!0},()=>{if(e.watchTimer)clearTimeout(e.watchTimer);e.watchTimer=setTimeout(()=>{e.reload().catch((r)=>e.log("devctl","WARN",L(r)))},V0)})}catch{e.log("devctl","WARN","unable to watch .devctl for configuration changes")}}async function wy(e){let t;try{t=on(e.cfg.repoRoot,e.cfg.configPath)}catch(l){throw e.bus.publish(Se(Ti,"",{error:L(l)})),e.log("devctl","ERROR",`configuration reload failed: ${L(l)}`),l}try{ju(e.registry,t),Bu(e.registry,t),Uu(e.registry,t)}catch(l){throw e.bus.publish(Se(Ti,"",{error:L(l)})),e.log("devctl","ERROR",`configuration reload failed: ${L(l)}`),l}if(e.setupMode){if(e.setupMode=!1,e.log("devctl","INFO",`configuration created at ${e.cfg.configPath}; leaving setup mode`),!e.configWatcher)Wu(e)}let r=Mu(e.cfg,t),n=JSON.stringify(e.cfg.proxy)!==JSON.stringify(t.proxy),o=JSON.stringify(e.cfg.secrets)!==JSON.stringify(t.secrets),i=e.cfg.plugins.map((l)=>l.path),s=e.cfg.services;e.cfg=fy(e.cfg,t),J0(e,s,t.services);let a=X0(e.restartRequired,r.restart_required,Object.keys(t.services));e.restartRequired=a,r.restart_required=a;let c=await z0(e,t,i);if(c.length>0)r.supervisor_restart_required=[...r.supervisor_restart_required??[],...c];if(e.syncServiceWatchers(),o)e.detector.update(t.secrets.extra_markers,t.secrets.extra_patterns),e.logs.setSecrets(t.secrets.extra_markers,t.secrets.extra_patterns);if(n){let l=e.proxy?.isRunning()??!1;if(await e.stopProxy(),l&&e.cfg.proxy.enabled)await e.startProxy();e.log("devctl","INFO","proxy configuration changed; proxy restarted")}if(e.bus.publish(Se(da,"",{restart_required:r.restart_required,changes:r.changes,supervisor_restart_required:r.supervisor_restart_required})),e.log("devctl","INFO",r.restart_required.length===0?"configuration reloaded":`configuration reloaded; restart required: ${r.restart_required.join(", ")}`),r.supervisor_restart_required)e.log("devctl","WARN",sc(r.supervisor_restart_required));return e.persistState(),e.refreshIdentity(),r}function X0(e,t,r){let n=new Set(r),o=new Set(e.filter((i)=>n.has(i)));for(let i of t)if(n.has(i))o.add(i);return[...o].sort()}import{watch as Z0}from"fs";import{join as ky,relative as eA}from"path";ht();function Sy(e,t){if(!e?.enabled)return!1;let r=t.replaceAll("\\","/").replace(/^\.\//,"");if(r===""||r.includes(".."))return!1;if((e.ignore.length>0?e.ignore:$i).some((o)=>Q0(o,r)))return!1;if(e.paths.length===0)return!1;return e.paths.some((o)=>{let i=o.replaceAll("\\","/").replace(/\/$/,"");return r===i||r.startsWith(`${i}/`)})}function Q0(e,t){let r=e.replace(/[.+^${}()|[\]\\]/g,"\\$&").replaceAll("**/","<<<DS>>>").replaceAll("**","<<<DS>>>").replaceAll("*","[^/]*").replaceAll("<<<DS>>>",".*");return new RegExp(`^${r}$`).test(t)}class Gu{host;entries=new Map;constructor(e){this.host=e}sync(e){let t=new Set;for(let[r,n]of Object.entries(e)){if(!n.watch.enabled||n.watch.paths.length===0){this.stop(r);continue}t.add(r);let o=JSON.stringify(n.watch);if(this.entries.get(r)?.signature===o)continue;this.stop(r),this.start(r,n.watch)}for(let r of[...this.entries.keys()])if(!t.has(r))this.stop(r)}watched(){return[...this.entries.keys()].sort()}close(){for(let e of[...this.entries.keys()])this.stop(e)}start(e,t){let r=[],n=this.host.repoRoot();for(let o of t.paths){let i=o.replaceAll("\\","/").replace(/\/$/,"");if(i===""||i==="."){this.host.log(e,"WARN","service.watch skipped a whole-repo path; list concrete directories");continue}let s=ky(n,i);try{let a=Z0(s,{recursive:!0},(c,l)=>{let d=l?ky(s,l.toString()):s,u=eA(n,d).replaceAll("\\","/");if(!Sy(t,u))return;this.schedule(e,t.debounce_ms)});r.push(a)}catch{this.host.log(e,"WARN",`unable to watch ${i}`)}}if(r.length===0)return;this.entries.set(e,{signature:JSON.stringify(t),watch:t,handles:r}),this.host.log(e,"INFO",`watching ${t.paths.join(", ")}`)}schedule(e,t){let r=this.entries.get(e);if(!r)return;if(r.timer)clearTimeout(r.timer);r.timer=setTimeout(()=>{if(r.timer=void 0,!this.host.isActive(e))return;this.host.log(e,"INFO","watch: restarting after file change"),this.host.restart(e).catch((n)=>{this.host.log(e,"WARN",n instanceof Error?n.message:String(n))})},t)}stop(e){let t=this.entries.get(e);if(!t)return;if(t.timer)clearTimeout(t.timer);for(let r of t.handles)r.close();this.entries.delete(e)}}import{isAbsolute as Ku,join as qu}from"path";class Vu{clientEnv=new Map;serviceProfile=new Map;serviceProfileEnv=new Map;profile="";profileEnv={};deps;constructor(e){this.deps=e}forget(e){this.clientEnv.delete(e),this.serviceProfile.delete(e),this.serviceProfileEnv.delete(e)}serviceWorkDir(e){if(e.working_dir!==""&&!Ku(e.working_dir))return qu(this.deps.cfg().repoRoot,e.working_dir);return e.working_dir}async resolveServiceExecution(e,t,r,n,o,i=!0){let s=this.deps.cfg(),a=this.deps.ports().get(e)??Object.fromEntries(t.ports.filter((m)=>!m.auto).map((m)=>[m.name,m.value])),c=this.deps.proxy(),l=c?.isRunning()?`http://${c.address()}`:s.proxy.enabled?`http://${xn(s.proxy.listen)}`:"",d=this.deps.userEmail(),u=rs(e,"127.0.0.1",a,l,s.project.name,d);if(!t.container){if(u.DEVCTL_INTERNAL_TOKEN=this.deps.internalTok(),s.proxy.token_endpoint.enabled)u.DEVCTL_TOKEN_URL=this.deps.boundTokenURL()||`http://127.0.0.1:${this.deps.tokenEndpoint()?.listenPort()||s.proxy.token_endpoint.port}/token`}let f=await ts(s.repoRoot,{service:e,profile:r,serviceCfg:t,profileEnv:n,assignedPorts:a,runtime:u,userEmail:d,cfg:s,fetchSecret:ls(async()=>(await this.deps.tokens.get("user","",[])).accessToken),pluginSources:this.deps.environmentSources(),clientEnv:o,includeProcess:i}),p=t.working_dir&&!Ku(t.working_dir)?qu(s.repoRoot,t.working_dir):t.working_dir;return{env:Qo(f),workDir:p}}async resolveTaskEnvironment(e,t,r){let n=this.deps.cfg(),o=this.deps.userEmail(),i=await ts(n.repoRoot,{service:`task:${e}`,profile:this.profile,serviceCfg:t,profileEnv:this.profileEnv,assignedPorts:{},runtime:rs(`task:${e}`,"127.0.0.1",{},"",n.project.name,o),userEmail:o,cfg:n,clientEnv:r,fetchSecret:ls(async()=>(await this.deps.tokens.get("user","",[])).accessToken),pluginSources:this.deps.environmentSources()}),s=t.working_dir&&!Ku(t.working_dir)?qu(n.repoRoot,t.working_dir):t.working_dir;return{env:Qo(i),workDir:s}}}jr();te();jr();At();import{cpus as dA,loadavg as uA,platform as pA,uptime as fA}from"os";import{readFileSync as tA}from"fs";import{freemem as rA,totalmem as nA}from"os";import{spawnSync as oA}from"child_process";var zu=1024,iA=2000,sA=/page size of (\d+) bytes/i,ri;function aA(e){let t=e.match(/^MemAvailable:\s+(\d+)/m);if(!t)return;let r=Number(t[1]);return Number.isFinite(r)&&r>=0?r:void 0}function cA(e){let t=e.match(sA),r=t?Number(t[1]):0;if(!Number.isFinite(r)||r<=0)return;let n=Cc(e,"free")+Cc(e,"inactive")+Cc(e,"speculative")+Cc(e,"purgeable");return Math.round(n*r/zu)}function Cc(e,t){let r=e.match(new RegExp(`Pages ${t}:\\s+(\\d+)`,"i")),n=r?Number(r[1]):0;return Number.isFinite(n)&&n>0?n:0}function lA(){if(process.platform==="linux")try{return aA(tA("/proc/meminfo","utf8"))}catch{return}if(process.platform==="darwin"){let e=Date.now();if(ri&&e-ri.at<iA)return ri.leftoverKB;let t=oA("vm_stat",{encoding:"utf8"});if(t.status!==0||!t.stdout)return ri?.leftoverKB;let r=cA(t.stdout);if(r===void 0)return ri?.leftoverKB;return ri={at:e,leftoverKB:r},r}return}function Cy(){let e=Math.max(0,Math.round(nA()/zu)),t=Math.max(0,Math.round(rA()/zu)),r=Math.max(0,Math.min(e,lA()??t));return{totalKB:e,unusedKB:t,leftoverKB:r}}function Py(e){return{user:"",project:e?.google.project_id??"",project_source:e?.google.project_id?"configuration":"",adc:!1,service_accounts:{},service_account_status:Object.fromEntries(e?Hr(e).map((t)=>[t,"unknown"]):[]),iap:e?.proxy.routes.some((t)=>t.auth.type.toLowerCase()==="iap")??!1}}function Yu(){let e=uA(),t=Cy();return{platform:pA(),cpuCount:dA().length,loadAvg1:e[0]??0,loadAvg5:e[1]??0,loadAvg15:e[2]??0,memTotalKB:t.totalKB,memFreeKB:t.unusedKB,memAvailableKB:t.leftoverKB,hostUptimeSec:fA()}}function Ju(e,t){let r={},n={};for(let o of Hr(e)){let i=t.get(o)??"unknown";if(n[o]=i,i!=="unknown")r[o]=i==="available"}return{service_accounts:r,service_account_status:n}}function _y(e){let t={};for(let[o,i]of e.runtimes)t[o]={...i,ports:e.ports.get(o)??i.ports,profile:e.serviceProfile.get(o)??i.profile,env_source:e.clientEnv.has(o)?"client":"daemon"};let r=e.proxy?.stats(),n={requestTotal:r?.total??0,requestErrors:r?.errors??0,recentRequests:r?.recent??[]};return{session_id:e.sessionID,repo_root:e.cfg.repoRoot,profile:e.profile,services:t,proxy:{running:e.proxy?.isRunning()??!1,address:e.proxy?.address(),routes:e.cfg.proxy.routes.map((o)=>{let i=o.match.host||"*",s=o.match.path,a=s===""?i:`${i}${s.startsWith("/")?s:`/${s}`}`;return{name:o.name,identity:o.auth.identity.service_account||o.auth.identity.type||o.auth.type,upstream:o.upstream.url,auth:o.auth.type,match:a,client_id:o.auth.client_id.trim()||void 0}}),...n},mcp:{running:e.mcp?.isRunning()??!1,address:e.mcp?.isRunning()?`http://${e.mcp.address()}/mcp`:void 0,port:e.mcp?.isRunning()?e.mcp.listenPort():void 0,token:e.mcpToken,disabled_tools:[...e.mcpDisabledTools]},identity:{...e.identityCache,...Ju(e.cfg,e.serviceAccountStatus)},credentials:{backend:e.tokens.storeBackend(),entries:[...e.credentialEntries]},detached:e.detached,setup_mode:e.setupMode?!0:void 0,logs:e.logs.snapshot(),restart_required:[...e.restartRequired],system:Yu(),stats_series:e.statsSeries}}function Ry(e){let t=[`PROFILE: ${e.profile||"(none)"}`,"","SERVICE\tSTATUS\tHEALTH\tPID"];for(let[r,n]of Object.entries(e.services))t.push(`${r} ${Jo(n)} ${n.health} ${n.pid}`);return t.push("",`PROXY ${e.proxy.running?"RUNNING":"STOPPED"} ${e.proxy.address??""}`),t.push(`MCP ${e.mcp?.running?"RUNNING":"STOPPED"} ${e.mcp?.address??""}`),t.join(`
|
|
2276
|
+
`)}}async serve(e,t){let r=Date.now(),n=e.headers[gs]?.toString()||xy(8).toString("hex");t.setHeader(gs,n);let o=e.method??"GET",i=e.url??"/",s=this.detector?this.detector.redactText(i):i,a=Sy(this.cfg.routes,e);if(!a){ri(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=an(a.auth),l=_n(c),d=0,u;try{let m={};for(let[y,x]of Object.entries(e.headers)){if(Hu.has(y.toLowerCase()))continue;if(typeof x==="string")m[y]=x;else if(Array.isArray(x))m[y]=x.join(",")}let p=e.socket.remoteAddress??"",f=m.host??"",h=m["x-forwarded-for"];if(m["x-forwarded-for"]=h?`${h}, ${p}`:p,m["x-forwarded-host"]=f,m["x-forwarded-proto"]="http",delete m.host,m["accept-encoding"]="gzip, deflate, br",m[gs]=n,this.middleware.length===0)await ni(a,m,this.tokens);for(let y of this.middleware)await y.apply({route:a,headers:m,tokens:this.tokens});let v=wy(this.upstreamBase(a),i),b=o!=="GET"&&o!=="HEAD"?e:void 0,g=await fetch(v,{method:o,headers:m,body:b,redirect:"manual",duplex:b?"half":void 0});t.statusCode=g.status,d=g.status;let C=(g.headers.get("content-encoding")??"").split(",").map((y)=>y.trim().toLowerCase()).filter((y)=>y!==""),E=C.length>0&&C.every((y)=>j0.has(y));g.headers.forEach((y,x)=>{let w=x.toLowerCase();if(Hu.has(w))return;if(E&&(w==="content-encoding"||w==="content-length"))return;t.setHeader(x,this.detector?this.detector.redactText(y):y)}),await B0(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(ye(Di,a.name,{status:g.status,request_id:n,duration:S,identity:l}))}catch(m){let p=m instanceof Error?m.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}),ri(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 ni(e,t,r){let n=e.auth.type.toLowerCase();if(n===""||n==="none")return;if(!r)throw P(zt,"token manager unavailable");let o=an(e.auth),i=await r.get(_n(o),e.auth.audience,[],ic(e.auth));t.authorization=`Bearer ${i.accessToken}`;for(let[s,a]of Object.entries(e.auth.headers??{}))t[s]=a.includes("${token}")?a.replaceAll("${token}",i.accessToken):a}async function B0(e,t){if(!e.body){t.end();return}let r=F0.fromWeb(e.body);await new Promise((n,o)=>{r.on("error",o),t.on("error",o),t.on("finish",n),r.pipe(t)})}function U0(e){return e.protocol==="https:"?$0:O0}function wy(e,t){let r=new URL(e),n=new URL(t||"/",r),o=new URL(r.href);if(o.pathname=W0(n.pathname),o.search=n.search,o.hash="",o.origin!==r.origin)throw P(zt,"refusing to proxy to a different origin");return o}function W0(e){return(e.startsWith("/")?e:`/${e}`).replace(/^\/+/,"/")}function ri(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 Sy(e,t){let r=(t.headers.host??"").split(":")[0]??"",n=t.url??"/";return e.find((o)=>{if(Ko(o))return!1;let i=o.match.host===""||o.match.host===r,s=o.match.path===""||n.startsWith(o.match.path);return i&&s})}class Bu{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(!Er(e))return Promise.reject(P(zt,`refusing to bind token endpoint to ${e}`));return new Promise((t,r)=>{this.server=ky((n,o)=>{this.serve(n,o)}),this.server.on("error",(n)=>r(Se(zt,"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(!G0(e.socket.remoteAddress)){ri(t,403,"forbidden");return}if((e.headers[N0]??"")!==this.secret){ri(t,401,"unauthorized");return}if(e.url?.startsWith("/token")!==!0){ri(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{ri(t,500,"token error")}}}function G0(e){if(!e)return!0;return e==="127.0.0.1"||e==="::1"||e==="::ffff:127.0.0.1"}Tr();var Cy=1;class Py{tokenProviders=[];identityProviders=[];pluginIdentityProviders=[];environmentSources=[];healthChecks=[];logParsers=[];proxyMiddleware=[];loadErrors=[];pluginPaths=[];registerBuiltins(){this.tokenProviders.push(...En()),this.identityProviders.push(du(),uu()),this.logParsers.push(Yf()),this.proxyMiddleware.push(Y0())}register(e){z0(e),mo(this.tokenProviders,e.tokenProviders),mo(this.identityProviders,e.identityProviders),mo(this.pluginIdentityProviders,e.identityProviders),mo(this.environmentSources,e.environmentSources),mo(this.healthChecks,e.healthChecks),mo(this.logParsers,e.logParsers),mo(this.proxyMiddleware,e.proxyMiddleware)}}async function Ec(e,t=process.cwd()){let r=new Py;r.registerBuiltins();for(let n of e){if(n.trim()==="")continue;try{let o=n.startsWith("file:")?n:q0(n)?n:V0(t,n),s=await import(o.startsWith("file:")?o:K0(o).href);if(s.sdkVersion!==Cy)throw Error(`plugin SDK version ${String(s.sdkVersion??"missing")} is incompatible; expected ${Cy}`);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 z0(e){if(!e||typeof e!=="object")throw Error("plugin module must export an object");oi("tokenProviders",e.tokenProviders,["fetch"]),oi("identityProviders",e.identityProviders,["accepts","resolve"]),oi("environmentSources",e.environmentSources,["load"]),oi("healthChecks",e.healthChecks,["check"]),oi("logParsers",e.logParsers,["parse"]),oi("proxyMiddleware",e.proxyMiddleware,["apply"])}function oi(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 Y0(){return{name:"identity_inject",apply:async(e)=>{await ni(e.route,e.headers,e.tokens)}}}function mo(e,t){if(!t)return;e.push(...t)}var tA=200;function Uu(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 Wu(e,t){let r=new Map;for(let n of e){let o=nA(n,t);try{r.set(o,J0(o).mtimeMs)}catch{}}return r}async function rA(e,t,r){let n=t.plugins.map((a)=>a.path),o=JSON.stringify(r)!==JSON.stringify(n),i=Wu(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 Ec(n,t.repoRoot);e.registry=a;for(let c of a.loadErrors)e.log("devctl","ERROR",`plugin ${c.path} skipped: ${c.message}`);Uu(e),e.log("devctl","INFO","plugin path list reloaded")}return e.pluginMtimes=i,s}function nA(e,t){if(e.startsWith("file:"))return e;return Q0(e)?e:eA(t,e)}function Gu(e,t){let r=su(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 P(Y,`unknown health check type(s): ${o.map((i)=>`${i.service}.health.type=${i.type}`).join(", ")}`)}function Ku(e,t){let r=au(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 P(Y,`unknown identity type(s): ${o.map((i)=>`${i.service}.identity.type=${i.type}`).join(", ")}`)}function qu(e,t){let r=new Set(ss),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 P(Y,`unknown environment source(s): ${o.join(", ")}`)}function oA(e,t,r){for(let n of Object.keys(r))if(!t[n]&&!e.runtimes.has(n))e.runtimes.set(n,os(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 Vu(e){let t=Z0(e.cfg.repoRoot,".devctl");if(!e.fs.exists(t))return;try{e.configWatcher=X0(t,{recursive:!0},()=>{if(e.watchTimer)clearTimeout(e.watchTimer);e.watchTimer=setTimeout(()=>{e.reload().catch((r)=>e.log("devctl","WARN",L(r)))},tA)})}catch{e.log("devctl","WARN","unable to watch .devctl for configuration changes")}}async function Ry(e){let t;try{t=sn(e.cfg.repoRoot,e.cfg.configPath)}catch(l){throw e.bus.publish(ye(Ii,"",{error:L(l)})),e.log("devctl","ERROR",`configuration reload failed: ${L(l)}`),l}try{Gu(e.registry,t),Ku(e.registry,t),qu(e.registry,t)}catch(l){throw e.bus.publish(ye(Ii,"",{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)Vu(e)}let r=Nu(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=by(e.cfg,t),oA(e,s,t.services);let a=iA(e.restartRequired,r.restart_required,Object.keys(t.services));e.restartRequired=a,r.restart_required=a;let c=await rA(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(ye(ga,"",{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",uc(r.supervisor_restart_required));return e.persistState(),e.refreshIdentity(),r}function iA(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 aA}from"fs";import{join as Ey,relative as cA}from"path";dt();function _y(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:ji).some((o)=>sA(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 sA(e,t){let r=e.replace(/[.+^${}()|[\]\\]/g,"\\$&").replaceAll("**/","<<<DS>>>").replaceAll("**","<<<DS>>>").replaceAll("*","[^/]*").replaceAll("<<<DS>>>",".*");return new RegExp(`^${r}$`).test(t)}class zu{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=Ey(n,i);try{let a=aA(s,{recursive:!0},(c,l)=>{let d=l?Ey(s,l.toString()):s,u=cA(n,d).replaceAll("\\","/");if(!_y(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 Yu,join as Ju}from"path";class Xu{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!==""&&!Yu(e.working_dir))return Ju(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((f)=>!f.auto).map((f)=>[f.name,f.value])),c=this.deps.proxy(),l=c?.isRunning()?`http://${c.address()}`:s.proxy.enabled?`http://${Sn(s.proxy.listen)}`:"",d=this.deps.userEmail(),u=cs(e,"127.0.0.1",a,l,s.project.name,d);if(!t.container){if(u.DEVCTL_INTERNAL_TOKEN=this.deps.internalTok(),s.proxy.token_endpoint.enabled)u.DEVCTL_TOKEN_URL=this.deps.boundTokenURL()||`http://127.0.0.1:${this.deps.tokenEndpoint()?.listenPort()||s.proxy.token_endpoint.port}/token`}let m=await as(s.repoRoot,{service:e,profile:r,serviceCfg:t,profileEnv:n,assignedPorts:a,runtime:u,userEmail:d,cfg:s,fetchSecret:hs(async()=>(await this.deps.tokens.get("user","",[])).accessToken),pluginSources:this.deps.environmentSources(),clientEnv:o,includeProcess:i}),p=t.working_dir&&!Yu(t.working_dir)?Ju(s.repoRoot,t.working_dir):t.working_dir;return{env:ei(m),workDir:p}}async resolveTaskEnvironment(e,t,r){let n=this.deps.cfg(),o=this.deps.userEmail(),i=await as(n.repoRoot,{service:`task:${e}`,profile:this.profile,serviceCfg:t,profileEnv:this.profileEnv,assignedPorts:{},runtime:cs(`task:${e}`,"127.0.0.1",{},"",n.project.name,o),userEmail:o,cfg:n,clientEnv:r,fetchSecret:hs(async()=>(await this.deps.tokens.get("user","",[])).accessToken),pluginSources:this.deps.environmentSources()}),s=t.working_dir&&!Yu(t.working_dir)?Ju(n.repoRoot,t.working_dir):t.working_dir;return{env:ei(i),workDir:s}}}Tr();te();Tr();At();import{cpus as yA,loadavg as bA,platform as xA,uptime as wA}from"os";import{readFileSync as lA}from"fs";import{freemem as dA,totalmem as uA}from"os";import{spawnSync as pA}from"child_process";var Qu=1024,mA=2000,fA=/page size of (\d+) bytes/i,ii;function hA(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 gA(e){let t=e.match(fA),r=t?Number(t[1]):0;if(!Number.isFinite(r)||r<=0)return;let n=Tc(e,"free")+Tc(e,"inactive")+Tc(e,"speculative")+Tc(e,"purgeable");return Math.round(n*r/Qu)}function Tc(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 vA(){if(process.platform==="linux")try{return hA(lA("/proc/meminfo","utf8"))}catch{return}if(process.platform==="darwin"){let e=Date.now();if(ii&&e-ii.at<mA)return ii.leftoverKB;let t=pA("vm_stat",{encoding:"utf8"});if(t.status!==0||!t.stdout)return ii?.leftoverKB;let r=gA(t.stdout);if(r===void 0)return ii?.leftoverKB;return ii={at:e,leftoverKB:r},r}return}function Ty(){let e=Math.max(0,Math.round(uA()/Qu)),t=Math.max(0,Math.round(dA()/Qu)),r=Math.max(0,Math.min(e,vA()??t));return{totalKB:e,unusedKB:t,leftoverKB:r}}function Ay(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?Br(e).map((t)=>[t,"unknown"]):[]),iap:e?.proxy.routes.some((t)=>t.auth.type.toLowerCase()==="iap")??!1}}function Zu(){let e=bA(),t=Ty();return{platform:xA(),cpuCount:yA().length,loadAvg1:e[0]??0,loadAvg5:e[1]??0,loadAvg15:e[2]??0,memTotalKB:t.totalKB,memFreeKB:t.unusedKB,memAvailableKB:t.leftoverKB,hostUptimeSec:wA()}}function ep(e,t){let r={},n={};for(let o of Br(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 Ly(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,...ep(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 Dy(e){let t=[`PROFILE: ${e.profile||"(none)"}`,"","SERVICE\tSTATUS\tHEALTH\tPID"];for(let[r,n]of Object.entries(e.services))t.push(`${r} ${Qo(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(`
|
|
2277
2277
|
`)+`
|
|
2278
|
-
`}var mA=4000;class Xu{cache;accounts=new Map;entries=[];deps;constructor(e){this.deps=e,this.cache=Py(e.cfg()),this.deps.bus.subscribe((t)=>{let r=t.payload??{},n=t.type===Mo?`token refreshed identity=${String(r.identity??"")}`:t.type===Gn?`token refresh failed identity=${String(r.identity??"")} audience=${String(r.audience??"")}: ${String(r.error??"")}`:`authentication changed user=${String(r.user??"")}`;if(this.deps.logs.append({timestamp:this.deps.clock.isoNow(),service:"auth",source:"auth",level:t.type===Gn?"WARN":"INFO",message:n,pid:0}),t.type===Mo||t.type===Gn)this.syncCredentialEntries()},[Mo,Gn,ld])}get identityCache(){return this.cache}get serviceAccountStatus(){return this.accounts}get credentialEntries(){return this.entries}async prepareServiceIdentity(e,t){let r=qa(t.identity);if(r.kind!=="none")try{if(r=await Ig(t.identity,()=>Fg(this.deps.cfg().google.project_id),this.deps.identityProviders()),r.kind==="service_account")await this.deps.tokens.get(Ko(r),"",[]),this.accounts.set(r.serviceAccount,"available")}catch(n){if(r.kind==="service_account")this.accounts.set(r.serviceAccount,"unavailable");if(gA(t)||r.kind==="service_account"||r.kind!=="user"&&r.kind!=="none")throw await this.deps.fail(e,n),n;this.deps.log(e,"WARN","cloud identity unavailable; starting service locally")}}async syncCredentialEntries(){this.entries=(await this.deps.tokens.listStatus()).map((e)=>({identity:e.identity,audience:e.audience,expires_at:e.expires_at,valid:e.valid}))}async refreshIdentity(e){try{let t=this.deps.cfg(),r=await this.deps.detectGoogle(t.google.project_id);if(e?.probeServiceAccounts)for(let i of Hr(t))await this.probeServiceAccount(i);let{service_accounts:n,service_account_status:o}=Ju(t,this.accounts);this.cache={user:r.userEmail,project:r.projectID||t.google.project_id,project_source:r.projectSource||(t.google.project_id?"configuration":""),adc:r.adcAvailable,service_accounts:n,service_account_status:o,iap:t.proxy.routes.some((i)=>i.auth.type.toLowerCase()==="iap")},await this.syncCredentialEntries(),this.deps.bus.publish(Se(ld,"",{user:this.cache.user,adc:this.cache.adc})),this.deps.log("auth","INFO",`authentication changed user=${this.cache.user||"(unknown)"} adc=${this.cache.adc}`),this.deps.persistState()}catch(t){this.deps.log("devctl","WARN",`identity refresh failed: ${L(t)}`)}}async probeServiceAccount(e){let t;try{await hA(this.deps.tokens.refresh(`sa:${e}`,"",[]),mA),t="available"}catch{t="unavailable"}return this.accounts.set(e,t),t}}function hA(e,t){return new Promise((r,n)=>{let o=setTimeout(()=>n(Error("identity probe timed out")),t);e.then((i)=>{clearTimeout(o),r(i)},(i)=>{clearTimeout(o),n(i)})})}function gA(e){return e.capabilities.some((t)=>["google_api","iap","service_identity"].includes(t))}te();te();Zu();var wA=600,SA=65535;async function Iy(e,t){let r=Number.isInteger(t)&&(t??0)>0?t:fo(e);for(let n=0;n<wA;n+=1){let o=r+n;if(o>SA)break;if(await jo(o))return o}throw E(Y,`no free MCP port starting at ${r}`)}class ep{listener;disabled=[];token;deps;constructor(e){this.deps=e,this.token=Tm(e.repoRoot())}get instance(){return this.listener}get disabledTools(){return this.disabled}async bootFromPreferences(){let e=Do(this.deps.repoRoot());if(this.setDisabledTools(e.mcp_disabled_tools??[]),e.mcp_enabled)await this.start(e.mcp_port).catch((t)=>this.deps.log("devctl","ERROR",L(t)))}async start(e){if(this.listener?.isRunning())return;let t=await Iy(this.deps.repoRoot(),e);this.listener=this.deps.createListener({port:t,token:this.token,hostApi:this.deps.hostApi(),onEvent:(r,n)=>this.deps.log("mcp",r,`mcp ${n}`),disabledTools:()=>this.disabled}),await this.listener.start(),this.deps.persistState()}setDisabledTools(e){let t=e.filter((n)=>this.deps.isKnownTool(n)),r=this.disabled.join(",");if(this.disabled=[...new Set(t)].sort(),this.disabled.join(",")===r)return;this.deps.log("devctl","INFO",this.disabled.length===0?"all MCP tools enabled":`MCP tools disabled: ${this.disabled.join(", ")}`)}async stop(){await this.listener?.stop(),this.listener=void 0,this.deps.persistState()}}class tp{server;tokenEP;boundURL="";suppressed=!1;deps;constructor(e){this.deps=e}get instance(){return this.server}get tokenEndpoint(){return this.tokenEP}get boundTokenURL(){return this.boundURL}get isSuppressed(){return this.suppressed}setSuppressed(e){this.suppressed=e}async start(){if(this.server?.isRunning())return;this.server=new $u(this.deps.cfg().proxy,this.deps.tokens,this.deps.logs,this.deps.bus,this.deps.detector,this.deps.middleware(),(t,r)=>this.deps.ports().get(t)?.[r||"http"]),await this.server.start();let e=this.deps.cfg();if(e.proxy.token_endpoint.enabled)this.tokenEP=new Fu(e.proxy.token_endpoint.host||"127.0.0.1",e.proxy.token_endpoint.port,this.deps.internalTok(),this.deps.tokens),await this.tokenEP.start(),this.boundURL=`http://127.0.0.1:${this.tokenEP.listenPort()}/token`;this.deps.persistState()}async stop(){await this.server?.stop(),await this.tokenEP?.stop(),this.deps.persistState()}}At();var kA=3000,My=5000,Oy=60;class rp{timer;cpu=[];mem=[];lastAt=0;deps;constructor(e){this.deps=e}start(){if(this.timer)return;this.timer=setInterval(()=>void this.poll(),kA)}stop(){if(this.timer)clearInterval(this.timer),this.timer=void 0}series(){return{interval_ms:My,cpu:[...this.cpu],mem:[...this.mem]}}async poll(){this.recordStatsSample();let e=[];for(let r of this.deps.runtimes().values())if(r.pid>0&&(r.state===je||r.state===pr||r.state===Br))e.push(r.pid);if(e.length===0)return;let t=await iy(e);for(let r of this.deps.runtimes().values()){let n=r.pid>0?t.get(r.pid):void 0;if(n)r.cpuPercent=n.cpuPercent,r.memoryKB=n.memoryKB}}recordStatsSample(){let e=this.deps.clock.unixMs();if(e-this.lastAt<My)return;this.lastAt=e;let t=Yu(),r=t.cpuCount>0?t.loadAvg1/t.cpuCount:0,n=t.memTotalKB>0?1-t.memAvailableKB/t.memTotalKB:0;if(this.cpu.push(r),this.mem.push(n),this.cpu.length>Oy)this.cpu.shift();if(this.mem.length>Oy)this.mem.shift()}}function np(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}function En(e){if(!Array.isArray(e))return[];return e.filter((t)=>typeof t==="string")}function us(e){if(!np(e))return;let t={};for(let[r,n]of Object.entries(e))if(typeof n==="string")t[r]=n;return t}te();import{createServer as CA}from"net";var PA=2000;class op{server;deps;constructor(e){this.deps=e}get nativeServer(){return this.server}removeStaleSocket(e){if(process.platform==="win32")return;if(this.deps.socketExists(e))try{this.deps.unlinkSocket(e)}catch{this.deps.log("devctl","WARN","unable to remove stale socket")}}listen(e){return new Promise((t,r)=>{this.server=CA((n)=>{this.handleConn(n)}),this.server.on("error",r),this.server.listen(e,()=>t())})}close(){this.server?.close(),this.server=void 0}handleConn(e){let t="",r=[],n=!1,o=()=>{while(r.length>0){let a=r[0],c=e.write(`${JSON.stringify(a)}
|
|
2279
|
-
`);if(r.shift(),!c){n=!0,e.once("drain",()=>{n=!1,o()});return}}},i=(a)=>{if(a.id===void 0&&a.event!==void 0&&r.length
|
|
2280
|
-
`);t=c.pop()??"";for(let l of c){if(l.trim()==="")continue;this.dispatchLine(l,i)}}),e.on("close",()=>s())}async dispatchLine(e,t){let r;try{r=JSON.parse(e)}catch{t({error:"invalid json"});return}try{let n=await this.deps.dispatch(r.method??"",r.params);t({id:r.id,result:n})}catch(n){let o=ph(n);t({id:r.id,error:o.error,kind:o.kind,hint:o.hint,service:o.service})}}}te();jr();At();class ip{cfg;sessionID;internalTok;bus;logs;procs;tokens;detector;env;proxy;mcp;resources;runtimes=new Map;ports=new Map;lock;shuttingDown=!1;detached=!1;identity;rpc;detectGoogleFn;inspectProcessFn;processAliveFn;acquireLockFn;socketExistsFn;unlinkSocketFn;registry;restartRequired=[];processMeta=new Map;configWatcher;watchTimer;setupMode;serviceWatchers;pluginMtimes=new Map;orchestrator;commands;clock;fs;healthCheckers;constructor(e,t){this.healthCheckers=t.healthCheckers,this.cfg=e,this.sessionID=t.sessionID,this.internalTok=id(),this.clock=t.clock,this.fs=t.fs,this.bus=t.bus,this.detectGoogleFn=t.detectGoogle,this.inspectProcessFn=t.inspectProcess,this.processAliveFn=t.processAlive,this.acquireLockFn=t.acquireLock,this.socketExistsFn=t.socketExists,this.unlinkSocketFn=t.unlinkSocket,this.detector=t.detector,this.logs=t.logs,this.procs=t.procs,this.orchestrator=t.orchestrator,this.tokens=t.tokens,this.proxy=new tp({cfg:()=>this.cfg,ports:()=>this.ports,tokens:this.tokens,logs:this.logs,bus:this.bus,detector:this.detector,internalTok:()=>this.internalTok,middleware:()=>this.registry?.proxyMiddleware??[],persistState:()=>this.persistState()}),this.env=new Vu({cfg:()=>this.cfg,ports:()=>this.ports,userEmail:()=>this.identity.identityCache.user,proxy:()=>this.proxy.instance,tokenEndpoint:()=>this.proxy.tokenEndpoint,boundTokenURL:()=>this.proxy.boundTokenURL,internalTok:()=>this.internalTok,tokens:this.tokens,environmentSources:()=>this.registry?.environmentSources}),this.mcp=new ep({repoRoot:()=>this.cfg.repoRoot,createListener:t.createMcpListener,hostApi:()=>this.asMcpHost(),isKnownTool:t.isKnownTool,log:(r,n,o)=>this.log(r,n,o),persistState:()=>this.persistState()}),this.resources=new rp({clock:this.clock,runtimes:()=>this.runtimes}),this.identity=new Xu({cfg:()=>this.cfg,tokens:this.tokens,detectGoogle:(r)=>this.detectGoogleFn(r),clock:this.clock,bus:this.bus,logs:this.logs,persistState:()=>this.persistState(),fail:(r,n)=>this.fail(r,n),log:(r,n,o)=>this.log(r,n,o),identityProviders:()=>this.registry?.identityProviders}),this.rpc=new op({dispatch:(r,n)=>this.dispatch(r,n),subscribe:(r,n)=>this.bus.subscribe(r,n),log:(r,n,o)=>this.log(r,n,o),socketExists:(r)=>this.socketExistsFn(r),unlinkSocket:(r)=>this.unlinkSocketFn(r)}),this.setupMode=!this.fs.exists(e.configPath);for(let r of Object.keys(e.services))this.runtimes.set(r,Qi(r));this.orchestrator.bind(this.lifecycleSession()),this.commands=t.createCommands(this),this.serviceWatchers=new Gu({repoRoot:()=>this.cfg.repoRoot,log:(r,n,o)=>this.log(r,n,o),isActive:(r)=>this.orchestrator.serviceIsActive(r),restart:(r)=>this.restart([r],{auto:!0})})}get profile(){return this.env.profile}set profile(e){this.env.profile=e}get profileEnv(){return this.env.profileEnv}set profileEnv(e){this.env.profileEnv=e}get clientEnv(){return this.env.clientEnv}get serviceProfile(){return this.env.serviceProfile}get serviceProfileEnv(){return this.env.serviceProfileEnv}snapshotHost(){let e=this;return{get sessionID(){return e.sessionID},get cfg(){return e.cfg},get profile(){return e.profile},get runtimes(){return e.runtimes},get ports(){return e.ports},get serviceProfile(){return e.serviceProfile},get clientEnv(){return e.clientEnv},get proxy(){return e.proxy.instance},get mcp(){return e.mcp.instance},get mcpToken(){return e.mcp.token},get mcpDisabledTools(){return e.mcp.disabledTools},get identityCache(){return e.identity.identityCache},get serviceAccountStatus(){return e.identity.serviceAccountStatus},get credentialEntries(){return e.identity.credentialEntries},get detached(){return e.detached},get setupMode(){return e.setupMode},get restartRequired(){return e.restartRequired},get statsSeries(){return e.resources.series()},logs:{snapshot:()=>e.logs.snapshot()},tokens:{storeBackend:()=>e.tokens.storeBackend()}}}reloadHost(){let e=this;return{get cfg(){return e.cfg},set cfg(t){e.cfg=t},get setupMode(){return e.setupMode},set setupMode(t){e.setupMode=t},get configWatcher(){return e.configWatcher},set configWatcher(t){e.configWatcher=t},get watchTimer(){return e.watchTimer},set watchTimer(t){e.watchTimer=t},get restartRequired(){return e.restartRequired},set restartRequired(t){e.restartRequired=t},get fs(){return e.fs},get registry(){return e.registry},set registry(t){e.registry=t},get pluginMtimes(){return e.pluginMtimes},set pluginMtimes(t){e.pluginMtimes=t},get detector(){return e.detector},get bus(){return e.bus},get orchestrator(){return e.orchestrator},get runtimes(){return e.runtimes},get tokens(){return e.tokens},get logs(){return e.logs},get proxy(){return e.proxy.instance},persistState:()=>e.persistState(),log:(t,r,n)=>e.log(t,r,n),refreshIdentity:()=>e.refreshIdentity(),startProxy:()=>e.startProxy(),stopProxy:()=>e.stopProxy(),reload:()=>e.reload(),forgetService:(t)=>e.forgetService(t),syncServiceWatchers:()=>e.serviceWatchers.sync(e.cfg.services)}}recoverHost(){let e=this;return{get cfg(){return e.cfg},set cfg(t){e.cfg=t},get profile(){return e.profile},set profile(t){e.profile=t},get profileEnv(){return e.profileEnv},set profileEnv(t){e.profileEnv=t},get runtimes(){return e.runtimes},get ports(){return e.ports},get processMeta(){return e.processMeta},get serviceProfile(){return e.serviceProfile},get serviceProfileEnv(){return e.serviceProfileEnv},get orchestrator(){return e.orchestrator},get procs(){return e.procs},logs:{append:(t)=>e.logs.append(t)},get clock(){return e.clock},get tokens(){return e.tokens},get registry(){return e.registry},get proxy(){return e.proxy.instance},get tokenEP(){return e.proxy.tokenEndpoint},get boundTokenURL(){return e.proxy.boundTokenURL},get internalTok(){return e.internalTok},get bus(){return e.bus},inspectProcessFn:(t)=>e.inspectProcessFn(t),processAliveFn:(t)=>e.processAliveFn(t),serviceWorkDir:(t)=>e.serviceWorkDir(t),persistState:()=>e.persistState(),setState:(t,r,n,o,i)=>e.setState(t,r,n,o,i),log:(t,r,n)=>e.log(t,r,n)}}async run(){let e=ea(this.cfg.repoRoot);this.lock=this.acquireLockFn(this.cfg.repoRoot,e),this.rpc.removeStaleSocket(e),this.registry=await kc(this.cfg.plugins.map((t)=>t.path),this.cfg.repoRoot),this.pluginMtimes=Hu(this.cfg.plugins.map((t)=>t.path),this.cfg.repoRoot);for(let t of this.registry.loadErrors)this.log("devctl","ERROR",`plugin ${t.path} skipped: ${t.message}`);Nu(this.reloadHost()),ju(this.registry,this.cfg),Bu(this.registry,this.cfg),Uu(this.registry,this.cfg),await this.recoverSession(),this.serviceWatchers.sync(this.cfg.services),Wu(this.reloadHost()),this.persistState(),this.log("devctl","INFO",`supervisor started session=${this.sessionID}`),this.refreshIdentity(),this.resources.start(),await this.mcp.bootFromPreferences(),this.rpc.removeStaleSocket(e),await this.rpc.listen(e)}async dispatch(e,t){let r=np(t)?t:{};switch(e){case"ping":return{session:this.sessionID,version:jt,protocol:Yi};case"start":return this.commands.startService.execute({services:En(r.services),profile:typeof r.profile==="string"&&r.profile!==""?r.profile:void 0,detach:r.detach===!0,client_env:us(r.client_env)});case"stop":return await this.commands.stopService.execute(En(r.services)),null;case"restart":return await this.commands.restartService.execute(En(r.services),{cascade:r.cascade===!0,clientEnv:us(r.client_env)}),null;case"run_task":return this.runTask(typeof r.name==="string"?r.name:"",us(r.client_env)??{});case"exec":return this.execService(typeof r.service==="string"?r.service:"",En(r.command),us(r.client_env)??{},r.print_env===!0);case"auth_refresh":return await this.commands.refreshIdentity.execute(),this.snapshot().identity;case"status":return this.snapshot();case"logs":return await this.queryLogs({services:En(r.services),level:typeof r.level==="string"?r.level:"",search:typeof r.search==="string"?r.search:"",regex:r.regex===!0,source:typeof r.source==="string"?r.source:"",since:typeof r.since==="string"?r.since:"",until:typeof r.until==="string"?r.until:"",export:typeof r.export==="string"?r.export:""});case"logs_page":return await this.queryLogsPage({services:En(r.services),level:typeof r.level==="string"?r.level:"",search:typeof r.search==="string"?r.search:"",regex:r.regex===!0,source:typeof r.source==="string"?r.source:"",since:typeof r.since==="string"?r.since:"",until:typeof r.until==="string"?r.until:"",cursor:typeof r.cursor==="string"?r.cursor:void 0,direction:r.direction==="forward"?"forward":"backward",limit:typeof r.limit==="number"?r.limit:void 0});case"logs_stats":return await this.queryLogsFacets({services:En(r.services),level:typeof r.level==="string"?r.level:"",search:typeof r.search==="string"?r.search:"",regex:r.regex===!0,source:typeof r.source==="string"?r.source:"",since:typeof r.since==="string"?r.since:"",until:typeof r.until==="string"?r.until:""});case"proxy_start":return this.proxy.setSuppressed(!1),await this.commands.startProxy.execute(),null;case"proxy_stop":return this.proxy.setSuppressed(!0),await this.commands.stopProxy.execute(),null;case"mcp_start":{let o=typeof r.port==="number"?r.port:void 0;return await this.startMcp(o??Do(this.cfg.repoRoot).mcp_port),null}case"mcp_stop":return await this.stopMcp(),null;case"mcp_set_tools":{let o=Array.isArray(r.disabled)?r.disabled.filter((i)=>typeof i==="string"):[];return this.setMcpDisabledTools(o),{disabled_tools:[...this.mcp.disabledTools]}}case"reload":return this.commands.reloadConfig.execute();case"config_snapshot":return this.cfg;case"auth_invalidate":return this.tokens.invalidate(),null;case"shutdown":let n=typeof r.stop_services==="boolean"?r.stop_services:Ta(this.cfg.shutdown);return setTimeout(()=>{this.shutdown(n)},50),null;default:throw E(lt,`unknown method ${e}`)}}isDetached(){return this.detached}async start(e){return this.orchestrator.start(e)}lifecycleSession(){let e=this;return{get cfg(){return e.cfg},get profile(){return e.profile},set profile(t){e.profile=t},get profileEnv(){return e.profileEnv},set profileEnv(t){e.profileEnv=t},get detached(){return e.detached},set detached(t){e.detached=t},get proxySuppressed(){return e.proxy.isSuppressed},get runtimes(){return e.runtimes},get ports(){return e.ports},get clientEnv(){return e.clientEnv},get serviceProfile(){return e.serviceProfile},get serviceProfileEnv(){return e.serviceProfileEnv},healthCheckers:{lookup:(t)=>{let r=e.registry?.healthChecks.find((n)=>n.name.toLowerCase()===t.toLowerCase());return r?{check:async(n,o)=>{let i=await r.check(n,o);return{...i,status:i.status}}}:e.healthCheckers.lookup(t)}},logs:{append:(t)=>e.logs.append(t)},bus:e.bus,processMeta:e.processMeta,get containerPrefix(){return`devctl-${ar(e.cfg.repoRoot)}-`},prepareServiceIdentity:(t,r)=>e.identity.prepareServiceIdentity(t,r),resolveServiceExecution:(t,r,n,o,i,s)=>e.env.resolveServiceExecution(t,r,n,o,i,s),detectGoogle:(t)=>e.detectGoogleFn(t),startProxy:()=>e.startProxy(),fail:(t,r)=>e.fail(t,r),claimIfAlreadyUp:(t)=>e.claimIfAlreadyUp(t),assignPendingPorts:(t)=>e.assignPendingPorts(t),setState:(t,r,n,o,i)=>e.setState(t,r,n,o,i),persistState:()=>e.persistState(),log:(t,r,n)=>e.log(t,r,n),releasePorts:(t)=>e.releasePorts(t),forgetService:(t)=>e.forgetService(t),clearRestartRequired:(t)=>e.dropRestartRequired(t)}}async assignPendingPorts(e){try{let t=await hh(this.cfg,e,Object.fromEntries(this.ports));for(let[r,n]of Object.entries(t)){this.ports.set(r,n);let o=Object.entries(n).map(([i,s])=>`${i}=${s}`).join(", ");this.log(r,"INFO",`assigned ports: ${o||"none"}`)}}catch(t){if(t instanceof Ve&&t.service!=="")await this.fail(t.service,t);else this.log("devctl","ERROR",L(t));throw t}}async runTask(e,t){let r=this.cfg.tasks[e];if(!r)throw E(lt,`unknown task ${e}`);if(r.dependencies.length>0)await this.start({services:r.dependencies,client_env:t});let n={...yn(),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(bn(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:wn(this.cfg.shutdown)*1000,onLine:(s,a)=>this.logs.append({timestamp:this.clock.isoNow(),service:e,source:s,stream:s,level:"",message:a,pid:0})});if(i.code!==0)throw E(He,`${e} exited with code ${i.code}`);return i}serviceWorkDir(e){return this.env.serviceWorkDir(e)}async claimIfAlreadyUp(e){return uy(this.recoverHost(),e)}async recoverSession(){return py(this.recoverHost())}async stop(e){return this.orchestrator.stop(e)}forgetService(e){this.orchestrator.health.forget(e),this.runtimes.delete(e),this.ports.delete(e),this.processMeta.delete(e),this.env.forget(e)}async restart(e,t){let r=t?.cascade===!0?co(this.cfg,e):e;await this.orchestrator.restart(e,t),this.dropRestartRequired(r)}dropRestartRequired(e){if(e.length===0)return;let t=new Set(e);this.restartRequired=this.restartRequired.filter((r)=>!t.has(r))}async startProxy(){return this.proxy.start()}async stopProxy(){return this.proxy.stop()}async startMcp(e){return this.mcp.start(e)}setMcpDisabledTools(e){this.mcp.setDisabledTools(e)}async stopMcp(){return this.mcp.stop()}asMcpHost(){return{status:()=>this.commands.getServiceStatus.execute(),logsPage:(e)=>this.queryLogsPage(e),config:()=>this.cfg,validateConfigText:(e)=>Ui(this.cfg.repoRoot,this.cfg.configPath,e),start:(e)=>this.commands.startService.execute(e),stop:(e)=>this.commands.stopService.execute(e),restart:(e,t)=>this.commands.restartService.execute(e,{cascade:t}),reload:()=>this.commands.reloadConfig.execute(),doctor:async()=>{for(let e of Hr(this.cfg))await this.identity.probeServiceAccount(e);return this.commands.runDoctor.execute(this.cfg)},exec:(e,t,r)=>this.execService(e,t,void 0,r),runTask:(e)=>this.runTask(e,{}),startProxy:()=>this.startProxy(),stopProxy:()=>this.stopProxy()}}async reload(){return wy(this.reloadHost())}async shutdown(e){if(this.shuttingDown)return;if(this.shuttingDown=!0,this.persistState(),e)await this.stop([]);if(this.orchestrator.health.dispose(),await this.stopProxy(),await this.stopMcp(),this.configWatcher?.close(),this.serviceWatchers.close(),this.watchTimer)clearTimeout(this.watchTimer);this.resources.stop(),this.rpc.close(),this.lock?.release(),await this.logs.close()}snapshot(){return _y(this.snapshotHost())}async refreshIdentity(e){return this.identity.refreshIdentity(e)}async queryLogs(e){let t=await this.logs.query({services:e.services,level:e.level,search:e.search,regex:e.regex,source:e.source,since:e.since,until:e.until});if(e.export)await this.logs.exportTo(e.export,{services:e.services,level:e.level,search:e.search,regex:e.regex,source:e.source,since:e.since,until:e.until});return{events:t}}async queryLogsPage(e){return this.logs.queryPage({services:e.services,level:e.level,search:e.search,regex:e.regex,source:e.source,since:e.since,until:e.until},{cursor:e.cursor,direction:e.direction,limit:e.limit})}async queryLogsFacets(e){return this.logs.queryFacets({services:e.services,level:e.level,search:e.search,regex:e.regex,source:e.source,since:e.since,until:e.until})}subscribe(e){return this.bus.subscribe(e)}formatStatus(){return Ry(this.snapshot())}setState(e,t,r,n,o){let i=this.runtimes.get(e)??Qi(e);if(!cs(i.state,t))this.log(e,"WARN",`unusual lifecycle ${i.state} \u2192 ${t}`);i.state=t,i.health=r,i.pid=n,i.last_error=o;let s=t===je||t===pr||t===Br;if(i.startTime=s?this.processMeta.get(e)?.startTime.toISOString():void 0,!s)i.cpuPercent=void 0,i.memoryKB=void 0;this.runtimes.set(e,i),this.bus.publish(Se(Om,e,{state:t,health:r,pid:n}))}async releasePorts(e){let t=this.ports.get(e);if(!t)return;let r=this.cfg.services[e];if(r?.container){this.ports.delete(e);return}let n=this.processMeta.get(e);for(let o of Object.values(t)){let i=await zn(o);if(!i||i.pid===process.pid)continue;if(r){let s=await this.inspectProcessFn(i.pid);if(!(s!==void 0&&s.command!==""&&ss({args:n?.command??[...r.command.args],workDir:n?.cwd??this.serviceWorkDir(r),startTime:n?.startTime},s))){this.log(e,"WARN",`port ${o} is held by pid ${i.pid}, which does not match ${e}; leaving it running`);continue}}try{await wa(i),this.log(e,"INFO",`released port ${o} (pid ${i.pid})`)}catch(s){this.log(e,"WARN",L(s))}}this.ports.delete(e)}async fail(e,t){this.orchestrator.health.clearHealthWatch(e),this.orchestrator.health.clearRestartTimer(e),this.orchestrator.health.bumpGeneration(e),this.setState(e,dt,Tt,0,L(t));try{await this.procs.stop(e,wn(this.cfg.shutdown)*1000)}catch(r){this.log(e,"WARN",L(r))}this.bus.publish(Se(la,e,{error:L(t)})),this.log(e,"ERROR",L(t)),this.persistState()}log(e,t,r){this.logs.append({timestamp:this.clock.isoNow(),service:e,source:"devctl",level:t,message:r,pid:0})}persistState(){let e=[];for(let t of this.procs.all()){let r=this.processMeta.get(t.name);e.push({name:t.name,pid:t.pid,command:r?.command??t.args,cwd:r?.cwd??t.workDir,startTime:(r?.startTime??t.startTime).toISOString(),ports:this.ports.get(t.name)??{},profile:this.serviceProfile.get(t.name)??this.profile})}Rm(this.cfg.repoRoot,{session_id:this.sessionID,repo_root:this.cfg.repoRoot,profile:this.profile,processes:e})}}var Fy=500,_A=1e4,RA=2000,EA=new URL("./log-worker.ts",import.meta.url);class Ny{worker;bus;pending=new Map;nextId=1;stats={total:0,errors:0,counts:{}};dead=!1;readySettled=!1;ready;resolveReady=()=>{return};rejectReady=()=>{return};constructor(e,t,r={}){this.bus=t,this.ready=new Promise((n,o)=>{this.resolveReady=n,this.rejectReady=o}),this.worker=new Worker(r.script??EA,{name:"devctl-logs"}),this.worker.addEventListener("message",(n)=>{this.onMessage(n.data)}),this.worker.addEventListener("error",(n)=>{this.markDead(Error(n.message||"log worker failed"))}),this.post({type:"init",config:e})}waitUntilReady(e=Fy){if(this.dead)return Promise.reject(Error("log worker is not running"));if(this.readySettled)return Promise.resolve();return new Promise((t,r)=>{let n=setTimeout(()=>{this.markDead(Error("log worker init timed out"))},e);this.ready.then(()=>{clearTimeout(n),t()},(o)=>{clearTimeout(n),r(o)})})}append(e){if(this.dead)return;try{this.post({type:"append",event:e})}catch{}}async query(e){let t=await this.rpc({type:"query",filter:e});if(!Array.isArray(t))throw Error("log worker query returned a non-array");return t}async queryPage(e,t){let r=await this.rpc({type:"queryPage",filter:e,page:t});if(!TA(r))throw Error("log worker queryPage returned an unexpected payload");return r}async queryFacets(e){let t=await this.rpc({type:"queryFacets",filter:e});if(!AA(t))throw Error("log worker queryFacets returned an unexpected payload");return t}snapshot(){return this.stats}async exportTo(e,t){await this.rpc({type:"exportTo",path:e,filter:t})}setParsers(e,t){if(this.dead)return;this.post({type:"setPluginPaths",paths:[...t??[]]})}setSecrets(e,t){if(this.dead)return;this.post({type:"setSecrets",extraMarkers:e,extraPatterns:t})}async close(){if(this.dead)return;try{await this.rpc({type:"close"},RA)}catch{}finally{this.markDead(Error("log worker closed"))}}rpc(e,t=_A){this.assertAlive();let r=this.nextId;this.nextId+=1;let n={...e,id:r};return new Promise((o,i)=>{let s=setTimeout(()=>{this.markDead(Error(`log worker timed out (${e.type})`))},t);this.pending.set(r,{resolve:o,reject:i,timer:s});try{this.post(n)}catch(a){clearTimeout(s),this.pending.delete(r),i(a instanceof Error?a:Error(String(a)))}})}post(e){this.assertAlive(),this.worker.postMessage(e)}onMessage(e){if(e.type==="ready"){this.settleReady();return}if(e.type==="appended"){this.stats=e.stats,this.bus?.publish(Se(Io,e.event.service,{event:e.event,level:e.event.level}));return}let t=this.pending.get(e.id);if(!t)return;if(this.pending.delete(e.id),clearTimeout(t.timer),e.type==="error"){t.reject(Error(e.error));return}t.resolve(e.result)}settleReady(){if(this.readySettled||this.dead)return;this.readySettled=!0,this.resolveReady()}assertAlive(){if(this.dead)throw Error("log worker is not running")}markDead(e){if(this.dead)return;if(this.dead=!0,!this.readySettled)this.readySettled=!0,this.rejectReady(e);this.rejectAll(e);try{this.worker.terminate()}catch{}}rejectAll(e){for(let t of this.pending.values())clearTimeout(t.timer),t.reject(e);this.pending.clear()}}function TA(e){if(e===null||Array.isArray(e)||!("events"in e))return!1;return Array.isArray(e.events)}function AA(e){return e!==null&&!Array.isArray(e)&&"byService"in e}function $y(e,t,r){return nh(new Rd(e.max,t,r,e.persist,e.directory,e.sessionID,e.retentionDays,e.maxSessionLogs))}async function Hy(e,t,r,n={}){if(n.standalone??Bun.isStandaloneExecutable===!0)return{logs:$y(e,t,r),usingWorker:!1};try{let i=new Ny(e,t,{script:n.script});return await i.waitUntilReady(n.initTimeoutMs??Fy),{logs:i,usingWorker:!0}}catch{return{logs:$y(e,t,r),usingWorker:!1}}}ni();import{createServer as tL}from"http";te();fs();var rL="2025-03-26",cb="2.0",ob=-32700,ib=-32600,nL=-32601,sb=-32602,oL=-32603;function iL(e){return e!==""&&Cn(e)}class lp{server;running=!1;addr="";boundPort=0;opts;constructor(e){this.opts=e}disabled(){return this.opts.disabledTools?.()??[]}address(){return this.addr}listenPort(){return this.boundPort}isRunning(){return this.running}emit(e,t){this.opts.onEvent?.(e,t)}start(){if(!iL(this.opts.host))return Promise.reject(E(lt,`refusing to bind MCP to ${this.opts.host}`));return new Promise((e,t)=>{this.server=tL((r,n)=>{this.serve(r,n)}),this.server.on("error",(r)=>t(Pe(lt,`unable to listen on ${this.opts.host}:${this.opts.port}`,r))),this.server.listen(this.opts.port,this.opts.host,()=>{let r=this.server?.address();this.boundPort=typeof r==="object"&&r?r.port:this.opts.port,this.addr=`${this.opts.host}:${this.boundPort}`,this.running=!0,this.emit("INFO",`listening on ${this.addr}`),e()})})}stop(){let e=this.server,t=this.running;if(!e)return this.running=!1,Promise.resolve();return new Promise((r)=>{e.close(()=>{if(this.running=!1,this.addr="",this.boundPort=0,this.server=void 0,t)this.emit("INFO","stopped");r()})})}async serve(e,t){if(e.method==="OPTIONS"){t.writeHead(204,gs()),t.end();return}if(!this.authorized(e)){this.emit("WARN",`rejected unauthorized request from ${ab(e)}`),ii(t,401,{error:"unauthorized"});return}if(aL(e)!=="/mcp"){ii(t,404,{error:"not found"});return}if(e.method==="GET"||e.method==="DELETE"){t.writeHead(405,{Allow:"POST, OPTIONS",...gs()}),t.end();return}if(e.method!=="POST"){t.writeHead(405,{Allow:"POST, OPTIONS",...gs()}),t.end();return}let n;try{n=await cL(e)}catch(a){ii(t,400,ms(null,ob,a instanceof Error?a.message:"invalid body"));return}let o;try{o=JSON.parse(n)}catch{ii(t,400,ms(null,ob,"parse error"));return}let i=ab(e);if(Array.isArray(o)){let a=[];for(let c of o){let l=await this.dispatch(c,i);if(l!==void 0)a.push(l)}ii(t,200,a);return}let s=await this.dispatch(o,i);if(s===void 0){t.writeHead(202,gs()),t.end();return}ii(t,200,s)}authorized(e){if(this.opts.token==="")return!0;return(e.headers.authorization??"")===`Bearer ${this.opts.token}`}async dispatch(e,t){if(!Ec(e))return ms(null,ib,"invalid request");let r=e,n=r.id??null,o=r.id===void 0;if(r.method===void 0||r.method==="")return o?void 0:ms(n,ib,"missing method");try{let i=await this.handleMethod(r.method,Ec(r.params)?r.params:{},t);if(o)return;return{jsonrpc:cb,id:n,result:i}}catch(i){if(o)return;let s=i instanceof hs?i.code:oL,a=i instanceof Error?i.message:"internal error";return this.emit("WARN",`rpc error method=${r.method} addr=${t}: ${a}`),ms(n,s,a)}}async handleMethod(e,t,r){switch(e){case"initialize":{let n=sL(t);return this.emit("INFO",`client connected addr=${r}${n?` (${n})`:""}`),{protocolVersion:rL,capabilities:{tools:{},resources:{}},serverInfo:{name:"devctl",version:jt}}}case"notifications/initialized":case"ping":return{};case"tools/list":return{tools:Jy(this.disabled()).map((n)=>({name:n.name,description:n.description,inputSchema:n.inputSchema}))};case"tools/call":return this.callTool(t);case"resources/list":return{resources:ap.map((n)=>({uri:n,name:n.replace("devctl://",""),mimeType:"application/json"}))};case"resources/read":return this.readResource(t);default:throw new hs(nL,`method not found: ${e}`)}}async callTool(e){let t=typeof e.name==="string"?e.name:"";if(t==="")throw new hs(sb,"tool name is required");if(Rc(t)&&!An(t,this.disabled())){let o=`tool ${t} is disabled in devctl's MCP settings`;return this.emit("WARN",`tool call rejected name=${t}: disabled`),{content:[{type:"text",text:o}],isError:!0}}let r=Ec(e.arguments)?e.arguments:{},n=Date.now();try{let o=await tb(this.opts.hostApi,t,r);return this.emit("INFO",`tool call name=${t} duration=${Date.now()-n}ms`),{content:[{type:"text",text:JSON.stringify(o,null,2)}]}}catch(o){let i=o instanceof Error?o.message:"tool failed";return this.emit("WARN",`tool call failed name=${t} duration=${Date.now()-n}ms: ${i}`),{content:[{type:"text",text:i}],isError:!0}}}async readResource(e){let t=typeof e.uri==="string"?e.uri:"";if(!rb(t))throw new hs(sb,`unknown resource ${t}`);let r=await nb(this.opts.hostApi,t);return this.emit("INFO",`resource read uri=${t}`),{contents:[{uri:t,mimeType:"application/json",text:JSON.stringify(r,null,2)}]}}}function sL(e){let t=e.clientInfo;if(!Ec(t))return"";let r=typeof t.name==="string"?t.name:"",n=typeof t.version==="string"?t.version:"";if(r==="")return"";return n===""?r:`${r} ${n}`}function ab(e){return e.socket.remoteAddress??"unknown"}class hs extends Error{code;constructor(e,t){super(t);this.code=e}}function gs(){return{"Access-Control-Allow-Origin":"*","Access-Control-Allow-Headers":"Authorization, Content-Type, Accept, MCP-Protocol-Version, Mcp-Session-Id","Access-Control-Allow-Methods":"POST, OPTIONS"}}function ii(e,t,r){let n=JSON.stringify(r);e.writeHead(t,{"Content-Type":"application/json","Content-Length":Buffer.byteLength(n),...gs()}),e.end(n)}function ms(e,t,r){return{jsonrpc:cb,id:e,error:{code:t,message:r}}}function aL(e){let t=e.url??"/",r=t.indexOf("?");return r>=0?t.slice(0,r):t}function cL(e){return new Promise((t,r)=>{let n=[];e.on("data",(o)=>{n.push(o)}),e.on("end",()=>{t(Buffer.concat(n).toString("utf8"))}),e.on("error",r)})}function Ec(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}fs();var uL=(e)=>new lp({host:"127.0.0.1",...e});async function pL(e,t={}){let r=t.clock??Qa,n=t.fs??sy,o=t.processes??new Eu,i=t.bus??new ud(2048),s=t.tokens??new ao(e.auth.refresh_threshold_seconds*1000,_n(),i,void 0,r),a=new Iu(o,r),c=rd(),l=new mr(e.secrets.extra_markers,e.secrets.extra_patterns),d=Bun.isStandaloneExecutable===!0,{logs:u,usingWorker:f}=await Hy({max:e.logs.max_memory_events,persist:e.logs.persistence.enabled,directory:e.logs.persistence.directory,sessionID:c,retentionDays:e.logs.persistence.retention_days,maxSessionLogs:e.logs.persistence.max_session_logs,extraMarkers:e.secrets.extra_markers,extraPatterns:e.secrets.extra_patterns},i,l,{standalone:d});if(!f&&!d)u.append({timestamp:r.isoNow(),service:"devctl",source:"devctl",level:"WARN",message:"log worker failed to start; using in-process log store",pid:0});return{supervisor:new ip(e,{healthCheckers:t.healthCheckers??Nv([]),detectGoogle:t.detectGoogle??io,tokens:s,inspectProcess:oy,processAlive:Qt,acquireLock:Em,socketExists:lL,unlinkSocket:dL,procs:o,orchestrator:a,clock:r,fs:n,bus:i,logs:u,detector:l,sessionID:c,createMcpListener:t.createMcpListener??uL,isKnownTool:Rc,createCommands:(m)=>fv(m,rc(Yo({tokens:s})),a)}),orchestrator:a,clock:r,fs:n}}async function lb(e,t){let r=to(e,t),{supervisor:n}=await pL(r),o=!1,i=()=>{if(o)return;o=!0,n.shutdown(Ta(r.shutdown)).finally(()=>process.exit(0))};process.on("SIGINT",i),process.on("SIGTERM",i),await n.run()}ht();te();Bf();import{Command as GO}from"commander";var Jk=["zsh","bash","fish"];function Xk(e){if(e==="zsh")return`#compdef devctl
|
|
2278
|
+
`}var SA=4000;class tp{cache;accounts=new Map;entries=[];deps;constructor(e){this.deps=e,this.cache=Ay(e.cfg()),this.deps.bus.subscribe((t)=>{let r=t.payload??{},n=t.type===$o?`token refreshed identity=${String(r.identity??"")}`:t.type===qn?`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===qn?"WARN":"INFO",message:n,pid:0}),t.type===$o||t.type===qn)this.syncCredentialEntries()},[$o,qn,fd])}get identityCache(){return this.cache}get serviceAccountStatus(){return this.accounts}get credentialEntries(){return this.entries}async prepareServiceIdentity(e,t){let r=Xa(t.identity);if(r.kind!=="none")try{if(r=await Hg(t.identity,()=>Wg(this.deps.cfg().google.project_id),this.deps.identityProviders()),r.kind==="service_account")await this.deps.tokens.get(_n(r),"",[]),this.accounts.set(r.serviceAccount,"available")}catch(n){if(r.kind==="service_account")this.accounts.set(r.serviceAccount,"unavailable");if(CA(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 Br(t))await this.probeServiceAccount(i);let{service_accounts:n,service_account_status:o}=ep(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(ye(fd,"",{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 kA(this.deps.tokens.refresh(`sa:${e}`,"",[]),SA),t="available"}catch{t="unavailable"}return this.accounts.set(e,t),t}}function kA(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 CA(e){return e.capabilities.some((t)=>["google_api","iap","service_identity"].includes(t))}te();te();np();var TA=600,AA=65535;async function Ny(e,t){let r=Number.isInteger(t)&&(t??0)>0?t:fo(e);for(let n=0;n<TA;n+=1){let o=r+n;if(o>AA)break;if(await Uo(o))return o}throw P(Y,`no free MCP port starting at ${r}`)}class op{listener;disabled=[];token;deps;constructor(e){this.deps=e,this.token=Of(e.repoRoot())}get instance(){return this.listener}get disabledTools(){return this.disabled}async bootFromPreferences(){let e=Mo(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 Ny(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()}}dt();import*as ho from"http2";import{randomBytes as LA}from"crypto";te();Tr();var ip="14",DA="16",IA="1",Hy=new Set(["host","connection","keep-alive","transfer-encoding","upgrade","proxy-connection","authorization"]);class sp{route;tokens;logs;bus;detector;server;client;running=!1;addr="";requests=new _c;constructor(e,t,r,n,o){this.route=e;this.tokens=t;this.logs=r;this.bus=n;this.detector=o}address(){return this.addr}isRunning(){return this.running}stats(){return this.requests.stats()}start(){let e=this.route.listen?.host||"127.0.0.1",t=this.route.listen?.port??0;if(!Er(e))return Promise.reject(P(zt,`refusing to bind grpc route ${this.route.name} to ${e}`));if(t===0)return Promise.reject(P(zt,`grpc route ${this.route.name} requires listen.port`));return new Promise((r,n)=>{let o=ho.createServer();o.on("stream",(i,s)=>void this.serve(i,s)),o.on("sessionError",()=>{}),o.on("error",(i)=>{if(this.running){this.log("ERROR",`grpc route ${this.route.name} listener error: ${i.message}`);return}n(Se(zt,`grpc route ${this.route.name} unable to listen on ${e}:${t}`,i))}),o.listen(t,e,()=>{this.server=o,this.running=!0,this.addr=`${e}:${t}`,this.log("INFO",`grpc route ${this.route.name} listening on ${this.addr} \u2192 ${this.route.upstream.url}`),r()})})}stop(){let e=this.server;if(this.client?.close(),this.client=void 0,!e)return Promise.resolve();return new Promise((t)=>{e.close(()=>{this.running=!1,t()})})}upstream(){if(this.client&&!this.client.closed&&!this.client.destroyed)return this.client;let e=ho.connect(this.route.upstream.url);return e.on("error",()=>{if(this.client===e)this.client=void 0}),e.on("goaway",()=>{if(this.client===e)this.client=void 0}),e.on("close",()=>{if(this.client===e)this.client=void 0}),this.client=e,e}async serve(e,t){let r=Date.now(),n=LA(8).toString("hex"),o=String(t[":path"]??"/"),i=an(this.route.auth),s=_n(i),a=!1,c=(f,h,v)=>{if(a)return;a=!0;let b=Date.now()-r,g=this.detector?this.detector.redactText(o):o,C=v??(h!=="0"?`grpc-status ${h}`:void 0);this.requests.record({timestamp:new Date().toISOString(),requestId:n,method:"POST",path:g,route:this.route.name,identity:s,status:f,durationMs:b,error:C}),this.log(C?"WARN":"INFO",`grpc ${o} route=${this.route.name} identity=${s} grpc-status=${h} duration=${b}ms${C?` error=${C}`:""}`,n,s),this.bus?.publish(ye(Di,this.route.name,{status:f,request_id:n,duration:b,identity:s}))},l=(f,h)=>{try{e.respond({":status":200,"content-type":"application/grpc","grpc-status":f,"grpc-message":h},{endStream:!0})}catch{e.close(ho.constants.NGHTTP2_INTERNAL_ERROR)}c(200,f,h)},d;try{d=this.buildUpstreamHeaders(t),await ni(this.route,d,this.tokens)}catch(f){l(DA,f instanceof Error?f.message:"token injection failed");return}let u;try{u=this.upstream().request(d)}catch(f){l(ip,f instanceof Error?f.message:"upstream unavailable");return}let m=!1,p={};u.on("response",(f)=>{if(m)return;if(m=!0,f["grpc-status"]!==void 0){e.respond(this.frontResponseHeaders(f),{endStream:!0}),c(Number(f[":status"]??200),String(f["grpc-status"]));return}e.respond(this.frontResponseHeaders(f),{waitForTrailers:!0}),e.on("wantTrailers",()=>{try{e.sendTrailers(this.sanitizeTrailers(p))}catch{}c(Number(f[":status"]??200),String(p["grpc-status"]??"0"))}),u.pipe(e,{end:!1}),u.on("end",()=>{try{e.end()}catch{}})}),u.on("trailers",(f)=>{p=f}),u.on("error",(f)=>{if(!m)l(ip,f.message);else c(502,ip,f.message),e.close(ho.constants.NGHTTP2_INTERNAL_ERROR)}),e.on("error",()=>u.destroy()),e.on("close",()=>{if(!a)c(499,IA,"client closed before completion"),u.destroy()}),e.pipe(u)}buildUpstreamHeaders(e){let t={};for(let[n,o]of Object.entries(e)){if(n.startsWith(":")||Hy.has(n.toLowerCase())||o===void 0)continue;t[n]=Array.isArray(o)?o.join(", "):String(o)}let r=new URL(this.route.upstream.url);return t[":method"]=String(e[":method"]??"POST"),t[":path"]=String(e[":path"]??"/"),t[":scheme"]=r.protocol==="https:"?"https":"http",t[":authority"]=r.host,t}frontResponseHeaders(e){let t={":status":e[":status"]??200};for(let[r,n]of Object.entries(e)){if(r.startsWith(":")||Hy.has(r.toLowerCase())||n===void 0)continue;t[r]=n}return t}sanitizeTrailers(e){let t={};for(let[r,n]of Object.entries(e)){if(r.startsWith(":")||n===void 0)continue;t[r]=n}if(t["grpc-status"]===void 0)t["grpc-status"]="0";return t}log(e,t,r="",n=""){this.logs?.append({timestamp:new Date().toISOString(),service:"proxy",source:"proxy",level:e,message:t,pid:0,request_id:r,identity:n})}}class ap{server;tokenEP;grpc=[];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 ju(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 Bu(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`;for(let t of e.proxy.routes.filter(Ko)){let r=new sp(t,this.deps.tokens,this.deps.logs,this.deps.bus,this.deps.detector);await r.start(),this.grpc.push(r)}this.deps.persistState()}async stop(){await this.server?.stop(),await this.tokenEP?.stop(),await Promise.all(this.grpc.map((e)=>e.stop())),this.grpc=[],this.deps.persistState()}}At();var MA=3000,jy=5000,By=60;class cp{timer;cpu=[];mem=[];lastAt=0;deps;constructor(e){this.deps=e}start(){if(this.timer)return;this.timer=setInterval(()=>void this.poll(),MA)}stop(){if(this.timer)clearInterval(this.timer),this.timer=void 0}series(){return{interval_ms:jy,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===mr||r.state===Ur))e.push(r.pid);if(e.length===0)return;let t=await uy(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<jy)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>By)this.cpu.shift();if(this.mem.length>By)this.mem.shift()}}function lp(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}function An(e){if(!Array.isArray(e))return[];return e.filter((t)=>typeof t==="string")}function vs(e){if(!lp(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 OA}from"net";var $A=2000;class dp{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=OA((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>=$A){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=yh(n);t({id:r.id,error:o.error,kind:o.kind,hint:o.hint,service:o.service})}}}te();Tr();At();class up{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=dd(),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 ap({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 Xu({cfg:()=>this.cfg,ports:()=>this.ports,userEmail:()=>this.identity.identityCache.user,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 op({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 cp({clock:this.clock,runtimes:()=>this.runtimes}),this.identity=new tp({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 dp({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,os(r));this.orchestrator.bind(this.lifecycleSession()),this.commands=t.createCommands(this),this.serviceWatchers=new zu({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=sa(this.cfg.repoRoot);this.lock=this.acquireLockFn(this.cfg.repoRoot,e),this.rpc.removeStaleSocket(e),this.registry=await Ec(this.cfg.plugins.map((t)=>t.path),this.cfg.repoRoot),this.pluginMtimes=Wu(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}`);Uu(this.reloadHost()),Gu(this.registry,this.cfg),Ku(this.registry,this.cfg),qu(this.registry,this.cfg),await this.recoverSession(),this.serviceWatchers.sync(this.cfg.services),Vu(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=lp(t)?t:{};switch(e){case"ping":return{session:this.sessionID,version:jt,protocol:ts};case"start":return this.commands.startService.execute({services:An(r.services),profile:typeof r.profile==="string"&&r.profile!==""?r.profile:void 0,detach:r.detach===!0,client_env:vs(r.client_env)});case"stop":return await this.commands.stopService.execute(An(r.services)),null;case"restart":return await this.commands.restartService.execute(An(r.services),{cascade:r.cascade===!0,clientEnv:vs(r.client_env)}),null;case"run_task":return this.runTask(typeof r.name==="string"?r.name:"",vs(r.client_env)??{});case"exec":return this.execService(typeof r.service==="string"?r.service:"",An(r.command),vs(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:An(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:An(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:An(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??Mo(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:Oa(this.cfg.shutdown);return setTimeout(()=>{this.shutdown(n)},50),null;default:throw P(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-${cr(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 wh(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 P(lt,`unknown task ${e}`);if(r.dependencies.length>0)await this.start({services:r.dependencies,client_env:t});let n={...xn(),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 P(Bo,`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 P(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(wn(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:kn(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 P(He,`${e} exited with code ${i.code}`);return i}serviceWorkDir(e){return this.env.serviceWorkDir(e)}async claimIfAlreadyUp(e){return vy(this.recoverHost(),e)}async recoverSession(){return yy(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)=>zi(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 Br(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 Ry(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 Ly(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 Dy(this.snapshot())}setState(e,t,r,n,o){let i=this.runtimes.get(e)??os(e);if(!fs(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===mr||t===Ur;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(ye(Bf,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 Jn(o);if(!i||i.pid===process.pid)continue;if(r){let s=await this.inspectProcessFn(i.pid);if(!(s!==void 0&&s.command!==""&&ps({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 _a(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,ut,Tt,0,L(t));try{await this.procs.stop(e,kn(this.cfg.shutdown)*1000)}catch(r){this.log(e,"WARN",L(r))}this.bus.publish(ye(ha,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})}If(this.cfg.repoRoot,{session_id:this.sessionID,repo_root:this.cfg.repoRoot,profile:this.profile,processes:e})}}var Wy=500,FA=1e4,NA=2000,HA=new URL("./log-worker.ts",import.meta.url);class Gy{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??HA,{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=Wy){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(!jA(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(!BA(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"},NA)}catch{}finally{this.markDead(Error("log worker closed"))}}rpc(e,t=FA){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(ye(Oo,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 jA(e){if(e===null||Array.isArray(e)||!("events"in e))return!1;return Array.isArray(e.events)}function BA(e){return e!==null&&!Array.isArray(e)&&"byService"in e}function Uy(e,t,r){return lh(new Ld(e.max,t,r,e.persist,e.directory,e.sessionID,e.retentionDays,e.maxSessionLogs))}async function Ky(e,t,r,n={}){if(n.standalone??Bun.isStandaloneExecutable===!0)return{logs:Uy(e,t,r),usingWorker:!1};try{let i=new Gy(e,t,{script:n.script});return await i.waitUntilReady(n.initTimeoutMs??Wy),{logs:i,usingWorker:!0}}catch{return{logs:Uy(e,t,r),usingWorker:!1}}}si();import{createServer as pL}from"http";te();bs();var mL="2025-03-26",fb="2.0",db=-32700,ub=-32600,fL=-32601,pb=-32602,hL=-32603;function gL(e){return e!==""&&Er(e)}class hp{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(!gL(this.opts.host))return Promise.reject(P(lt,`refusing to bind MCP to ${this.opts.host}`));return new Promise((e,t)=>{this.server=pL((r,n)=>{this.serve(r,n)}),this.server.on("error",(r)=>t(Se(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,Ss()),t.end();return}if(!this.authorized(e)){this.emit("WARN",`rejected unauthorized request from ${mb(e)}`),ci(t,401,{error:"unauthorized"});return}if(yL(e)!=="/mcp"){ci(t,404,{error:"not found"});return}if(e.method==="GET"||e.method==="DELETE"){t.writeHead(405,{Allow:"POST, OPTIONS",...Ss()}),t.end();return}if(e.method!=="POST"){t.writeHead(405,{Allow:"POST, OPTIONS",...Ss()}),t.end();return}let n;try{n=await bL(e)}catch(a){ci(t,400,xs(null,db,a instanceof Error?a.message:"invalid body"));return}let o;try{o=JSON.parse(n)}catch{ci(t,400,xs(null,db,"parse error"));return}let i=mb(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)}ci(t,200,a);return}let s=await this.dispatch(o,i);if(s===void 0){t.writeHead(202,Ss()),t.end();return}ci(t,200,s)}authorized(e){if(this.opts.token==="")return!0;return(e.headers.authorization??"")===`Bearer ${this.opts.token}`}async dispatch(e,t){if(!Ic(e))return xs(null,ub,"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:xs(n,ub,"missing method");try{let i=await this.handleMethod(r.method,Ic(r.params)?r.params:{},t);if(o)return;return{jsonrpc:fb,id:n,result:i}}catch(i){if(o)return;let s=i instanceof ws?i.code:hL,a=i instanceof Error?i.message:"internal error";return this.emit("WARN",`rpc error method=${r.method} addr=${t}: ${a}`),xs(n,s,a)}}async handleMethod(e,t,r){switch(e){case"initialize":{let n=vL(t);return this.emit("INFO",`client connected addr=${r}${n?` (${n})`:""}`),{protocolVersion:mL,capabilities:{tools:{},resources:{}},serverInfo:{name:"devctl",version:jt}}}case"notifications/initialized":case"ping":return{};case"tools/list":return{tools:rb(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:mp.map((n)=>({uri:n,name:n.replace("devctl://",""),mimeType:"application/json"}))};case"resources/read":return this.readResource(t);default:throw new ws(fL,`method not found: ${e}`)}}async callTool(e){let t=typeof e.name==="string"?e.name:"";if(t==="")throw new ws(pb,"tool name is required");if(Dc(t)&&!Dn(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=Ic(e.arguments)?e.arguments:{},n=Date.now();try{let o=await ab(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(!cb(t))throw new ws(pb,`unknown resource ${t}`);let r=await lb(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 vL(e){let t=e.clientInfo;if(!Ic(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 mb(e){return e.socket.remoteAddress??"unknown"}class ws extends Error{code;constructor(e,t){super(t);this.code=e}}function Ss(){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 ci(e,t,r){let n=JSON.stringify(r);e.writeHead(t,{"Content-Type":"application/json","Content-Length":Buffer.byteLength(n),...Ss()}),e.end(n)}function xs(e,t,r){return{jsonrpc:fb,id:e,error:{code:t,message:r}}}function yL(e){let t=e.url??"/",r=t.indexOf("?");return r>=0?t.slice(0,r):t}function bL(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 Ic(e){return typeof e==="object"&&e!==null&&!Array.isArray(e)}bs();var SL=(e)=>new hp({host:"127.0.0.1",...e});async function kL(e,t={}){let r=t.clock??nc,n=t.fs??py,o=t.processes??new Du,i=t.bus??new hd(2048),s=t.tokens??new co(e.auth.refresh_threshold_seconds*1000,En(),i,void 0,r),a=new Fu(o,r),c=ad(),l=new hr(e.secrets.extra_markers,e.secrets.extra_patterns),d=Bun.isStandaloneExecutable===!0,{logs:u,usingWorker:m}=await Ky({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(!m&&!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 up(e,{healthCheckers:t.healthCheckers??Gv([]),detectGoogle:t.detectGoogle??so,tokens:s,inspectProcess:dy,processAlive:Zt,acquireLock:Mf,socketExists:xL,unlinkSocket:wL,procs:o,orchestrator:a,clock:r,fs:n,bus:i,logs:u,detector:l,sessionID:c,createMcpListener:t.createMcpListener??SL,isKnownTool:Dc,createCommands:(f)=>bv(f,ac(Xo({tokens:s})),a)}),orchestrator:a,clock:r,fs:n}}async function hb(e,t){let r=no(e,t),{supervisor:n}=await kL(r),o=!1,i=()=>{if(o)return;o=!0,n.shutdown(Oa(r.shutdown)).finally(()=>process.exit(0))};process.on("SIGINT",i),process.on("SIGTERM",i),await n.run()}dt();te();qm();import{Command as t$}from"commander";var rC=["zsh","bash","fish"];function nC(e){if(e==="zsh")return`#compdef devctl
|
|
2281
2281
|
_devctl() {
|
|
2282
2282
|
local -a opts
|
|
2283
2283
|
local line="\${words[*]}"
|
|
@@ -2297,41 +2297,41 @@ complete -F _devctl devctl
|
|
|
2297
2297
|
devctl __complete (commandline -cp)
|
|
2298
2298
|
end
|
|
2299
2299
|
complete -c devctl -f -a "(__devctl_complete)"
|
|
2300
|
-
`;throw Error(`unsupported shell "${e}"; use ${
|
|
2300
|
+
`;throw Error(`unsupported shell "${e}"; use ${rC.join(", ")}`)}function Vm(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 Yr(c,n);if(i==="start"||i==="stop"||i==="restart"||i==="logs"){if(n==="--profile"||o[o.length-1]==="--profile")return Yr(a,n==="--profile"?"":n);return Yr([...s,"--profile","--detach","--json","--since","--until"],n)}if(i==="completion")return Yr([...rC].sort(),n);if(i==="run")return Yr([...Object.keys(t.tasks).sort(),"--json"],n);if(i==="exec")return Yr([...s,"--print-env","--reveal","--json"],n);if(i==="auth")return Yr(["status","login","logout","refresh"],n);if(i==="config")return Yr(["validate","show","diff"],n);if(i==="proxy")return Yr(["status","start","stop"],n);return Yr(c,n)}function Yr(e,t){if(t==="")return e;return e.filter((r)=>r.startsWith(t))}function we(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 oC(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("",we(e)).google.project_id}catch{o=""}let i=await t.detectGoogle(o);if(n.json){T(JSON.stringify(i,null,2)+`
|
|
2301
2301
|
`);return}if(T(`User: ${i.userEmail||"(unknown)"}
|
|
2302
2302
|
`),T(`Project: ${i.projectID||"(unset)"}
|
|
2303
2303
|
`),i.projectID!=="")T(`Source: ${i.projectSource}
|
|
2304
2304
|
`);T(`ADC: ${i.adcAvailable}
|
|
2305
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("",
|
|
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("",we(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
2307
|
`);return}if(T(`refreshed credentials expire ${i.expiresAt.toISOString()}
|
|
2308
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();
|
|
2309
|
+
`)}finally{await o.close()}})}te();At();xm();import{mkdirSync as VO}from"fs";import{basename as zO,dirname as YO}from"path";import{stringify as JO}from"yaml";ja();function iC(e,t){e.command("reload").description("reload configuration").action(async()=>{let r=await t.openController("",we(e),!0);try{let n=await r.reload();if(n.restart_required.length===0)T(`configuration reloaded
|
|
2310
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: ${
|
|
2312
|
-
`)}finally{await r.close()}})}function
|
|
2311
|
+
`);if(n.supervisor_restart_required&&n.supervisor_restart_required.length>0)T(`note: ${uc(n.supervisor_restart_required)}
|
|
2312
|
+
`)}finally{await r.close()}})}function sC(e,t){let r=e.command("config");r.command("validate").option("--json").action((n)=>{try{let o=t.load("",we(e)),i=t.validate(o);if(n.json){T(JSON.stringify({valid:i.length===0,issues:i},null,2)+`
|
|
2313
2313
|
`);return}if(i.length>0)throw Error(i.join(`
|
|
2314
2314
|
`));T(`configuration is valid
|
|
2315
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("",
|
|
2316
|
+
`);return}throw o}}),r.command("diff").description("show where effective configuration values came from").option("--json").action((n)=>{let o=t.load("",we(e)),i=t.configDiff(o);if(n.json){T(JSON.stringify({entries:i},null,2)+`
|
|
2317
2317
|
`);return}for(let s of i){T(`${s.path} = ${JSON.stringify(s.value)}
|
|
2318
2318
|
`),T(` winner: ${s.layer} (${s.source})
|
|
2319
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("",
|
|
2321
|
-
`);return}T(
|
|
2320
|
+
`)}}),r.command("show").option("--json").action((n)=>{let o=t.load("",we(e));if(n.json){T(JSON.stringify(o,null,2)+`
|
|
2321
|
+
`);return}T(JO(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}=Gd("",we(e)),c=Rl(i,zO(s)),l=t.validateConfigText(s,a,c.yaml);if(T(_l(c)),l.length>0){if(T(`validation:
|
|
2322
2322
|
${l.map((d)=>` ${d}`).join(`
|
|
2323
2323
|
`)}
|
|
2324
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`)
|
|
2326
|
-
`)})}
|
|
2325
|
+
`);return}if(t.fileExists(a))throw Error(`configuration already exists at ${a}; not overwriting`);VO(YO(a),{recursive:!0}),t.writeTextFile(a,c.yaml),T(`wrote ${a}
|
|
2326
|
+
`)})}si();At();import{setTimeout as XO}from"timers/promises";function cC(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("",we(e),!0);try{let s=await i.execService(r,n,o.printEnv===!0);if(s.environment){let a=o.reveal?s.environment:new hr(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
2327
|
`);else for(let c of Object.keys(a).sort())T(`${c}=${a[c]}
|
|
2328
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
|
|
2330
|
-
`);else{if(i.stdout)T(i.stdout);if(i.stderr)process.stderr.write(i.stderr)}}finally{await o.close()}})}function
|
|
2331
|
-
`);return}if(T(
|
|
2332
|
-
`)}finally{await o.close()}})}function
|
|
2333
|
-
`)}finally{await o.close()}})}function
|
|
2334
|
-
`)}finally{await o.close()}})}async function
|
|
2329
|
+
`);else{if(s.stdout)T(s.stdout);if(s.stderr)process.stderr.write(s.stderr)}}finally{await i.close()}})}function lC(e,t){e.command("run").argument("<task>","task to run").option("--json","machine-readable output").action(async(r,n)=>{let o=await t.openController("",we(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 dC(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("",we(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(lv(i)),n.detach)T(`detached; services continue running
|
|
2332
|
+
`)}finally{await o.close()}})}function uC(e,t){e.command("stop").argument("[services...]","services to stop").option("--json","machine-readable output").action(async(r,n)=>{let o=await t.openController("",we(e),!0);try{if(await o.stop(r),n.json)T(JSON.stringify({stopped:r},null,2)+`
|
|
2333
|
+
`)}finally{await o.close()}})}function pC(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("",we(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 aC(e,t,r){let{repoRoot:n,client:o}=await e.findDaemon("",r.repo??"",we(t));try{if(!o){let s=e.readPersistedState(n);if(r.json){T(JSON.stringify({running:!1,persisted:s},null,2)+`
|
|
2335
2335
|
`);return}if(T(`supervisor is not running
|
|
2336
2336
|
`),s&&s.processes.length>0){T(`last session ${s.session_id} profile ${s.profile||"(none)"}
|
|
2337
2337
|
`);for(let a of s.processes)T(`${a.name} stopped UNKNOWN ${a.pid}
|
|
@@ -2339,49 +2339,49 @@ ${l.map((d)=>` ${d}`).join(`
|
|
|
2339
2339
|
`);return}T(`PROFILE: ${i.profile||"(none)"}
|
|
2340
2340
|
|
|
2341
2341
|
SERVICE STATUS HEALTH PID
|
|
2342
|
-
`);for(let[s,a]of Object.entries(i.services))T(`${s} ${
|
|
2342
|
+
`);for(let[s,a]of Object.entries(i.services))T(`${s} ${Qo(a)} ${a.health} ${a.pid}
|
|
2343
2343
|
`);T(`
|
|
2344
2344
|
PROXY ${i.proxy.running?"RUNNING":"STOPPED"} ${i.proxy.address??""}
|
|
2345
2345
|
`),T(`MCP ${i.mcp?.running?"RUNNING":"STOPPED"} ${i.mcp?.address??""}
|
|
2346
2346
|
`),T(`IDENTITY ${i.identity.user||"(unknown)"}
|
|
2347
2347
|
`),T(`CLOUD ${i.identity.project||"(unset)"}
|
|
2348
|
-
`)}finally{o?.close()}}var
|
|
2349
|
-
`),await
|
|
2350
|
-
`);try{await
|
|
2351
|
-
`);return}let i=await
|
|
2348
|
+
`)}finally{o?.close()}}var QO=2000;function mC(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 aC(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 aC(t,e,r),T(`
|
|
2350
|
+
`);try{await XO(QO,void 0,{signal:n.signal})}catch{break}}}finally{process.off("SIGINT",o),process.off("SIGTERM",o)}})}function fC(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??"",we(e));if(!o){T(`no supervisor is running for ${n}
|
|
2351
|
+
`);return}let i=await e$(o);try{let s=r.keepServices!==!0;await o.call("shutdown",{stop_services:s},i)}finally{o.close()}await ZO(t,n,i),T(r.keepServices!==!0?`stopped services and the supervisor for ${n}
|
|
2352
2352
|
`:`stopped the supervisor for ${n}; its services keep running
|
|
2353
|
-
`)})}async function
|
|
2354
|
-
`}async function
|
|
2355
|
-
`:
|
|
2353
|
+
`)})}async function ZO(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 e$(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 hC(e,t){e.command("attach").action(async()=>{let r=await t.openAttach("",we(e)),{runTuiWithController:n}=await Promise.resolve().then(() => (qm(),tC));await n(t,r)})}import{setTimeout as gC}from"timers/promises";function zm(e){return`${e.timestamp.slice(11,19)} ${e.service.padEnd(10)} ${String(e.level).padEnd(6)} ${e.message}
|
|
2354
|
+
`}async function vC(e,t,r,n=1000){let o=await e(void 0);o.events.forEach(t);let i=o.nextCursor;while(!r.aborted){try{await gC(n,void 0,{signal:r})}catch{return}o=await e(i),o.events.forEach(t),i=o.nextCursor}}function yC(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("",we(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 vC((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
|
+
`:zm(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
2356
|
`);return}if(o.json){T(JSON.stringify(c,null,2)+`
|
|
2357
|
-
`);return}for(let l of c)T(
|
|
2358
|
-
`);return}for(let c of a.events)T(
|
|
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("",
|
|
2360
|
-
`)}finally{await i.close()}})}function
|
|
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
|
|
2357
|
+
`);return}for(let l of c)T(zm(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(zm(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("",we(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 bC(e,t){e.command("daemon").command("logs").option("-f, --follow","keep printing new lines until interrupted").action(async(r)=>{let{repoRoot:n}=t.discover("",we(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 gC(500,void 0,{signal:a.signal})}catch{break}s()}}finally{process.off("SIGINT",c),process.off("SIGTERM",c)}})}kl();function xC(e,t){let r=e.command("proxy");r.command("status").option("--json").action(async(n)=>{let o=await t.openController("",we(e),!1);try{if(!o.client){T(`PROXY STOPPED
|
|
2362
2362
|
`);return}let i=await o.status();if(n.json){T(JSON.stringify(i.proxy,null,2)+`
|
|
2363
2363
|
`);return}T(`PROXY ${i.proxy.running?"RUNNING":"STOPPED"} ${i.proxy.address??""}
|
|
2364
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("",
|
|
2365
|
+
`)}}finally{await o.close()}}),r.command("start").action(async()=>{let n=await t.openController("",we(e),!0);try{await n.proxyStart()}finally{await n.close()}}),r.command("stop").action(async()=>{let n=await t.openController("",we(e),!0);try{await n.proxyStop()}finally{await n.close()}})}function wC(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("",we(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??vi(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(tm(c,l)),cursor:JSON.parse(rm(c,l)),kilo:JSON.parse(nm(c,l)),codex:om(c,l)}},null,2)+`
|
|
2366
2366
|
`);return}if(T(`MCP ${i?.mcp?.running?"RUNNING":"STOPPED"} ${c}
|
|
2367
2367
|
|
|
2368
2368
|
`),i?.setup_mode===!0)T(`This repository has no .devctl yet, so the daemon is in setup mode.
|
|
2369
2369
|
`),T(`Connect an agent with the config below and ask it to set devctl up for this repository.
|
|
2370
2370
|
`),T(`It can call get_setup_guide, search_docs, and validate_config; nothing will run until a configuration exists.
|
|
2371
2371
|
|
|
2372
|
-
`);T(
|
|
2372
|
+
`);T(vw(c,l))}finally{await o.close()}})}te();function SC(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
2373
|
`);return}if(T(`current ${n.current}
|
|
2374
2374
|
`),T(`latest ${n.latest||"(unavailable)"}
|
|
2375
2375
|
`),T(`channel ${n.kind}
|
|
2376
2376
|
`),!n.newer){if(n.latest!=="")T(`up to date
|
|
2377
2377
|
`);return}if(T(`install ${n.hint}
|
|
2378
2378
|
`),r.check||!n.command)return;if(T(`installing via ${n.kind}\u2026
|
|
2379
|
-
`),(await t.applyUpdate(n.command,!0)).code!==0){process.exitCode=
|
|
2380
|
-
`)})}function
|
|
2381
|
-
`)}),r.action(async()=>{let n=r.opts();await
|
|
2382
|
-
`);else T(t.formatDoctor(o));if(o.issues>0)process.exitCode=2})}function
|
|
2379
|
+
`),(await t.applyUpdate(n.command,!0)).code!==0){process.exitCode=hh;return}T(`updated to ${n.latest}; ${bc}
|
|
2380
|
+
`)})}function r$(e,t){let r=new t$;return r.name("devctl").description("Local development orchestrator").version(tr(),"-V, --version","print version").option("--config <path>","path to config file or .devctl directory").enablePositionalOptions(),r.command("version").description("print version").action(()=>{T(`${tr()}
|
|
2381
|
+
`)}),r.action(async()=>{let n=r.opts();await Gm(e,n.config??"")}),dC(r,e),uC(r,e),pC(r,e),lC(r,e),cC(r,e),mC(r,e),fC(r,e),yC(r,e),bC(r,e),n$(r,e),o$(r,e),oC(r,e),xC(r,e),wC(r,e),sC(r,e),iC(r,e),hC(r,e),i$(r,e),SC(r,e),s$(r,t),r}function n$(e,t){e.command("doctor").option("--json","machine-readable output").action(async(r)=>{let n=t.load("",we(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 o$(e,t){e.command("setup").option("--force","overwrite an existing configuration").action(async(r)=>{await t.runSetup("",we(e),r.force===!0)})}function i$(e,t){e.command("completion").argument("[shell]","zsh, bash, or fish").description("print a shell completion script").action((r)=>{T(nC(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("",we(e));T(Vm(o,i).join(`
|
|
2383
2383
|
`)+`
|
|
2384
|
-
`)}catch{T(
|
|
2384
|
+
`)}catch{T(Vm(o,Xn()).join(`
|
|
2385
2385
|
`)+`
|
|
2386
|
-
`)}})}function
|
|
2387
|
-
`),process.exit(
|
|
2386
|
+
`)}})}function s$(e,t){e.command("_supervisor").option("--repo <path>","repository root").action(async(r)=>{await t(r.repo??"",we(e))})}async function kC(e,t){process.stdout.on("error",(r)=>{if(r.code==="EPIPE")process.exit(0)});try{await r$(e,t).parseAsync(process.argv)}catch(r){process.stderr.write(L(r)+`
|
|
2387
|
+
`),process.exit(vh(r))}}ra();await kC(Wv(),hb);
|